使用下面的例子,我想用我的Expression
在我的包含方法,让它使用查询转嫁到SQL Server的EF
。
如何建立这个能够正常运行?
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;}
}
我知道我可以在整个谓语通过,但我只希望用户提供如何从有问题的实体解决的公司。
UPDATE
我决定来解决companyId而不是整个公司实体,我可以在内存ID列表和IM不会大惊小怪,如果这是IQueryable的或只是一个普通阵列/ IEnumerable的
不过,我得到一些奇怪的错误:
Extent.Select'的执行过程中发生了异常(O => O)。凡(p值=>(p.Hide =假))。如果(p值=>(p.Archived =假))。如果(项目= > System.Int32 []。包含(item.Development.CompanyId))”。 请参阅的InnerException了解更多详情。
内部异常是
参数表达式无效
更新2
我已编辑的代码,以反映我真的很想喜欢做的事,找到一个解决这个运气不好。
如果我理解正确的话,你要的是表达组成:
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);
}
下面是对表达撰写()扩展方法的实现:
/// <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's 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'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<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);
}
}
尝试Expression.Compile()方法:
return items.Where(item => validComps.Contains(resolveCompanyExpression.Compile()(item))).AsQueryable();
不是items
参数的IQueryable
? 如果是的话,试试这个:
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);
}