Using the expression <Func <T, X >> in Linq contains the extension

Using the following example, I would like to use my Expression inside my Contains method, passing the request to the sql server using EF .

How can I configure this to work correctly?

 void Main() { IQueryable<Person> qry = GetQueryableItemsFromDB(); var filtered = qry.Filter(p=>p.CompanyId); } public static class Ext { public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, int>> resolveCompanyIdExpression) { IEnumerable<int> validComps = GetCompanyIdsFromDataBase(); var exp = Expression.Lambda<Func<T, bool>>( Expression.Call(typeof(Queryable),"Contains", new[] { typeof(Company) }, Expression.Constant(validComps), resolveCompanyIdExpression.Body), resolveCompanyIdExpression.Parameters[0]); return items.Where(exp); } public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, IEnumerable<int>>> resolveCompanyIdExpression) { IEnumerable<int> validComps = GetCompanyIdsFromDataBase(); //No Idea what to do here ? } } public class Person { public int CompanyId {get;set;} } 

I know that I can pass the entire predicate, but I want the user to suggest how to allow the Company from the object in question.

UPDATE

I decided to allow companyId, not the whole essence of the company, I can get a list of identifiers in memory and they will not fuss if it is IQueryable or just a simple array / IEnumerable

However, I get some strange errors:

An exception occurred during the execution of 'Extent.Select (o => o). Where (p => (p.Hide = False)). Where (p => (p.Archived = False)). Where (item => System.Int32 []. Contains (item.Development.CompanyId)) '. See InnerException for more details.

Internal exception

The argument expression is invalid.

UPDATE 2

I edited the code to reflect what I really would like to do without having much luck finding a solution to this.

+7
source share
3 answers

If I understand correctly, then what you want is a composition of expressions:

 public static IQueryable<T> Filter<T>(IQueryable<T> query, Expression<Func<T, int>> getCompanyId) { IEnumerable<int> validCompanyIds = GetCompanyIdsFromDatabase(); Expression<Func<int, bool>> filterByCompanyId = id => validCompanyIds.Contains(id); // these generics will actually be inferred, I've just written them to be explicit Expression<Func<T, bool>> composed = filterByCompanyId.Compose<T, int, bool>(getCompanyId); return query.Where(composed); } 

The following is an implementation of the Compose () extension method in an expression:

  /// <summary> /// Composes two lambda expressions f(y) and g(x), returning a new expression representing f(g(x)). /// This is useful for constructing expressions to pass to functions like Where(). If given x => x.Id and id => ids.Contains(id), /// for example, you can create the expression x => ids.Contains(x.Id), which could be passed to Where() for an IQueryable of x type /// </summary> /// <typeparam name="TIn">The input of g</typeparam> /// <typeparam name="TIntermediate">The output of g and the input of f</typeparam> /// <typeparam name="TOut">The output of f</typeparam> /// <param name="f">The outer function</param> /// <param name="g">The inner function</param> /// <returns>A new lambda expression</returns> public static Expression<Func<TIn, TOut>> Compose<TIn, TIntermediate, TOut>(this Expression<Func<TIntermediate, TOut>> f, Expression<Func<TIn, TIntermediate>> g) { // The implementation used here gets around EF inability to process Invoke expressions. Rather than invoking f with the output of g, we // effectively "inline" g by replacing all instances of f parameter with g body and creating a new lambda with the rebound body of f and // the parameters of g var map = f.Parameters.ToDictionary(p => p, p => g.Body); var reboundBody = ParameterRebinder.ReplaceParameters(map, f.Body); var lambda = Expression.Lambda<Func<TIn, TOut>>(reboundBody, g.Parameters); return lambda; } public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, Expression> Map; public ParameterRebinder(Dictionary<ParameterExpression, Expression> map) { this.Map = map ?? new Dictionary<ParameterExpression, Expression>(); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, Expression> map, Expression exp) { return new ParameterRebinder(map).Visit(exp); } protected override Expression VisitParameter(ParameterExpression node) { Expression replacement; if (this.Map.TryGetValue(node, out replacement)) { return this.Visit(replacement); } return base.VisitParameter(node); } } 
+4
source

try the Expression.Compile () method:

 return items.Where(item => validComps.Contains(resolveCompanyExpression.Compile()(item))).AsQueryable(); 
0
source

Not items a IQueryable ? If yes, try the following:

 public static IQueryable<T> FilterByCompany<T>(this IQueryable<T> items, Expression<Func<T, Company>> resolveCompanyExpression) where T : EntityBase { IQueryable<Company> validComps = GetCompaniesFromDataBase(); var exp = Expression.Lambda<Func<T, bool>>( Expression.Call( typeof(Queryable), "Contains", new[] { typeof(Company) }, Expression.Constant(validComps), resolveCompanyExpression.Body ), resolveCompanyExpression.Parameters[0] ); return items.Where(exp); } 
0
source

All Articles