LINQ Where表达式扩展

时间:2014-07-02 06:17:55

标签: c# linq lambda

我想知道如何完成'一个LINQ to Entities表达式。这就是我想写的:

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函数和表达式方面越来越好,但此时不够好:(感谢所有的帮助和建议!

3 个答案:

答案 0 :(得分:3)

如果将方法设计为通用扩展方法(如其他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标志变量添加到您的扩展名mehtod。

编辑 - 试试这个修改:

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

Ok finnaly我决定实现构建表达式树,所以我可以确定它将被转换为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);
}

答案 1 :(得分:2)

由于我现在已经想过你不想编译表达式,你需要根据提供的表达式构建一个新的更复杂的表达式来检索字段值。我冒昧地使解决方案通用,因为代码不需要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。

答案 2 :(得分:0)

Linq查询最终将被转换为SQL。如果你在where子句中有任意函数,你将得到&#34; invoke不支持&#34;错误。以下是否适用于您?

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);
}