有条件的Linq查询

问题描述:

我们正在开发日志查看器。用户可以选择根据用户,严重程度等进行过滤。在SQL日期中,我将添加到查询字符串中,但我想用Linq来完成。我怎样才能有条件地添加where-clause?有条件的Linq查询

,如果你想,如果某些条件被传递到只能过滤,这样做

var logs = from log in context.Logs 
      select log; 

if (filterBySeverity) 
    logs = logs.Where(p => p.Severity == severity); 

if (filterByUser) 
    logs = logs.Where(p => p.User == user); 

否则,这样可以让你的表达树是你想要什么。这样,创建的SQL将完全符合您的需求,而且毫无问题。

+2

嗨你有任何建议使where子句ORs而不是ANDs ..? – 2012-09-09 08:06:57

+1

是的,这样做有点困难。我见过的最好的是通过规范模式并将谓词拉入规范中,然后调用规范。或者(someOtherSpecification)。基本上你必须写一点你自己的表达树。示例代码和解释在这里:http://codeinsanity.com/archive/2008/08/13/implementing-repository-and-specification-patterns-using-linq.aspx – 2012-09-10 17:16:19

+0

我有一个愚蠢的问题,如果这些日志被收购从数据库中,我们得到所有的日志,然后在内存中过滤它们?如果是这样,那么我怎么能通过条件数据库 – 2016-05-28 18:21:17

只要使用C#的& &操作:

var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity == "Critical") 

编辑:啊,需要更仔细地阅读。你想知道如何有条件地添加额外的子句。在那种情况下,我不知道。 :)我可能会做的只是准备几个查询,并执行正确的,取决于我最终需要什么。

你可以使用一个外部方法:

var results = 
    from rec in GetSomeRecs() 
    where ConditionalCheck(rec) 
    select rec; 

... 

bool ConditionalCheck(typeofRec input) { 
    ... 
} 

这会工作,但不能被分解成表达式树,这意味着LINQ到SQL将运行对每个记录的校验码。

或者:

var results = 
    from rec in GetSomeRecs() 
    where 
     (!filterBySeverity || rec.Severity == severity) && 
     (!filterByUser|| rec.User == user) 
    select rec; 

可能在表达式目录树工作,这意味着LINQ到SQL会进行优化。

那么,我以为是你可以把过滤条件变成谓词的泛型列表:

var list = new List<string> { "me", "you", "meyou", "mow" }; 

    var predicates = new List<Predicate<string>>(); 

    predicates.Add(i => i.Contains("me")); 
    predicates.Add(i => i.EndsWith("w")); 

    var results = new List<string>(); 

    foreach (var p in predicates) 
     results.AddRange(from i in list where p.Invoke(i) select i);    

导致含有“我”,“meyou”列表,“割”。

您可以通过对谓词进行优化,使用完全不同的函数来处理所有谓词。

这不是最漂亮的事情,但您可以使用lambda表达式并选择性地传递您的条件。在TSQL我做了很多以下,使参数可选的:

where字段= @FieldVar或@FieldVar IS NULL

你可以复制同样的风格,配有下列拉姆达(一个例子检查验证):

MyDataContext db = new MyDataContext();

空隙RunQuery(字符串参数1,串param2的,INT?参数3){

Func键checkUser =用户=>

((param1.Length> 0)?用户。参数1 ==参数1:1 == 1)& &

((param2.Length> 0)user.Param2 == param2的:1 == 1)& &

(!(参数3 = NULL) ?user.Param3 == param3:1 == 1);

User foundUser = db.Users.SingleOrDefault(checkUser);

}

当涉及到有条件的LINQ,我很喜欢的过滤器和管道模式。
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/

基本上你会为每个接受IQueryable和参数的过滤器案例创建一个扩展方法。

public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id) 
{ 
    return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query; 
} 

另一种选择是使用类似于PredicateBuilder讨论的here。 它允许你编写类似下面的代码:

var newKids = Product.ContainsInDescription ("BlackBerry", "iPhone"); 

var classics = Product.ContainsInDescription ("Nokia", "Ericsson") 
        .And (Product.IsSelling()); 

var query = from p in Data.Products.Where (newKids.Or (classics)) 
      select p; 

请注意,我只带了这使用LINQ 2 SQL工作。 EntityFramework不实现此方法工作所需的Expression.Invoke。我对这个问题有一个问题here

我最近有一个类似的要求,最终在他MSDN中找到了这个。包括下载的DynamicQuery样品中 CSharp Samples for Visual Studio 2008

的类,可以按以下格式在运行时创建的动态查询:

var query = 
db.Customers. 
Where("City = @0 and Orders.Count >= @1", "London", 10). 
OrderBy("CompanyName"). 
Select("new(CompanyName as Name, Phone)"); 

利用这一点,你可以在运行时动态地构建一个查询字符串,并将其传递到哪里()方法:

string dynamicQueryString = "City = \"London\" and Order.Count >= 10"; 
var q = from c in db.Customers.Where(queryString, null) 
     orderby c.CompanyName 
     select c; 

我最终使用类似于达人的答案,但有一个IQueryable接口:

IQueryable<Log> matches = m_Locator.Logs; 

// Users filter 
if (usersFilter) 
    matches = matches.Where(l => l.UserName == comboBoxUsers.Text); 

// Severity filter 
if (severityFilter) 
    matches = matches.Where(l => l.Severity == comboBoxSeverity.Text); 

Logs = (from log in matches 
     orderby log.EventTime descending 
     select log).ToList(); 

在命中数据库之前构建查询。该命令直到.ToList()结束时才会运行。

如果您需要在列表过滤器底座/阵列使用以下命令:

public List<Data> GetData(List<string> Numbers, List<string> Letters) 
    { 
     if (Numbers == null) 
      Numbers = new List<string>(); 

     if (Letters == null) 
      Letters = new List<string>(); 

     var q = from d in database.table 
       where (Numbers.Count == 0 || Numbers.Contains(d.Number)) 
       where (Letters.Count == 0 || Letters.Contains(d.Letter)) 
       select new Data 
       { 
        Number = d.Number, 
        Letter = d.Letter, 
       }; 
     return q.ToList(); 

    } 

这样做:

​​

where声明中有这样的:

where (lastNameSearch && name.LastNameSearch == "smith") 

意味着当最终查询被创建时,如果lastNameSearchfalse查询将完全省略任何用于姓氏搜索的SQL。