如何使用可查询。在运行时设置类型时的位置?

我正在使用 EF6 为应用实现搜索/筛选 UI 的后端。我有一些代码可以构建一个表达式来与Queryable一起使用。对于给定的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;

    }


呼啦一阵风
浏览 79回答 2
2回答

慕容708150

回答你的具体问题。鉴于IQueryable&nbsp;source LambdaExpression&nbsp;predicate如何调用静态泛型方法Queryable.Where<T>(IQueryable<T>&nbsp;source,&nbsp;Expression<Func<T,&nbsp;bool>>&nbsp;predicate)它可以使用(A)反射,(B)DLR动态调度和(C)来完成。Expression.Call您要做的是选项(B)。然而var&nbsp;result&nbsp;=&nbsp;Queryable.Where((dynamic)source,&nbsp;predicate);对具有类型第二个参数的方法进行动态搜索,这当然会失败。LambdaExpression为了能够动态匹配目标方法,您还需要创建第二个参数:dynamicvar&nbsp;result&nbsp;=&nbsp;Queryable.Where((dynamic)source,&nbsp;(dynamic)predicate);上述等效选项(C)的实现是:var&nbsp;result&nbsp;=&nbsp;source.Provider.CreateQuery(Expression.Call(&nbsp; &nbsp;&nbsp;&nbsp;typeof(Queryable),&nbsp;nameof(Queryable.Where),&nbsp;new[]&nbsp;{&nbsp;source.ElementType&nbsp;}, &nbsp;&nbsp;&nbsp;&nbsp;source.Expression,&nbsp;predicate));

GCT1015

恭喜你的第一个问题。让我们首先看一下基于某些自定义筛选器筛选数据集合的方法。我将假设您更喜欢传入筛选器,将属性名称保存为键,将属性值保存为值。NameValueCollectionType在继续筛选整个集合之前,让我们首先弄清楚如何确定一个对象是否具有与筛选器匹配的属性。由于我们直到运行时才知道对象,因此我们需要在 C# 中使用泛型来实现此目的。Type步骤1- 获取所有类属性我们需要获取泛型类的所有属性,例如 .使用反射执行此操作被认为是缓慢的,Matt Warren解释了为什么反射在.NET中很慢以及如何解决它。因此,我们将实现类组件模型的缓存,以获取其存在于命名空间 System.ComponentModel.PropertyDescriptorCollection 中的类组件模型。<TClass>PropertyDescriptorCollection组件缓存private static IDictionary<string, PropertyDescriptorCollection> _componentsCache&nbsp; &nbsp; &nbsp; &nbsp; = new Dictionary<string, PropertyDescriptorCollection>();我们的键表示泛型类的名称,值保存该给定类的名称。DictionaryPropertyDescriptorCollectioninternal static bool InnerFilter<T>(T obj, NameValueCollection filters)&nbsp; &nbsp; &nbsp; &nbsp; where T : class{&nbsp; &nbsp; &nbsp; &nbsp; Type type = typeof(T);&nbsp; &nbsp; &nbsp; &nbsp; PropertyDescriptorCollection typeDescriptor = null;&nbsp; &nbsp; &nbsp; &nbsp; if (_componentsCache.ContainsKey(type.Name))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; typeDescriptor = _componentsCache[type.Name];&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; typeDescriptor = TypeDescriptor.GetProperties(type);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _componentsCache.Add(type.Name, typeDescriptor);&nbsp; &nbsp; &nbsp; &nbsp; }}步骤2- 循环通过过滤器在获取了变量中泛型类(如上所示)后,现在让我们遍历筛选器,看看其任何属性名称是否与任何筛选器键匹配。如果属性名称与任何筛选器键匹配,则现在我们检查属性的实际值是否与筛选器值匹配。为了提高搜索/筛选函数的质量,我们将在 C# 中使用正则表达式来确定比较是命中还是未命中。PropertyDescriptorCollectionTtypeDescriptorTfor (int i = 0; i < filters.Count; i++){&nbsp; &nbsp; string filterName = filters.GetKey(i);&nbsp; &nbsp; string filterValue = filters[i];&nbsp; &nbsp; PropertyDescriptor propDescriptor = typeDescriptor[filterName];&nbsp; &nbsp; if (propDescriptor == null)&nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; else&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; string propValue = propDescriptor.GetValue(obj).ToString();&nbsp; &nbsp; &nbsp; &nbsp; bool isMatch = Regex.IsMatch(propValue, $"({filterValue})");&nbsp; &nbsp; &nbsp; &nbsp; if (isMatch)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; }}步骤3- 实现扩展方法。为了使我们编写的代码易于使用和重用,我们将在 C# 中实现扩展方法,以便我们可以在项目中的任何位置更好地重用我们的函数。- 使用上述函数的通用集合筛选器函数。由于 可以通过 .函数在 中,我们将在函数调用中使用它,如下所示。IQueryable<T>IEnumerable<T>Where()System.Linqpublic static IEnumerable<T> Filter<T>(this IEnumerable<T> collection, NameValueCollection filters)&nbsp; &nbsp; &nbsp; &nbsp; where T : class{&nbsp; &nbsp; if (filters.Count < 1)&nbsp; &nbsp; &nbsp; &nbsp; return collection;&nbsp; &nbsp; return collection.Where(x => x.InnerFilter(filters));}步骤4将所有内容放在一起。现在我们已经拥有了所需的一切,让我们看看最终/完整代码在单个类中如何看起来像一个代码块。staticpublic static class Question54484908&nbsp;{&nbsp; &nbsp; private static IDictionary<string, PropertyDescriptorCollection> _componentsCache = new Dictionary<string, PropertyDescriptorCollection> ();&nbsp; &nbsp; public static IEnumerable<T> Filter<T> (this IEnumerable<T> collection, NameValueCollection filters)&nbsp; &nbsp; &nbsp; &nbsp; where T : class&nbsp;&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (filters.Count < 1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return collection;&nbsp; &nbsp; &nbsp; &nbsp; return collection.Where (x => x.InnerFilter (filters));&nbsp; &nbsp; }&nbsp; &nbsp; internal static bool InnerFilter<T> (this T obj, NameValueCollection filters)&nbsp; &nbsp; &nbsp; &nbsp; where T : class&nbsp;&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; Type type = typeof (T);&nbsp; &nbsp; &nbsp; &nbsp; PropertyDescriptorCollection typeDescriptor = null;&nbsp; &nbsp; &nbsp; &nbsp; if (_componentsCache.ContainsKey (type.Name))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; typeDescriptor = _componentsCache[type.Name];&nbsp; &nbsp; &nbsp; &nbsp; else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; typeDescriptor = TypeDescriptor.GetProperties (type);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _componentsCache.Add (type.Name, typeDescriptor);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < filters.Count; i++) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; string filterName = filters.GetKey (i);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; string filterValue = filters[i];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PropertyDescriptor propDescriptor = typeDescriptor[filterName];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (propDescriptor == null)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; string propValue = propDescriptor.GetValue (obj).ToString ();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bool isMatch = Regex.IsMatch (propValue, $"({filterValue})");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (isMatch)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return false;&nbsp; &nbsp; }}最后过滤 、 、 数组IEnumerable<T>List<T>这就是您将在项目中的任何位置使用上述代码的方式。private IEnumerable<Question> _questions;_questions = new List<Question>(){&nbsp; &nbsp; new Question("Question 1","How do i work with tuples"),&nbsp; &nbsp; new Question("Question 2","How to use Queryable.Where when type is set at runtime?")};var filters = new NameValueCollection&nbsp;{&nbsp;&nbsp; &nbsp;{ "Description", "work" }};var results = _questions.Filter(filters);滤波DbSet<T>每个都有一个函数,该函数返回可以用作 a 的函数,因此我们的函数可以使用,如下所示。DbContext.Set<T>DbSet<T>IQueryable<T>例_dbContext.Set<Question>().Filter(filters);希望这能回答你的问题,或者更确切地说,为你指出正确的方向。
打开App,查看更多内容
随时随地看视频慕课网APP