在Linq中使用Expression <Func >包含扩展名

使用以下示例我想在我的Contains方法中使用我的Expression ,让它使用EF将查询传递到sql server。

我怎样才能使这个正常工作?

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

我知道我可以传递整个谓词,但我只希望用户提供如何从相关实体解析公司。

UPDATE

我决定解决companyId而不是整个公司实体,我可以在内存中获取id列表,如果是IQueryable或者只是一个普通的数组/ IEnumerable,我就不会讨论

但是我得到一些奇怪的错误:

执行’Extent.Select(o => o)时出现exception。其中(p =>(p.Hide = False))。其中(p =>(p.Archived = False))。Where(item = > System.Int32 []。包含(item.Development.CompanyId))’。 有关更多详细信息,请参阅InnerException。

内在的例外是

参数表达式无效

更新2

我编辑了代码以反映我真正想要做的事情,没有太多运气找到解决方案。

如果我理解正确,你想要的是表达构成:

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

下面是表达式上Compose()扩展方法的实现:

  ///  /// 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's type ///  /// The input of g /// The output of g and the input of f /// The output of f /// The outer function /// The inner function /// A new lambda expression public static Expression> Compose(this Expression> f, Expression> g) { // The implementation used here gets around EF's inability to process Invoke expressions. Rather than invoking f with the output of g, we // effectively "inline" g by replacing all instances of f's parameter with g's 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>(reboundBody, g.Parameters); return lambda; } public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary Map; public ParameterRebinder(Dictionary map) { this.Map = map ?? new Dictionary(); } public static Expression ReplaceParameters(Dictionary 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); } } 

尝试Expression.Compile()方法:

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

items参数不是IQueryable吗? 如果是这样,试试这个:

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