asp.net 带有多个参数的Func< T,string>

whlutmcx  于 2023-06-25  发布在  .NET
关注(0)|答案(4)|浏览(164)
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();
33qvvth1

33qvvth11#

你可以使用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)));

当然,您也可以创建自己的自定义类来 Package 这些参数,这样您就不必重复指定委托类型了。
同样的技术也可以很容易地应用到我上面列出的IQueryable解决方案中。

xggvc2p6

xggvc2p62#

一种选择是什么都不做,像这样调用现有的方法:

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>>(每个参数一个或多个选择器)。
在这一点上,代码将开始看起来非常粗糙,因为您必须在每次调用扩展方法之前建立一个字典,因此第一个选项因其清晰和相对简洁而变得更有吸引力。

jgovgodb

jgovgodb3#

如果您正在研究组合表达式,可以通过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);
  }
}
5jdjgkvh

5jdjgkvh4#

我遇到了这个问题,然后偶然发现了一个更好的答案,这个答案现在已经有了。你可以传入一个带有参数的元组。特别是现在我们已经命名元组可用:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples

相关问题