C# LINQ:仅当值不为null时添加where子句

C# LINQ:仅当值不为null时添加where子句,c#,asp.net,linq,performance,sql-server-2008,C#,Asp.net,Linq,Performance,Sql Server 2008,我知道一个典型的方式是这样的: 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 ==

我知道一个典型的方式是这样的:

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);
IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
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)
{
}
然而,从我们从其他开发人员手中接手的一个程序中,我们看到了如下代码:

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);
IQueryable<Staffs> query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1);
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)
{
}
如果这是一个普通的SQL语句,我肯定会说第二个是一个糟糕的做法。因为当name1为null时,它会在查询中添加一个无意义的where子句


但我对LINQ是新手,所以我不确定LINQ是否与众不同

通常,使用流畅的语法(而不是查询语法)编写此类内容会让人感觉更流畅

e、 g

因此,如果
name1
为空,则不执行任何
Where()
调用。如果您有多个不同的过滤器,所有这些过滤器可能都是必需的,也可能不是必需的,还有各种不同的排序顺序,我发现这将变得更易于管理

为alex编辑:好的,我刚才回答了一个问题,即仅当值不为null时才添加where子句。在回答问题的另一部分时,我使用EntityFramework4进行了尝试,以查看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
为null的查询将导致根本没有
WHERE
子句,正如您所希望的那样;用
name1
作为“a”进行尝试,结果得到了一个简单的
,其中[t0].[name]=@p0
@p0
作为“a”发送。实体框架并没有这样优化。这有点令人担忧。

LINQ在其他一些原因上有所不同(不是在这个原因上), LINQ是一种以“更快的方式”获取数据的方法,它具有少量代码和尽可能清晰的cod,LINQ有许多好处:

  • 使将数据转换为对象变得更容易。我相信您已经听说过“阻抗不匹配”这个术语被经常使用,这意味着LINQ减少了在面向对象代码和数据范例(如分层、平面文件、消息、关系等)之间进行转换所需的工作量。它不能消除“阻抗不匹配”,因为您仍然必须以数据的本机形式对数据进行推理,但从这里到那里的桥梁(IMO)要短得多

  • 所有数据的通用语法。一旦学习了查询语法,就可以将其用于任何LINQ提供程序。我认为这是一个比巴别塔更好的开发模式,巴别塔是随着数据访问技术的发展而发展起来的。当然,每个LINQ提供程序都有其独特的细微差别,但基本方法和查询语法是相同的

  • 强类型代码。C#(或VB.NET)查询语法是语言的一部分,您可以使用C#类型进行编码,这些类型被翻译成提供者能够理解的内容。这意味着您获得了让编译器在开发生命周期中比其他地方更早地发现错误的效率。诚然,存储过程语法中的许多错误在保存时都会产生错误,但LINQ比SQL Server更通用。您必须考虑生成运行时错误的所有其他类型的数据源,因为它们的查询是由字符串或其他松散类型的机制构成的

  • 提供者集成。整合数据源非常容易。例如,对于一些非常复杂的场景,可以同时使用LINQ到对象、LINQ到SQL和LINQ到XML。我觉得它很优雅

  • 减少工作量。在LINQ之前,我花了很多时间构建DAL,但现在我的DataContext就是DAL。我也使用过OPFs,但现在我有了LINQ,它附带了多个机箱中的提供商和许多其他第三方提供商,让我从前面的几点中受益。我可以在一分钟内设置一个LINQ到SQL的DataContext(以我的计算机和IDE能够跟上的速度)

  • 在一般情况下,性能不会成为问题。SQL Server最近很好地优化了查询,就像存储过程一样。当然,仍有一些情况下出于性能原因需要存储进程。例如,我发现当我在一个事务中有多个具有附加逻辑的表之间进行交互时,使用存储过程更明智。除了在分布式事务中获取DTC之外,尝试在代码中执行相同任务的通信开销使得存储过程的选择更具吸引力。但是,对于在单个语句中执行的查询,LINQ是我的首选,因为即使存储过程的性能有一点提高,以前的点(IMO)带来的好处也更重要

  • 内置安全性。我喜欢LINQ之前的存储过程的一个原因是它们强制使用参数,有助于减少SQL注入攻击。LINQtoSQL已经参数化了输入,这同样安全

  • LINQ是声明性的。人们非常注意使用LINQtoXML或LINQtoSQL,但LINQtoObjects功能强大得令人难以置信。LINQ to对象的一个典型示例是从字符串[]读取项。然而,这只是一个小例子。如果您考虑一下您每天处理的所有IEnumerable集合(您也可以查询IEnumerable),那么机会就很多了。i、 e.在ASP.NET ListBox控件中搜索选定项,对两个集合执行集合操作(如Union),或遍历列表并在每个项的ForEach中运行lambda。一旦您开始使用LINQ(本质上是声明性的)进行思考,您就会发现您的许多任务比您现在使用的命令式技术更简单、更直观

  • 我也许可以继续,但我最好还是继续
    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);
    
    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);
    
    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)
    {
    }
    
    public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
            => condition ? query.Where(whereClause) : query;