2012-05-07 19 views
6

我正在为我的实体框架应用程序编写一个简单的搜索查询。我需要检查一堆字段是否为空,如果不是,请对它们调用ToLower()并与搜索查询进行比较。 LINQ查询看起来是这样的:有没有一种简单的方法在LINQ to Entities中编写自定义函数?

public IQueryable<Store> SearchStores(string q, IQueryable<Store> source) 
{ 
    q = q.ToLower(); 

    return (
     from s in source 
     where (
      (s.Name != null && s.Name.ToLower().Contains(q)) || 
      (s.Description != null && s.Description.ToLower().Contains(q)) || 
      ... 
} 

有很多这样的线,所以我很想写一个辅助方法来清理了一下:

public static bool SafeSearch(this string s, string q) 
{ 
    return s == null ? false : s.ToLower().Contains(q); 
} 

这当然但是,由于LINQ to Entities并不理解安全搜索功能是什么:

LINQ to Entities无法识别方法'Boolean SafeSearch(System.String,System.String)'方法,而且这种方法不能transla成为商店的表现。

有没有简单的方法来写这样一个简单的自定义函数?

谢谢!

+0

数据库上的排序规则类型是什么? – Brannon

回答

2

由于linq使用的表达式在你实际调用数据库之前不会执行,所以你需要将你的函数包装在一个谓词中。

private static Func<Country, bool> Predicate(string q) 
{ 
    return x => (
     q.SafeSearch(x.Name) || 
     q.SafeSearch(x.Description) 
     ); 
} 

通过在查询中调用SafeSearch扩展方法,还将处理x.Name为null的情况。

public static class SearchExt 
{ 
    public static bool SafeSearch(this string q, string param) 
    { 
     return param == null ? false : param.ToLower().Contains(q); 
    } 
} 

,然后你可以用extesion方法

return source.Where(Predicate(q)); 

或使用LINQ表达

return from p in source 
     where Predicate(q).Invoke(p) 
     select p; 
+0

这是使用扩展方法,但是当我在linq中使用Invoke方法尝试它时,我得到:LINQ to Entities不能识别该方法的布尔Invoke(Localsip.Models.Wine)方法,并且此方法不能翻译成商店表达。有关于此的任何想法? – ManicBlowfish

+0

噢,好够了。仍然想知道如何在linq查询中包含“Predicate”Func。尽管感谢您的帮助。 – ManicBlowfish

+0

@ManicBlowfish你可以从源代码中返回p。Where(Predicate(q))选择p –

1

有一种方法来准备动态查询和条件,并且还要用用它功能来构建它们的一部分。语法也是可读的,这可以解决问题的“简单”部分。有可能通过结合Linq表达式。有几篇关于如何完成的文章,但我想我想出了一个新的方法。至少我没有在网上找到它。

要继续,您需要一个包含3个简单功能的库。他们使用System.Linq.Expressions.ExpressionVisitor来动态修改表达式。关键特性是将表达式中的参数统一起来,以便使具有相同名称的两个参数相同(UnifyParametersByName)。其余部分将用给定表达式(ReplacePar)和辅助方法(NewExpr)替换命名参数。该图书馆提供github上的MIT许可证:LinqExprHelper,但您可以自行快速编写一些内容。

首先定义一些方法,稍后可用于创建动态查询。

public class Store 
{ 
    ... 

    public static Expression<Func<Store, bool>> 
     SafeSearchName(string sWhat) 
    { 
     return LinqExprHelper.NewExpr(
      (Store s) => s.Name != null && s.Name.ToLower().Contains(sWhat) 
     ); 
    } 

    public static Expression<Func<Store, bool>> 
     SafeSearchDesc(string sWhat) 
    { 
     return LinqExprHelper.NewExpr(
      (Store s) => s.Description != null && s.Description.ToLower().Contains(sWhat) 
     ); 
    } 
} 

然后你以这种方式查询:

// Define a master condition, using named parameters. 
    var masterExpr = LinqExprHelper.NewExpr(
     (Store s, bool bSearchName, bool bSearchDesc) 
     => (bSearchName && bSearchDesc)); 

    // Replace stub parameters with some real conditions. 
    var combExpr = masterExpr 
     .ReplacePar("bSearchName", Store.SafeSearchName("b").Body) 
     .ReplacePar("bSearchDesc", Store.SafeSearchDesc("p").Body); 
     // Sometimes you may skip a condition using this syntax: 
     //.ReplacePar("bSearchDesc", Expression.Constant(true)); 

    // It's interesting to see how the final expression looks like. 
    Console.WriteLine("expr: " + combExpr); 

    // Execute the query using combined expression. 
    db.Stores 
     .Where((Expression<Func<Store, bool>>)combExpr) 
     .ToList().ForEach(i => { Console.WriteLine(i.Name + ", " + i.Description); }); 

我没有在生产中使用这个还没有,但一些简单的测试都通过了。我没有看到用这种方式组合查询有任何限制。如果我们需要更多的参数,我们可以附加额外的组合级别。这种方法的优点是可以使用内联的lambda表达式,这些表达式非常适合阅读,还可以使用动态表达式创建和组合,这非常有用。

毕竟它“简单”吗?如果您认为Linq的方法语法很简单,那么这几乎就是这么简单。它不允许你创建自定义的Linq函数,但是可以提供类似的功能。

+0

表达式为我做了诀窍 –

相关问题