Skip to content

Instantly share code, notes, and snippets.

@sgissinger
Last active October 25, 2018 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgissinger/ed2669f9418747895a9e5d164059cc76 to your computer and use it in GitHub Desktop.
Save sgissinger/ed2669f9418747895a9e5d164059cc76 to your computer and use it in GitHub Desktop.
Dynamic Linq
public interface IFilterable
{
string ColumnName { get; }
string Operator { get; }
string Value { get; }
}
public interface ISortable
{
string ColumnName { get; }
string Direction { get; }
bool IsDescending { get; }
}
public static class LinqExtensions
{
public static IQueryable<T> OrderBy<T>(this IEnumerable<T> source, IEnumerable<ISortable> sortables)
{
return source.AsQueryable().OrderBy(sortables);
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, IEnumerable<ISortable> sortables)
{
if (source == null)
throw new ArgumentNullException("source");
if (sortables == null)
throw new ArgumentNullException("orders");
var type = typeof(T);
var first = true;
foreach (var sortable in sortables)
{
var command = first ? "OrderBy" : "ThenBy";
if (sortable.IsDescending)
command = string.Format("{0}Descending", command);
var property = type.GetProperty(sortable.ColumnName);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var lambdaExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(lambdaExpression));
source = source.Provider.CreateQuery<T>(resultExpression);
first = false;
}
return source;
}
public static IQueryable<T> Where<T>(this IEnumerable<T> source, IEnumerable<IFilterable> filterables)
{
return source.AsQueryable().Where(filterables);
}
public static IQueryable<T> Where<T>(this IQueryable<T> source, IEnumerable<IFilterable> filterables)
{
if (source == null)
throw new ArgumentNullException("source");
if (filterables == null)
throw new ArgumentNullException("filters");
var type = typeof(T);
foreach (var filterable in filterables)
{
var property = type.GetProperty(filterable.ColumnName);
// TODO: Extend in order to manage other cases, this one only take care of string equalities
if (property.PropertyType == typeof(string))
{
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var value = Expression.Constant(filterable.Value);
var expression = Expression.Equal(value, value);
if (filterable.Operator == "eq")
expression = Expression.Equal(propertyAccess, value);
else if (filterable.Operator == "like")
expression = LikeOrNotLike(propertyAccess, value, true);
else if (filterable.Operator == "notLike")
expression = LikeOrNotLike(propertyAccess, value, false);
var lambdaExpression = Expression.Lambda(expression, parameter);
var resultExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { type },
source.Expression, lambdaExpression);
source = source.Provider.CreateQuery<T>(resultExpression);
}
}
return source;
}
private static BinaryExpression LikeOrNotLike(MemberExpression propertyAccess, ConstantExpression value, bool like)
{
var cmp = Expression.Constant(StringComparison.InvariantCulture);
var indexOf = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
var toLower = typeof(string).GetMethod("ToLower", Type.EmptyTypes);
var valueToLower = Expression.Call(value, toLower);
var propertyToLower = Expression.Call(propertyAccess, toLower);
var left = Expression.Call(propertyToLower, indexOf, valueToLower, cmp);
BinaryExpression expression;
if (like)
{
var right = Expression.Constant(0, typeof(int));
expression = Expression.GreaterThanOrEqual(left, right);
}
else
{
var right = Expression.Constant(-1, typeof(int));
expression = Expression.Equal(left, right);
}
return expression;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment