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