我知道一种典型的方式是这样的:
IQueryable query = from staff in dataContext.Staffs;
if(name1 != null)
{
query = from staff in query where (staff.name == name1);
}
但是,从我们从其他开发人员手中接过的程序中,我们看到了这样的代码:
IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);
如果这是一个普通的SQL语句,我肯定会说第二个是不好的做法。因为当name1为null时,它会向查询添加无意义的where子句。
但我是LINQ的新手,所以我不确定LINQ是否有所不同?
答案 0 :(得分:18)
你可以像
一样写IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 != null && staff.name == name1);
这样,如果第一个条件评估为假,则不会评估条件的第二部分
<强>更新强>
如果你写
IQueryable query = from staff in dataContext.Staffs;
query = from staff in query where (name1 == null || staff.name == name1);
和name1为null您的条件的第二部分将不会被评估,因为条件只需要一个条件才能返回true
请参阅此link了解更多详情
答案 1 :(得分:10)
使用流利的语法而不是查询语法,这种事情通常会更顺畅。
e.g。
IQueryable query = dataContext.Staffs;
if(name1 != null)
{
query = query.Where(x => x.name == name1);
}
因此,如果name1
为空,则您不会进行任何Where()
调用。如果您有多个不同的过滤器,可能需要或可能不需要,也许各种不同的排序顺序,我发现这变得更易于管理。
编辑alex:好的,我正在回答有关仅在值不为null时添加where子句的问题。在回答问题的其他部分时,我尝试使用Entity Framework 4来查看LINQ生成的SQL。您可以将query
转换为ObjectQuery
并调用.ToTraceString()
来执行此操作。结果是WHERE
条款如下:
WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1
所以,是的,这是经典的错误SQL,如果你在name
列上有索引,不要指望它被使用。
编辑#2:使用LINQ to SQL而不是实体框架再次尝试使用,但结果却截然不同。这一次,尝试name1
为空的查询会导致根本没有WHERE
条款,正如您所希望的那样;在name1
为“a”的情况下尝试将WHERE [t0].[name] = @p0
和@p0
作为“a”发送。实体框架确实不似乎优化。这有点令人担忧。
答案 2 :(得分:3)
执行此操作的最佳方法是创建一个扩展方法,它将接受条件语句和where表达式。如果条件为真,那么它将使用where表达式,否则它将不使用它。这可以大大清理代码,无需if语句。
public static class LinqExtensions
{
public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
{
if (condition)
{
return query.Where(whereClause);
}
return query;
}
}
现在您可以编写如下代码:
IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
答案 3 :(得分:1)
LINQ在其他一些原因中不同(不是这个原因), LINQ是以更快的方式获取数据的方法,尽可能使用小编码和清晰的鳕鱼,LINQ有很多好处:
可以更轻松地将数据转换为对象。我确信你已经听说过“Impedence Mismatch”一词经常被使用,这意味着LINQ减少了在面向对象的代码和数据范例(如分层,平面文件,消息)之间进行转换所必须做的工作量。关系等等。它并没有消除“Impedence Mismatch”,因为你仍然必须以原生形式推理你的数据,但从这里到那里的桥梁(IMO)要短得多。
所有数据的通用语法。一旦学习了查询语法,就可以将它与任何LINQ提供程序一起使用。我认为这是一个比巴塞尔之塔更好的开发范例,这种方式多年来一直通过数据访问技术得到发展。当然,每个LINQ提供程序都有必要的独特细微差别,但基本方法和查询语法是相同的。
强类型代码。 C#(或VB.NET)查询语法是语言的一部分,您使用C#类型进行编码,这些类型被转换为提供者理解的内容。这意味着您可以提高编译器在开发生命周期的早期发现错误的效率。当然,存储过程语法中的许多错误会在您保存时产生错误,但LINQ比SQL Server更通用。您必须考虑生成运行时错误的所有其他类型的数据源,因为它们的查询是使用字符串或其他一些松散类型的机制形成的。
提供商集成。将数据源整合在一起非常容易。例如,对于一些非常复杂的场景,您可以将LINQ to Objects,LINQ to SQL和LINQ to XML一起使用。我觉得它非常优雅。
减少工作量。在LINQ之前,我花了很多时间构建DAL,但现在我的DataContext是DAL。我也使用过OPF,但是现在我有LINQ,它附带了多个提供商和许多其他第三方提供商,从我之前的观点中获益。我可以在一分钟内设置一个LINQ to SQL DataContext(和我的计算机一样快,IDE可以跟上)。
一般情况下的表现不会成为问题。 SQL Server最近很好地优化了查询,就像存储过程一样。当然,由于性能原因,仍然存在需要存储过程的情况。例如,当我在表之间进行多次交互并且在事务内部具有额外逻辑时,我发现使用存储过程更聪明。尝试在代码中执行相同任务的通信开销除了使DTC涉及分布式事务之外,还使得存储过程的选择更加引人注目。但是,对于在单个语句中执行的查询,LINQ是我的首选,因为即使存储过程中的性能提升很小,以前的优点(IMO)也会带来更多的好处。
内置安全性。在LINQ之前我首选存储过程的一个原因是它们强制使用参数,有助于减少SQL注入攻击。 LINQ to SQL已经参数化输入,这同样安全。
LINQ是声明性的。使用LINQ to XML或LINQ to SQL非常注意,但LINQ to Objects非常强大。 LINQ to Objects的典型示例是从字符串[]中读取项目。然而,这只是一个小例子。如果您考虑每天使用的所有IEnumerable集合(您也可以查询IEnumerable),那么机会很多。即,在所有项目上搜索ASP.NET ListBox控件,对两个集合执行集合操作(如Union),或者迭代List并在每个项目的ForEach中运行lambda。一旦你开始在LINQ中思考,这本质上是声明性的,你可以发现许多任务比你现在使用的命令式技术更简单,更直观。
我可能会继续,但我最好停在那里。希望这将为您提供更积极的观点,让您了解如何通过LINQ提高工作效率,并从更广泛的角度将其视为有用的技术。
答案 4 :(得分:1)
我在标准SQL中看到过这种模式,如果你有几个可能为NULL的参数,它似乎很有用。例如:
SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName )
AND ( @LastName IS NULL OR LastName = @LastName )
如果您在LINQ中看到这一点,他们可能会盲目地翻译他们的旧SQL查询。
答案 5 :(得分:1)
我喜欢使用Expression e.g。
Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true;
if (DateBirth.HasValue)
{
Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth);
expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate);
}
IQueryable query = dataContext.Persons;
query = query.Where(expresionFinal);
答案 6 :(得分:1)
所以我尝试了这里列出的.Where(..., x => ...)
扩展方法作为答案,但它不适用于实体框架,因为Linq To Entities不知道如何将其转换为TSQL。
所以这是我的解决方案,我的解决方案:
Expression<Func<SomeEfPoco, bool>> columnBeingFilteredPredicate = x => true; // Default expression to just say yes
if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue))
{
columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue;
}
_context.SomeEfPocos.Where(x => ..... &&
..... &&
..... &&)
.Where(columnBeingFilteredPredicate);
在我的例子中, someColumnBeingFilteredValue
是封装方法的字符串参数,默认值为NULL。
答案 7 :(得分:0)
不,我不是非常同意你的观点。 在这里你只是给出了一个简单的逻辑
if(name1 != null)
// do your stuff
但是如果你做了与name1有不同价值的东西会发生什么...... !!
好的,现在考虑一下这种情况。
在此示例中,您将展示如何处理源集合中可能的空值。
诸如IEnumerable<T>
之类的对象集合可以包含值为null的元素。
如果源集合为null或包含值为null的元素,
并且您的查询不处理空值,执行查询时将抛出NullReferenceException
。
可能这可能是一个问题......
答案 8 :(得分:0)
对于EF Core,我将其分解为:
IQueryable<Partners> recs = contextApi.Partners;
if (status != -1)
{
recs = recs.Where(i => i.Status == status);
}
recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName);
foreach (var rec in recs)
{
}
我必须明确我的输入而不是依赖var
。
答案 9 :(得分:0)
我喜欢扩展的想法
public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
=> condition ? query.Where(whereClause) : query;
答案 10 :(得分:0)
我使用下面的扩展方法。它不如其他答案中的 WhereIf 扩展灵活,但使用起来更短。
public static IQueryable<T1> FilterBy<T1, T2>(this IQueryable<T1> query, T2 expectedValue, Expression<Func<T1, T2>> propertyAccessor)
{
if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor));
if (expectedValue == null) return query;
var equalExpr = Expression.Equal(propertyAccessor.Body, Expression.Constant(expectedValue, typeof(T2)));
var lambda = Expression.Lambda<Func<T1, bool>>(equalExpr, propertyAccessor.Parameters);
return query.Where(lambda);
}
它可以像这样使用:
var query = dataContext.Staffs.FilterBy(name, s => s.Name);