条件Linq查询


92

我们正在研究日志查看器。使用时可以选择按用户,严重性等进行过滤。在Sql天内,我将添加到查询字符串中,但是我想使用Linq进行过滤。如何有条件地添加子句?

Answers:


156

如果仅在通过某些条件时才要过滤,请执行以下操作

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子句设为OR而不是ANDs ..有什么建议吗?
乔恩

1
是的...很难做。我所见过的最好的方法是通过规范模式并将谓词拉入规范,然后调用Specification.Or(someOtherSpecification)。基本上,您必须稍微编写自己的表达式树。此处的示例代码和说明:codeinsanity.com/archive/2008/08/13/…–
Darren Kopp

我有一个愚蠢的问题,如果要从数据库中获取这些日志,我们是否要获取所有日志,然后将其过滤到内存中?如果是这样,那么我该如何将条件传递给数据库
Ali Umair

它不是在内存中过滤它们。它正在建立一个查询并发送数据库中的所有条件(至少对于大多数linq-to-x提供程序而言)
Darren Kopp

收到此错误LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Ali Umair

22

如果您需要基于列表/数组进行过滤,请使用以下命令:

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

    }

3
到目前为止,这是最好,最正确的答案。有条件的|| 仅比较第一部分,如果第一部分为真,则跳过第二部分。
Serj Sagan 2012年

1
此构造在生成的SQL查询中包括表达式的“或”部分。接受的答案将生成更有效的陈述。当然,取决于数据提供者的优化。LINQ-to-SQL可能有更好的优化,但是LINQ-to-Entities没有。
Suncat2000 '16

20

我使用了类似于达人的答案,但是有了一个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()最后才运行。



8

我用扩展方法解决了这个问题,该方法允许在流利的表达式中间有条件地启用LINQ。这消除了用if语句分解表达式的需要。

.If() 扩展方法:

public static IQueryable<TSource> If<TSource>(
        this IQueryable<TSource> source,
        bool condition,
        Func<IQueryable<TSource>, IQueryable<TSource>> branch)
    {
        return condition ? branch(source) : source;
    }

这使您可以执行以下操作:

return context.Logs
     .If(filterBySeverity, q => q.Where(p => p.Severity == severity))
     .If(filterByUser, q => q.Where(p => p.User == user))
     .ToList();

这也是一个IEnumerable<T>可以处理大多数其他LINQ表达式的版本:

public static IEnumerable<TSource> If<TSource>(
    this IEnumerable<TSource> source,
    bool condition,
    Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
    {
        return condition ? branch(source) : source;
    }

4

另一个选择是使用此处讨论的PredicateBuilder之类的东西。它允许您编写如下代码:

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,此方法才能正常工作。我在这里对这个问题有疑问。


对于那些在存储库顶部使用业务逻辑层以及诸如AutoMapper之类的工具在数据传输对象和实体模型之间进行映射的人来说,这是一个很好的方法。使用谓词生成器将允许您动态地将IQueryable修改,然后再将其发送到AutoMapper进行展平,即将列表放入内存。请注意,它也支持实体框架。
chrisjsherm

3

这样做:

bool lastNameSearch = true/false; // depending if they want to search by last name,

where语句中包含以下内容:

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

表示在创建最终查询时,如果lastNameSearch是,false则查询将完全省略用于姓氏搜索的任何SQL。


取决于数据提供者。LINQ-to-Entities无法很好地对其进行优化。
Suncat2000 '16

1

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

WHERE字段= @FieldVar或@FieldVar为NULL

您可以使用以下lambda(检查身份验证的示例)来复制相同的样式:

MyDataContext db = new MyDataContext();

void RunQuery(string param1,string param2,int?param3){

Func checkUser =用户=>

(((param1.Length> 0)?user.Param1 == param1:1 == 1)&&

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

(((param3!= null)?user.Param3 == param3:1 == 1);

用户foundUser = db.Users.SingleOrDefault(checkUser);

}


1

我最近有一个类似的要求,最终在MSDN中找到了这个要求。 Visual Studio 2008的CSharp示例

下载的DynamicQuery示例中包含的类允许您在运行时以以下格式创建动态查询:

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

使用此方法,您可以在运行时动态构建查询字符串,并将其传递给Where()方法:

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

1

您可以创建和使用此扩展方法

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
    return isToExecute ? source.Where(predicate) : source;
}

0

只需使用C#的&&运算符即可:

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

编辑:嗯,需要更仔细地阅读。您想知道如何有条件地添加其他子句。在那种情况下,我不知道。:)我可能要做的只是准备几个查询,然后根据我最终需要执行的查询来执行正确的查询。


0

您可以使用外部方法:

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

...

bool ConditionalCheck( typeofRec input ) {
    ...
}

这是可行的,但不能分解成表达式树,这意味着Linq to SQL会对每个记录运行检查代码。

或者:

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

这可能在表达式树中起作用,这意味着将优化Linq to SQL。


0

好吧,我认为您可以将过滤条件放入通用谓词列表中:

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

这将导致包含“ me”,“ meyou”和“ mow”的列表。

您可以通过对谓词执行foreach来优化此功能,而对谓词使用完全不同的函数来对所有谓词进行“或”运算。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.