Last active
December 7, 2016 01:17
-
-
Save osya/4a9e6d822b636e20fe5ac7c5b1426297 to your computer and use it in GitHub Desktop.
Dynamic LINQ Expression with Generic Any<T>() function Example #CSharp
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
private static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags) | |
{ | |
var typeArity = typeArgs.Length; | |
var methods = type.GetMethods() | |
.Where(m => m.Name == name) | |
.Where(m => m.GetGenericArguments().Length == typeArity) | |
.Select(m => m.MakeGenericMethod(typeArgs)); | |
return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null); | |
} | |
private static bool IsIEnumerable(Type type) | |
{ | |
return type.IsGenericType | |
&& type.GetGenericTypeDefinition() == typeof(IEnumerable<>); | |
} | |
private static Type GetIEnumerableImpl(Type type) | |
{ | |
// Get IEnumerable implementation. Either type is IEnumerable<T> for some T, | |
// or it implements IEnumerable<T> for some T. We need to find the interface. | |
if (IsIEnumerable(type)) | |
return type; | |
var t = type.FindInterfaces((m, o) => IsIEnumerable(m), null); | |
return t[0]; | |
} | |
/// <summary> | |
/// based on http://stackoverflow.com/questions/326321/how-do-i-create-an-expression-tree-calling-ienumerabletsource-any | |
/// </summary> | |
/// <param name="collection"></param> | |
/// <param name="predicate"></param> | |
/// <returns></returns> | |
private static Expression CallAny(Expression collection, Expression predicate) | |
{ | |
var cType = GetIEnumerableImpl(collection.Type); | |
collection = Expression.Convert(collection, cType); | |
var elemType = cType.GetGenericArguments()[0]; | |
var predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool)); | |
// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>) | |
var anyMethod = (MethodInfo) | |
GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType }, | |
new[] { cType, predType }, BindingFlags.Static); | |
return Expression.Call( | |
anyMethod, | |
collection, | |
predicate); | |
} | |
// GET: Restaurants | |
public async Task<ActionResult> Index(string cuisineId, int? placeId = null, int? cityId = null) | |
{ | |
var rParam = Expression.Parameter(typeof(Restaurant), "r"); | |
var filterLambda = placeId != null | |
? Expression.Lambda<Func<Restaurant, bool>>( | |
Expression.Equal( | |
Expression.Property(Expression.Property(rParam, "Place"), "Id"), | |
Expression.Constant(placeId) | |
), | |
rParam | |
) | |
: cityId != null | |
? Expression.Lambda<Func<Restaurant, bool>>( | |
Expression.Equal( | |
Expression.Property(Expression.Property(Expression.Property(rParam, "Place"), "City"), "Id"), | |
Expression.Constant(cityId) | |
), | |
rParam | |
) | |
: Expression.Lambda<Func<Restaurant, bool>>(Expression.Constant(true), rParam); | |
if (!string.IsNullOrEmpty(cuisineId)) | |
{ | |
var cParam = Expression.Parameter(typeof(Cuisine), "c"); | |
var anyPredicatExpr = Expression.Call( | |
null, | |
typeof(string).GetMethod("Equals", new[] {typeof(string), typeof(string)}), | |
Expression.Property(cParam, "Id"), | |
Expression.Constant(cuisineId)); | |
var cuiPredicate = Expression.Lambda(anyPredicatExpr, cParam); | |
var cuiExpr = CallAny(Expression.Property(rParam, "Cuisines"), cuiPredicate); | |
var cuiLambda = Expression.Lambda<Func<Restaurant, bool>>(cuiExpr, rParam); | |
filterLambda = (placeId != null || cityId != null) ? | |
Expression.Lambda<Func<Restaurant, bool>>(Expression.AndAlso(filterLambda.Body, cuiLambda.Body), rParam) : cuiLambda; | |
} | |
var restaurants = await _db.Restaurants.Where(filterLambda).Include(r => r.Cuisines).ToArrayAsync(); | |
var model = restaurants.Select(r => new RestaurantViewModel {Id = r.Id, Name = r.Name, ImageFullFileName = r.ImageFullFileName, Place = r.Place, Cuisines = r.Cuisines }); | |
return View(model); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment