Created
April 2, 2014 01:16
-
-
Save wgrrrr/9926305 to your computer and use it in GitHub Desktop.
A "query by example" implementation for Entity Framework 5 and C# 5 using a generic repository pattern.
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
/// <summary> | |
/// Uses the supplied entity as search criteria and returns matching records. When more than one entity properties | |
/// are populated it will use an AND in the query. | |
/// </summary> | |
/// <remarks> | |
/// Only simple properties such as strings and value types can be evaluated by this method. If you require a more | |
/// advanced query you should add an entity specific DbRepository class which extends this class. | |
/// </remarks> | |
/// <param name="example">The entity to use as an example</param> | |
public virtual IQueryable<TEntity> GetByExample(TEntity example) | |
{ | |
var predicates = new List<Expression<Func<TEntity, bool>>>(); | |
foreach (var property in example.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) | |
{ | |
// Only add properties that are not null, are either a value type or string, and do not have their default value | |
if (property.GetValue(example) == null || | |
(!property.PropertyType.IsValueType && property.PropertyType != typeof (string)) || | |
property.GetValue(example).Equals(GetDefault(property.PropertyType))) | |
continue; | |
var pe = Expression.Parameter(typeof (TEntity), property.Name); | |
var leftSide = Expression.Property(pe, property.GetMethod); | |
var rightSide = Expression.Constant(property.GetValue(example), property.PropertyType); | |
var predicateBody = Expression.Equal(leftSide, rightSide); | |
var lambda = Expression.Lambda<Func<TEntity, bool>>(predicateBody, new[] {pe}); | |
predicates.Add(lambda); | |
} | |
return Context.Set<TEntity>().Where(predicates.Aggregate(PredicateBuilder.And)); | |
} |
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
/// <summary> | |
/// Enables the efficient, dynamic composition of query predicates. | |
/// </summary> | |
/// <remarks> | |
/// See http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ | |
/// </remarks> | |
public static class PredicateBuilder | |
{ | |
/// <summary> | |
/// Creates a predicate that evaluates to true. | |
/// </summary> | |
public static Expression<Func<T, bool>> True<T>() | |
{ | |
return param => true; | |
} | |
/// <summary> | |
/// Creates a predicate that evaluates to false. | |
/// </summary> | |
public static Expression<Func<T, bool>> False<T>() | |
{ | |
return param => false; | |
} | |
/// <summary> | |
/// Creates a predicate expression from the specified lambda expression. | |
/// </summary> | |
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) | |
{ | |
return predicate; | |
} | |
/// <summary> | |
/// Combines the first predicate with the second using the logical "and". | |
/// </summary> | |
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, | |
Expression<Func<T, bool>> second) | |
{ | |
return first.Compose(second, Expression.AndAlso); | |
} | |
/// <summary> | |
/// Combines the first predicate with the second using the logical "or". | |
/// </summary> | |
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, | |
Expression<Func<T, bool>> second) | |
{ | |
return first.Compose(second, Expression.OrElse); | |
} | |
/// <summary> | |
/// Negates the predicate. | |
/// </summary> | |
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression) | |
{ | |
UnaryExpression negated = Expression.Not(expression.Body); | |
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters); | |
} | |
/// <summary> | |
/// Combines the first expression with the second using the specified merge function. | |
/// </summary> | |
private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, | |
Func<Expression, Expression, Expression> merge) | |
{ | |
// zip parameters (map from parameters of second to parameters of first) | |
Dictionary<ParameterExpression, ParameterExpression> map = first.Parameters | |
.Select((f, i) => new {f, s = second.Parameters[i]}) | |
.ToDictionary(p => p.s, p => p.f); | |
// replace parameters in the second lambda expression with the parameters in the first | |
Expression secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); | |
// create a merged lambda expression with parameters from the first expression | |
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); | |
} | |
private class ParameterRebinder : ExpressionVisitor | |
{ | |
private readonly Dictionary<ParameterExpression, ParameterExpression> map; | |
private ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) | |
{ | |
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); | |
} | |
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, | |
Expression exp) | |
{ | |
return new ParameterRebinder(map).Visit(exp); | |
} | |
protected override Expression VisitParameter(ParameterExpression p) | |
{ | |
ParameterExpression replacement; | |
if (map.TryGetValue(p, out replacement)) | |
{ | |
p = replacement; | |
} | |
return base.VisitParameter(p); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment