[英]Func<T, string> with multiple parameters
public static IEnumerable<T> MyMethod<T>(this IEnumerable<T> entity,
string param, Func<T, string> selector)
{
return entity.Where(l =>
System.Data.Objects.SqlClient.SqlFunctions.PatIndex(param, "%"),
selector(l)) > 0);
}
我按以下方式使用它:
dbContext.ENTITY.MyMethod("%foo", f => f.SomeProp).ToList();
但是如何使MyMethod
足夠通用以處理Func<T, string> selector
參數上的任意數量的屬性?
我想我可以將類型更改為List<Func<T, string>> selector
並對其進行迭代,但我想有更好的方法嗎?
更新:
傳遞兩個參數時的期望結果(例如):
dbContext.ENTITY.Where(l =>
SqlFunctions.PatIndex("%foo", l.prop1) > 0 &&
SqlFunctions.PatIndex("%bar%", l.prop2) > 0).ToList();
您可以使用params
關鍵字來接受任意數量的參數:
public static IEnumerable<T> MyMethod<T>(this IEnumerable<T> entity,
string param,
params Func<T, string>[] selectors)
{
foreach(var selector in selectors)
{
entity = entity.Where(l =>
SqlFunctions.PatIndex(param, selector(l)) > 0);
}
return entity;
}
但是,我懷疑這將在實體框架中按原樣工作。 我認為您需要使用Expression<Func<T, string>>
實體框架可以代替它轉換 SQL:
public static IEnumerable<T> MyMethod<T>(
this IQueryable<T> entity,
string pattern,
params Expression<Func<T, string>>[] selectors)
{
var method = typeof(SqlFunctions).GetMethod("PatIndex");
foreach(var selector in selectors)
{
var param = Expression.Parameter(typeof(T));
var call = Expression.Call(method, Expression.Constant(pattern), selector);
var gt = Expression.GreaterThan(call, Expression.Constant(0));
var filter = Expression.Lamda(call, param);
entity = entity.Where(filter);
}
return entity;
}
然后你可以這樣稱呼它:
dbContext.ENTITY.MyMethod("%foo",
f => f.SomeProp1,
f => f.SomeProp2,
f => f.SomeProp3).ToList();
要將其與多個pattern
參數一起使用,您可以使用字典(我不會實現方法主體,因為從上面的代碼中可以很明顯地做到這一點):
public static IEnumerable<T> MyMethod<T>(
this IEnumerable<T> entity,
Dictionary<string, Func<T, bool>> filters)
{
...
}
你可以這樣調用:
dbContext.ENTITY.MyMethod(new Dictionary<string, Func<Entity, bool>>()
{
{ "%foo", l => l.Prop1 },
{ "%bar", l => l.Prop2 },
});
或者,您可以使用一組元組:
public static IEnumerable<T> MyMethod<T>(
this IQueryable<T> entity,
string pattern,
params Tuple<string, Func<T, string>>[] filters)
{
...
}
你可以這樣調用:
dbContext.ENTITY.MyMethod(
Tuple.Create("%foo", (Func<Entity, string>)(l => l.Prop1)),
Tuple.Create("%bar", (Func<Entity, string>)(l => l.Prop2)));
當然,您也可以制作自己的自定義類來包裝這些參數,這樣您就不必重復指定委托類型。
同樣的技術可以很容易地應用於我上面列出的IQueryable
解決方案。
一種選擇是不做任何更改並像這樣調用現有的方法:
dbContext.ENTITY
.MyMethod("%foo", f => f.SomeProp)
.MyMethod("%foo", f => f.AnotherProp)
.MyMethod("%bar", b => b.SomethingElse)
.ToList();
這就是你在你的方法中必須做的事情,無論如何都需要多個選擇器(在循環中構建Where
s)。
public static IEnumerable<T> MyMethod<T>(this IEnumerable<T> entities,
string param, params Func<T, string>[] selectors)
{
IEnumerable<T> results = entities;
foreach(var selector in selectors)
{
results = results.Where(l -> …selector(l)…);
}
return results;
}
但是,當您想將不同的選擇器與不同的string param
關聯時,這不起作用。 在這種情況下,您需要傳入IDictionary<string, Func<T,string>>
(每個參數一個選擇器)或ILookup<string, Func<T,string>>
(每個參數一個或多個選擇器) .
在這一點上,代碼將開始看起來非常粗糙,因為您必須在每次調用擴展方法之前構建一個字典,因此第一個選項因其清晰和相對簡潔而變得更具吸引力。
如果您正在研究組合表達式,您可以通過 PredicateBuilder 來完成 - 即:
public static IEnumerable<T> MyMethod<T>(this IEnumerable<T> entity, string param, params Func<T, string>[] selectors) {
var expression = PredicateBuilder.Make<T>(t => true);
foreach(var selector in selectors) {
expression = PredicateBuilder.And<T>(expression,
l =>
System.Data.Objects.SqlClient.SqlFunctions.PatIndex(param, "%", selector(l)) > 0);
}
return entity.AsQueryable().Where(expression).AsEnumerable();
}
public static class PredicateBuilder {
public static Expression<Func<T, bool>> Make<T>() {
return null;
}
public static Expression<Func<T, bool>> Make<T>(this Expression<Func<T, bool>> predicate) {
return predicate;
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> orExpression) {
if (expr == null) {
return orExpression;
}
var invokedExpr = Expression.Invoke(orExpression, expr.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.Or(expr.Body, invokedExpr), expr.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> andExpression) {
if (expr == null) {
return andExpression;
}
var invokedExpr = Expression.Invoke(andExpression, expr.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.And(expr.Body, invokedExpr), expr.Parameters);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.