2 回答

TA貢獻1831條經(jīng)驗 獲得超4個贊
回答你的具體問題。鑒于
IQueryable source LambdaExpression predicate
如何調(diào)用靜態(tài)泛型方法
Queryable.Where<T>(IQueryable<T> source, Expression<Func<T, bool>> predicate)
它可以使用(A)反射,(B)DLR動態(tài)調(diào)度和(C)來完成。Expression.Call
您要做的是選項(B)。然而
var result = Queryable.Where((dynamic)source, predicate);
對具有類型第二個參數(shù)的方法進行動態(tài)搜索,這當然會失敗。LambdaExpression
為了能夠動態(tài)匹配目標方法,您還需要創(chuàng)建第二個參數(shù):dynamic
var result = Queryable.Where((dynamic)source, (dynamic)predicate);
上述等效選項(C)的實現(xiàn)是:
var result = source.Provider.CreateQuery(Expression.Call( typeof(Queryable), nameof(Queryable.Where), new[] { source.ElementType }, source.Expression, predicate));

TA貢獻1827條經(jīng)驗 獲得超4個贊
恭喜你的第一個問題。
讓我們首先看一下基于某些自定義篩選器篩選數(shù)據(jù)集合的方法。我將假設(shè)您更喜歡傳入篩選器,將屬性名稱保存為鍵,將屬性值保存為值。NameValueCollectionType
在繼續(xù)篩選整個集合之前,讓我們首先弄清楚如何確定一個對象是否具有與篩選器匹配的屬性。由于我們直到運行時才知道對象,因此我們需要在 C# 中使用泛型來實現(xiàn)此目的。Type
步驟1
- 獲取所有類屬性
我們需要獲取泛型類的所有屬性,例如 .使用反射執(zhí)行此操作被認為是緩慢的,Matt Warren解釋了為什么反射在.NET中很慢以及如何解決它。因此,我們將實現(xiàn)類組件模型的緩存,以獲取其存在于命名空間 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
- 循環(huán)通過過濾器
在獲取了變量中泛型類(如上所示)后,現(xiàn)在讓我們遍歷篩選器,看看其任何屬性名稱是否與任何篩選器鍵匹配。如果屬性名稱與任何篩選器鍵匹配,則現(xiàn)在我們檢查屬性的實際值是否與篩選器值匹配。為了提高搜索/篩選函數(shù)的質(zhì)量,我們將在 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
- 實現(xiàn)擴展方法。
為了使我們編寫的代碼易于使用和重用,我們將在 C# 中實現(xiàn)擴展方法,以便我們可以在項目中的任何位置更好地重用我們的函數(shù)。
- 使用上述函數(shù)的通用集合篩選器函數(shù)。
由于 可以通過 .函數(shù)在 中,我們將在函數(shù)調(diào)用中使用它,如下所示。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
將所有內(nèi)容放在一起。
現(xiàn)在我們已經(jīng)擁有了所需的一切,讓我們看看最終/完整代碼在單個類中如何看起來像一個代碼塊。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;
}
}
最后
過濾 、 、 數(shù)組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>
每個都有一個函數(shù),該函數(shù)返回可以用作 a 的函數(shù),因此我們的函數(shù)可以使用,如下所示。DbContext.Set<T>DbSet<T>IQueryable<T>
例
_dbContext.Set<Question>().Filter(filters);
希望這能回答你的問題,或者更確切地說,為你指出正確的方向。
- 2 回答
- 0 關(guān)注
- 110 瀏覽
添加回答
舉報