我们正在研究一个日志查看器。 使用时可以选择按用户,严重性等进行过滤。在Sql天内,我将添加到查询字符串中,但是我想使用Linq进行过滤。 如何有条件地添加子句?
如果您只想过滤通过特定条件的情况,请执行以下操作
1 2 3 4 5 6 7 8
| 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正是您所需要的,而且仅此而已。
如果您需要基于列表/数组进行过滤,请使用以下命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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();
} |
我使用了类似于达人的答案,但是有了一个IQueryable接口:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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()最后才会运行。
当谈到有条件的linq时,我非常喜欢过滤器和管道模式。
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-3/
基本上,您为每个接受IQueryable和参数的过滤器案例创建一个扩展方法。
1 2 3 4
| public static IQueryable<Type> HasID(this IQueryable<Type> query, long? id)
{
return id.HasValue ? query.Where(o => i.ID.Equals(id.Value)) : query;
} |
我用扩展方法解决了这个问题,该方法允许在流利的表达式中间有条件地启用LINQ。这消除了用if语句分解表达式的需要。
.If()扩展方法:
1 2 3 4 5 6 7
| public static IQueryable<TSource> If<TSource>(
this IQueryable<TSource> source,
bool condition,
Func<IQueryable<TSource>, IQueryable<TSource>> branch)
{
return condition ? branch(source) : source;
} |
这使您可以执行以下操作:
1 2 3 4
| 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表达式:
1 2 3 4 5 6 7
| public static IEnumerable<TSource> If<TSource>(
this IEnumerable<TSource> source,
bool condition,
Func<IEnumerable<TSource>, IEnumerable<TSource>> branch)
{
return condition ? branch(source) : source;
} |
另一个选择是使用类似此处讨论的PredicateBuilder的东西。
它允许您编写如下代码:
1 2 3 4 5 6 7
| 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,此方法才能正常工作。我对此有一个疑问。
这样做:
1
| bool lastNameSearch = true/false; // depending if they want to search by last name, |
在where语句中具有以下内容:
1
| where (lastNameSearch && name.LastNameSearch =="smith") |
表示在创建最终查询时,如果lastNameSearch为false,则该查询将完全省略用于姓氏搜索的任何SQL。
您可以创建和使用此扩展方法
1 2 3 4
| public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool isToExecute, Expression<Func<TSource, bool>> predicate)
{
return isToExecute ? source.Where(predicate) : source;
} |
我最近有一个类似的要求,最终在MSDN中找到了这个要求。
Visual Studio 2008的CSharp示例
下载的DynamicQuery示例中包含的类使您可以在运行时以以下格式创建动态查询:
1 2 3 4 5
| var query =
db.Customers.
Where("City = @0 and Orders.Count >= @1","London", 10).
OrderBy("CompanyName").
Select("new(CompanyName as Name, Phone)"); |
使用此方法,您可以在运行时动态构建查询字符串,并将其传递给Where()方法:
1 2 3 4
| string dynamicQueryString ="City = \"London\" and Order.Count >= 10";
var q = from c in db.Customers.Where(queryString, null)
orderby c.CompanyName
select c; |
这不是最漂亮的东西,但是您可以使用lambda表达式并可选地传递您的条件。在TSQL中,我做了很多以下操作以使参数可选:
WHERE Field = @FieldVar OR @FieldVar IS NULL
您可以使用以下lambda(检查身份验证的示例)来复制相同的样式:
MyDataContext db = new MyDataContext();
void RunQuery(string param1, string param2, int? param3){
Func checkUser = user =>
((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);
blockquote>
}
blockquote>
好吧,我认为您可以将过滤条件放入通用谓词列表中:
1 2 3 4 5 6 7 8 9 10 11
| 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进行完全优化,以对谓词进行完全不同的功能。
您可以使用外部方法:
1 2 3 4 5 6 7 8 9 10
| var results =
from rec in GetSomeRecs()
where ConditionalCheck(rec)
select rec;
...
bool ConditionalCheck( typeofRec input ) {
...
} |
这是可行的,但不能分解成表达式树,这意味着Linq to SQL会对每个记录运行检查代码。
或者:
1 2 3 4 5 6
| var results =
from rec in GetSomeRecs()
where
(!filterBySeverity || rec.Severity == severity) &&
(!filterByUser|| rec.User == user)
select rec; |
这可能适用于表达式树,这意味着将优化Linq to SQL。
只需使用C#的&&运算符即可:
1
| var items = dc.Users.Where(l => l.Date == DateTime.Today && l.Severity =="Critical") |
编辑:嗯,需要更仔细地阅读。您想知道如何有条件地添加其他子句。在那种情况下,我不知道。 :)我可能要做的只是准备几个查询,并根据我最终需要执行的查询来执行正确的查询。