Skip to content

Instantly share code, notes, and snippets.

@osya
Last active December 7, 2016 01:17
Show Gist options
  • Save osya/4a9e6d822b636e20fe5ac7c5b1426297 to your computer and use it in GitHub Desktop.
Save osya/4a9e6d822b636e20fe5ac7c5b1426297 to your computer and use it in GitHub Desktop.
Dynamic LINQ Expression with Generic Any<T>() function Example #CSharp
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