要扩展的表达式

本文关键字:表达式 扩展 | 更新日期: 2023-09-27 18:11:00

我想知道如何"完成"LINQ到实体的表达式。这就是我想写的:

IQueryable<Products> qry = ...
qry = ApplyFilter(qry, p => p.Name, "hello");
private IQueryable<Products> ApplyFilter(
          IQueryable<Products> qry, 
          Expression<Func<Products,String>> field, 
          String likeFilter)
{
  // ???
  return qry.Where( field.Contains( likeFilter )); 
}

调用语法很重要(需要简单和干净),函数参数和函数体是弱点。:)我在lambda函数和表达式方面越来越好,但目前还不够好:)感谢所有的帮助和建议!

要扩展的表达式

如果你把你的方法设计成一个通用的扩展方法(就像其他的linq扩展),这会看起来更好。

public static class Extensions
{
    public static IQueryable<T> ApplyFilter<T>(this IQueryable<T> qry, Func<T, string> field, string likeFilter)
    {
        return qry.Where(x => field(x).Contains(likeFilter));
    }
}

用法:

IQueryable<Product> qry = new List<Product>() 
    { 
        new Product() {Name = "Ball", Category = "Sport"},
        new Product() {Name = "Bag", Category = "Other"},
        new Product() {Name = "Sport bag", Category = "Sport"},
    }.AsQueryable();
var result = qry.ApplyFilter(p => p.Category, "Sport");

另外,您可以将caseSensitive标志变量添加到您的扩展方法中。

编辑 -试试这个修改:

public static IEnumerable<T> ApplyFilter<T>(this IQueryable<T> qry, Func<T, string> field, string likeFilter)
{
    foreach (var item in qry)
    {
        if (field(item).Contains(likeFilter))
        {
            yield return item;
        }
    }
}

不幸的是,我不知道这是否打破了IQueryable从句的积累。

编辑2

好,最后我决定实现作为构建表达式树,所以我可以肯定它会被翻译成SQL成功。最终(我希望:D)解决方案:

public static IQueryable<T> ApplyFilter<T>(this IQueryable<T> qry, Expression<Func<T, string>> field, string likeFilter)
{
    var member = field.Body as MemberExpression;
    var propInfo = member.Member as PropertyInfo;
    var param = Expression.Parameter(typeof(T), "x");
    var prop = Expression.Property(param, propInfo);
    var containsMethod = typeof(string).GetMethod("Contains");
    var body = Expression.Call(prop, containsMethod, Expression.Constant(likeFilter));
    var expr = Expression.Lambda<Func<T, bool>>(body, param);
    return qry.Where(expr);
}

由于我现在认为您不想编译表达式,您需要基于所提供的表达式构建一个新的更复杂的表达式来检索字段值。我冒昧地使解决方案通用,因为代码不需要Products类型:

private IQueryable<T> ApplyFilter<T>(
          IQueryable<T> qry,
          Expression<Func<T,String>> field,
          String likeFilter)
{
  var methodInfo = typeof(String).GetMethod("Contains");
  var methodCallExpression = Expression
    .Call(field.Body, methodInfo, Expression.Constant(likeFilter));
  var predicate = Expression
    .Lambda<Func<T, Boolean>>(methodCallExpression, field.Parameters[0]);
  return qry.Where(predicate);
}

如果field表达式为p => p.Name,则predicate表达式为p => p.Name.Contains(likeFilter)。实体框架能够理解这个表达式并将其转换成SQL

Linq查询最终将被转换为SQL。如果在where子句中有任意函数,则会得到"不支持调用"错误。下面的方法对你有用吗?

public class Products
{
    public string Name { get; set; }
}

static void Main()
{
    IQueryable<Products> qry = new List<Products> {         
        new Products() {Name = "Football" },
        new Products() {Name = "Baseball" },
        new Products() {Name = "Glove" },
    }.AsQueryable();
    var r = ApplyFilter(qry, p => p.Name.Contains("ball"));
}

private static IQueryable<Products> ApplyFilter(IQueryable<Products> qry, Expression<Func<Products, bool>> predicate)
{
    return qry.Where(predicate);
}