添加 时 LINQ 'OR'条件的问题。最后的ToList(或ToArray)



通过 DataContext 使用数据库。

有这样的操作:

var flat = StavRealtyDb.TargetFlat
    .Where(x => true || _some condition_).ToList();

所以,如果条件的第一部分为,编译器应该跳过第二部分,对吧?但它不会发生。如果有的话,也会发生同样的问题。ToArray((。

但是,如果它不会像."要列出"或"要列出"。最后 ToArray - 编译器将跳过条件的第二部分。

问题出在哪里?正常吗?:)

更新

好的,谢谢你的回答!我明白这很正常。但问题是条件的第一部分包含:someobject == null;第二部分包含:某个对象。包含((。

.Where(x => someobject == null || someobject.Field.Contains(x.somefield))

所以我有 ArgumentNullException 当某个对象 == null 时(我希望第一部分将返回 true,而第二部分不会执行(。你能告诉我,我该如何解决这个问题吗?

附言:实际上,.其中((表达式更复杂:

.Where(x=> (part1 || part 2) && (part1 || part2) && ......)

你正在IQueryableWhere,论点是一个Expression而不是一个Func

http://msdn.microsoft.com/en-us/library/vstudio/bb535040%28v=vs.100%29.aspx

Expression只是传递给基础Where实现。

我发现这个主题很有趣,所以我创建了一个示例。假设我们有以下代码:

using System;
using System.Linq.Expressions;
namespace TestApplication
{
    class CompilerTest
    {
        public void Test()
        {
            Func<bool> func = () => true || Foo();
            Expression<Func<bool>> expr = () => true || Foo();
        }
        public static bool Foo()
        {
            return new Random().Next() % 2 == 0;
        }
    }
}

我们构建它(发布版本(,反编译的代码如下所示:

using System;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace TestApplication
{
  internal class CompilerTest
  {
    [CompilerGenerated]
    private static Func<bool> CSu0024u003Cu003E9__CachedAnonymousMethodDelegate1;
    public CompilerTest()
    {
      base.u002Ector();
    }
    public void Test()
    {
      if (CompilerTest.CSu0024u003Cu003E9__CachedAnonymousMethodDelegate1 == null)
      {
        // ISSUE: method pointer
        CompilerTest.CSu0024u003Cu003E9__CachedAnonymousMethodDelegate1 = new Func<bool>((object) null, __methodptr(u003CTestu003Eb__0));
      }
      Func<bool> func = CompilerTest.CSu0024u003Cu003E9__CachedAnonymousMethodDelegate1;
      (Expression<Func<bool>>) (() => true || CompilerTest.Foo());
    }
    public static bool Foo()
    {
      return new Random().Next() % 2 == 0;
    }
    [CompilerGenerated]
    private static bool u003CTestu003Eb__0()
    {
      return true;
    }
  }
}

如果我们隐藏编译器生成的代码(dotPeek选项(,它看起来像这样:

using System;
using System.Linq.Expressions;
namespace TestApplication
{
  internal class CompilerTest
  {
    public void Test()
    {
      Func<bool> func = (Func<bool>) (() => true);
      (Expression<Func<bool>>) (() => true || CompilerTest.Foo());
    }
    public static bool Foo()
    {
      return new Random().Next() % 2 == 0;
    }
  }
}

正如我们所看到的,lambda 中指定的 Func 代码得到了优化(如果编译器对其进行优化,这才是真正的问题(。表达式中指定的代码显然没有优化(因为它是一个表达式,所以从编译器的角度来看,此时没有什么可以优化的(。

到目前为止,答案涉及一些起作用但不能涵盖全局的问题。此外,第二个条件是Contains语句,这是基本信息。所以假设核心陈述是

StavRealtyDb.TargetFlat.Where(x => true || someList.Contains(x.Id))

如前所述,整个LINQ语句是一个Expression,因此编译器不应用任何优化。所以如果一切顺利——

  1. .Net 运行时成功创建表达式,
  2. 将其传递给 EF 查询提供程序,
  3. 查询提供程序将其转换为 SQL,
  4. ADO.Net 运行 SQL,
  5. 数据库执行它,并将结果集返回到 .Net 运行时,
  6. .Net 运行时从结果集创建对象,并将其返回到 var flat 中。

在此事件过程中,第二个谓词始终由 .Net 运行时读取,而不管它前面有多少个谓词,但如果查询优化器找到这样做的理由,则由数据库进行评估

现在有些不对劲:someList null.现在整个过程在第 3 步停止。查询提供程序尝试读取someList(以便将其转换为 IN 子句(。但是,当然,这会因空引用而失败。

因此,您必须确保someList永远不会为空(它可能是一个空列表(,或者您可以编写Where条件:仅当someList不为 null 时,您才包含Contains谓词。

我认为这是Linq Providers的正常和设计使然。 在这种情况下,Linq 提供程序旨在获取 Where 表达式并将其转换为 Sql 并在 Sql 服务器上执行。

Where()将返回一个IQueryable<>,您可以将其视为准备执行的查询。但是,它仅内置Where()中,并且将在需要实际数据时执行 - 在这种情况下,在调用ToList()时。查询将保存OR,因此它没有短路是完全正常的行为,因为它根本没有在Where()内执行。

如果您想更好地理解它,请尝试将其拆分并在Where()放置断点。

var query = StavRealtyDb.TargetFlat.Where(x => true || _some condition_);
var actualData = query.ToList();

调试时将鼠标悬停在query上时,您会注意到它是一个实际的查询,例如

SELECT `Extent1`....

调用 ToList() 后,您会注意到数据是从数据库中检索并放入List

最新更新