【发布时间】:2019-02-01 18:06:14
【问题描述】:
我正在使用 EF6 为应用程序实现搜索/过滤 UI 的后端。我有代码构建一个表达式以与 Queryable.Where 一起使用给定的 DbSet,其中 DbSet 的类型在运行时确定(DBContext 有很多,它们可能会改变)。如果我首先通过将表达式转换为特定类型来作弊,那么对 Where 的调用可以正常工作。否则,我会收到此错误:
'System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression>)'的最佳重载方法匹配有一些无效参数'
我正在努力寻找一种方法来过滤像这样的 DbSet,其中在运行时提供底层“表”类型。这是一个大大简化的代码版本来说明:
void ProcessFilter(AppDbContext context, NameValueCollection filters, Type tableType)
{
// If tableType == typeof(Organisation), expression is a Expression<Func<Organisation, bool>>
var expression = GetFilterExpression(filters);
var dbset = Set(context, tableType);
dynamic dynamicSet = dbset;
// This fails
var results = Queryable.Where(dynamicSet, expression);
// see https://stackoverflow.com/questions/4285598/iqueryable-non-generic-missing-count-and-skip-it-works-with-iqueryablet
// Suppose tableType == typeof(Organisation)
// This works
var typedExpression = expression as Expression<Func<Organisation, bool>>;
var typedResults = Queryable.Where(dynamicSet, typedExpression);
}
public static IQueryable Set(DbContext context, Type T)
{
// Similar to code in
// https://stackoverflow.com/questions/21533506/find-a-specified-generic-dbset-in-a-dbcontext-dynamically-when-i-have-an-entity
var method = typeof(DbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "Set" && x.IsGenericMethod).First();
// Build a method with the specific type argument
method = method.MakeGenericMethod(T);
return method.Invoke(context, null) as IQueryable;
}
【问题讨论】:
-
最让我印象深刻的是您对 Queryable.Where 的使用。说这可能是缺乏经验,但我的经验和对该组方法的粗略搜索将两个返回的重载的第一个参数标记为
this关键字。这表明您应该调用dynamicSet.Where(expression),这与我曾经使用过或看到使用过这种方法的唯一方式一致。 -
@Inagnikai - 问题是,dynamicSet.Where 不会编译 - 请参阅该代码行下评论中链接中的文章......因为类型是在运行时提供的,所以没有'不是在编译时使用的强类型 IQueryable
。 -
我明白你在说什么。从表面上看,您的表格类型是由用户通过某种控制提供的。他们尝试设置它的方式提供了非常高的灵活性,但可以预见的是以牺牲类型安全为代价。您是否有任何理由无法将用户输入映射到具体类型,并可能创建具有 IQueryable 约束的通用方法来处理 .Where() 调用?
-
对于这种情况,您将不得不介绍
Reflection -
@Inagnikai - 是的,一种解决方法是使用一个函数将 tableType 的每个可能值映射到对 Where 的显式调用,该调用在编译时具有代码中指定的类型......但这会很难维护,我希望有一种方法可以做到这一点,可以使用库风格,而不必为 DbContext 中的所有表类型提供特殊情况代码......
标签: c# entity-framework iqueryable