Skip to content

Commit

Permalink
More awesomeness!
Browse files Browse the repository at this point in the history
Added Filter<T> for easier filtering;
More tests;
Minor refactoring and better naming.
  • Loading branch information
Nima Ara committed Mar 5, 2017
1 parent e477861 commit 2949c90
Show file tree
Hide file tree
Showing 40 changed files with 2,056 additions and 1,061 deletions.
10 changes: 6 additions & 4 deletions Easy.Storage.Common/Easy.Storage.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
<HintPath>..\packages\Dapper.1.50.2\lib\net451\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Easy.Common, Version=1.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Easy.Common.1.3.0\lib\net452\Easy.Common.dll</HintPath>
<Reference Include="Easy.Common, Version=1.9.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Easy.Common.1.9.0\lib\net45\Easy.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
Expand All @@ -52,14 +52,16 @@
<Compile Include="Attributes\IdentityAttribute.cs" />
<Compile Include="Dialect.cs" />
<Compile Include="Extensions\DbConnectionExtensions.cs" />
<Compile Include="Extensions\HelperExtensions.cs" />
<Compile Include="Extensions\Reader.cs" />
<Compile Include="Filter\FilteredQuery.cs" />
<Compile Include="Filter\Operator.cs" />
<Compile Include="TypeHandlers\DateTimeOffsetHandler.cs" />
<Compile Include="Extensions\EnumExtensions.cs" />
<Compile Include="Extensions\ExpressionExtensions.cs" />
<Compile Include="TypeHandlers\GuidHandler.cs" />
<Compile Include="IRepository.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QueryFilter.cs" />
<Compile Include="Filter\Filter.cs" />
<Compile Include="Formatter.cs" />
<Compile Include="Repository.cs" />
<Compile Include="Table.cs" />
Expand Down
21 changes: 11 additions & 10 deletions Easy.Storage.Common/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@
{
using System;
using System.Collections.Generic;
using Easy.Storage.Common.Filter;

/// <summary>
/// Provides a set of helper methods for working with <see cref="Enum"/>.
/// </summary>
public static class EnumExtensions
{
private static readonly Dictionary<Operand, string> Operands = new Dictionary<Operand, string>
private static readonly Dictionary<Operator, string> Operands = new Dictionary<Operator, string>
{
{Operand.Equal, "="},
{Operand.NotEqual, "<>"},
{Operand.GreaterThan, ">"},
{Operand.GreaterThanOrEqual, ">="},
{Operand.LessThan, "<"},
{Operand.LessThanOrEqual, "<="},
{Operator.Equal, "="},
{Operator.NotEqual, "<>"},
{Operator.GreaterThan, ">"},
{Operator.GreaterThanOrEqual, ">="},
{Operator.LessThan, "<"},
{Operator.LessThanOrEqual, "<="},
};

/// <summary>
/// Returns the string representation of the given <paramref name="operand"/>.
/// Returns the string representation of the given <paramref name="operator"/>.
/// </summary>
internal static string OperandAsStr(this Operand operand)
internal static string AsString(this Operator @operator)
{
return Operands[operand];
return Operands[@operator];
}
}
}
22 changes: 0 additions & 22 deletions Easy.Storage.Common/Extensions/ExpressionExtensions.cs

This file was deleted.

24 changes: 24 additions & 0 deletions Easy.Storage.Common/Extensions/HelperExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Easy.Storage.Common.Extensions
{
using System.Collections.Generic;
using Dapper;

/// <summary>
/// Provides a set of helpful methods for working with various objects.
/// </summary>
internal static class HelperExtensions
{
/// <summary>
/// Converts the given <paramref name="parameters"/> to <see cref="DynamicParameters"/>.
/// </summary>
internal static DynamicParameters ToDynamicParameters(this IDictionary<string, object> parameters, object template = null)
{
var result = template == null ? new DynamicParameters() : new DynamicParameters(template);
foreach (var pair in parameters)
{
result.Add(pair.Key, pair.Value);
}
return result;
}
}
}
111 changes: 111 additions & 0 deletions Easy.Storage.Common/Filter/Filter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
namespace Easy.Storage.Common.Filter
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

/// <summary>
/// Represents a filter to limit the records returned from storage.
/// </summary>
/// <typeparam name="T">The type of the records to filter.</typeparam>
public sealed class Filter<T>
{
private readonly FilteredQuery _query;

private Filter()
{
_query = FilteredQuery.Make<T>();
}

/// <summary>
/// Gets the parameters generated by this instance.
/// </summary>
public IDictionary<string, object> Parameters => _query.Parameters;

/// <summary>
/// Makes and returns a <see cref="Filter{T}"/>.
/// </summary>
public static Filter<T> Make => new Filter<T>();

/// <summary>
/// Gets the <c>SQL</c> generated by this instance.
/// </summary>
/// <param name="sqlToPrefix">
/// An optional <c>SQL</c> to prefix to the result.
/// <remarks>
/// If <paramref name="sqlToPrefix"/> is <c>NULL</c> or <c>Empty</c>, a default
/// <c>SELECT</c> including all the columns for the record will be added.
/// </remarks>
/// </param>
// ReSharper disable once InconsistentNaming
public string GetSQL(string sqlToPrefix = null) => _query.Compile(sqlToPrefix);

/// <summary>
/// Adds an <c>AND IN</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="values">The values by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> AndIn<TProperty>(Expression<Func<T, TProperty>> selector, params TProperty[] values)
{
_query.AddInClause(selector, Formatter.AndClauseSeparator, true, values);
return this;
}

/// <summary>
/// Adds an <c>AND NOT IN</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="values">The values by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> AndNotIn<TProperty>(Expression<Func<T, TProperty>> selector, params TProperty[] values)
{
_query.AddInClause(selector, Formatter.AndClauseSeparator, false, values);
return this;
}

/// <summary>
/// Adds an <c>AND</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="operator">The operator.</param>
/// <param name="value">The value by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> And<TProperty>(Expression<Func<T, TProperty>> selector, Operator @operator, TProperty value)
{
_query.AddClause(selector, @operator, value, Formatter.AndClauseSeparator);
return this;
}

/// <summary>
/// Adds an <c>OR</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="operator">The operator.</param>
/// <param name="value">The value by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> Or<TProperty>(Expression<Func<T, TProperty>> selector, Operator @operator, TProperty value)
{
_query.AddClause(selector, @operator, value, Formatter.OrClauseSeparator);
return this;
}

/// <summary>
/// Adds an <c>OR IN</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="values">The values by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> OrIn<TProperty>(Expression<Func<T, TProperty>> selector, params TProperty[] values)
{
_query.AddInClause(selector, Formatter.OrClauseSeparator, true, values);
return this;
}

/// <summary>
/// Adds an <c>OR NOT IN</c> query for the column represented by <paramref name="selector"/>.
/// </summary>
/// <param name="selector">The column to take part in the filter.</param>
/// <param name="values">The values by which the <paramref name="selector"/> should be filtered.</param>
public Filter<T> OrNotIn<TProperty>(Expression<Func<T, TProperty>> selector, params TProperty[] values)
{
_query.AddInClause(selector, Formatter.OrClauseSeparator, false, values);
return this;
}
}
}
99 changes: 99 additions & 0 deletions Easy.Storage.Common/Filter/FilteredQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
namespace Easy.Storage.Common.Filter
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using Easy.Common.Extensions;
using Easy.Storage.Common.Extensions;

/// <summary>
/// Represents a filtered query.
/// </summary>
internal sealed class FilteredQuery
{
private readonly Table _table;
private readonly StringBuilder _builder;
private uint _paramCounter;

private FilteredQuery(Table table)
{
_table = table;
_builder = new StringBuilder();
Parameters = new Dictionary<string, object>();
}

internal IDictionary<string, object> Parameters { get; }

internal static FilteredQuery Make<T>() => new FilteredQuery(Table.Make<T>());

/// <summary>
/// Compiles and gets the <c>SQL</c> of the <see cref="FilteredQuery"/>.
/// </summary>
/// <param name="sqlToPrefix">
/// An optional <c>SQL</c> to prefix to the result.
/// <remarks>
/// If <paramref name="sqlToPrefix"/> is <c>NULL</c> or <c>Empty</c>, a default
/// <c>SELECT</c> including all the columns for the record will be added.
/// </remarks>
/// </param>
internal string Compile(string sqlToPrefix = null)
{
if (sqlToPrefix.IsNullOrEmpty())
{
sqlToPrefix = _table.Select;
}

// ReSharper disable once PossibleNullReferenceException
var endIndex = sqlToPrefix.Length;
if (sqlToPrefix.EndsWith(";"))
{
endIndex = endIndex - 1;
}

for (var i = 0; i < endIndex; i++)
{
_builder.Insert(i, sqlToPrefix[i]);
}

_builder.Append(';');

var result = _builder.ToString();

// clean up so we can reuse the filtering part of the query.
_builder.Remove(0, endIndex);
_builder.Remove(_builder.Length - 1, 1);

return result;
}

internal void AddClause<T, TProperty>(Expression<Func<T, TProperty>> selector, Operator @operator, TProperty value, string clause)
{
AppendSQL(clause, selector, value, @operator.AsString());
}

internal void AddInClause<T, TProperty>(Expression<Func<T, TProperty>> selector, string clause, bool isIn, params TProperty[] values)
{
var inClause = isIn ? Formatter.InClauseSeparator : Formatter.NotInClauseSeparator;
AppendSQL(clause, selector, values, inClause);
}

// ReSharper disable once InconsistentNaming
private void AppendSQL<T, TProperty, TValue>(string clause, Expression<Func<T, TProperty>> selector, TValue value, string operation)
{
var propertyName = selector.GetPropertyName();
var paramName = AddAndReturnParameter(propertyName, value);
var columnName = _table.PropertyNamesToColumns[propertyName];

//_builder.Remove(_builder.Length - 1, 1); // remove the ';'
_builder.AppendFormat("{0}({1}{2}@{3})", clause, columnName, operation, paramName);
}

private string AddAndReturnParameter<TValue>(string propertyName, TValue value)
{
var paramName = string.Concat(propertyName, (++_paramCounter).ToString());
Parameters.Add(paramName, value);
return paramName;
}
}
}
38 changes: 38 additions & 0 deletions Easy.Storage.Common/Filter/Operator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Easy.Storage.Common.Filter
{
/// <summary>
/// Represents different operations supported by the <see cref="Filter{T}"/>.
/// </summary>
public enum Operator
{
/// <summary>
/// Equal.
/// </summary>
Equal = 0,

/// <summary>
/// Not equal.
/// </summary>
NotEqual,

/// <summary>
/// Greater than.
/// </summary>
GreaterThan,

/// <summary>
/// Greater than or equal.
/// </summary>
GreaterThanOrEqual,

/// <summary>
/// Less than.
/// </summary>
LessThan,

/// <summary>
/// Less than or equal.
/// </summary>
LessThanOrEqual
}
}
4 changes: 2 additions & 2 deletions Easy.Storage.Common/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ internal static class Formatter
internal static readonly string ColumnSeparator = $",{NewLine}{Spacer}";
internal static readonly string AndClauseSeparator = $"{NewLine}AND{NewLine}{Spacer}";
internal static readonly string OrClauseSeparator = $"{NewLine}OR{NewLine}{Spacer}";
internal static readonly string InClauseSeparator = $"{NewLine}IN{NewLine}{Spacer}";
internal static readonly string NotInClauseSeparator = $"{NewLine}NOT IN{NewLine}{Spacer}";
internal static readonly string InClauseSeparator = " IN ";
internal static readonly string NotInClauseSeparator = " NOT IN ";
}
}
Loading

0 comments on commit 2949c90

Please sign in to comment.