-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
40 changed files
with
2,056 additions
and
1,061 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.