【问题标题】:Why would this LINQ to SQL query break when I do ToList()?当我执行 ToList() 时,为什么这个 LINQ to SQL 查询会中断?
【发布时间】:2011-05-19 20:04:24
【问题描述】:

最初我是用这样的代码合并两组结果:

var list1 = from a in IDataSourceObject
            blahblah
            select a;

var list2 = from a in IDataSourceObject2
            blahblah
            select a;    

var joinedlist = from a in list1
                 join b in list2 on a.id = b.id
                 into fishcakes
                 from b in fishcakes.DefaultIfEmpty()
                 orderby b.ranking
                 select new { blah=cakes, etc. }

这曾经可以正常工作,但后来我想多过滤一下列表 1,所以我这样做了:

var list1 = from a in IDataSourceObject
            blahblah
            select a;

// ToList required because im calling a method in my code
var updatedList1 = from a in list1.ToList()
                   where myMethod(somestuff) == true
                   select a;   

var list2 = from a in IDataSourceObject2
            blahblah
            select a;    

var joinedlist = from a in updatedList1
                 join b in list2 on a.id = b.id
                 into fishcakes
                 from b in fishcakes.DefaultIfEmpty()
                 orderby b.ranking
                 select new { blah=cakes, etc. }

但是我收到一个错误,基本上说 OrderBy b.ranking 为空。完成 ToList 后不再合并结果。我检查了 updatedList1 并让 myMethod 始终返回 true,所以问题本质上来自于使用 ToList()。

我知道这可能与延迟执行有关,但我不知道该怎么做。应该是完全一样的。

有人有什么建议吗?

【问题讨论】:

  • 尝试找到解决方案,牢记这一点,您确实加入了与遥远的内存集合!

标签: c# linq-to-sql


【解决方案1】:

调用fishcakes.DefaultIfEmpty() 可以返回包含null 的集合。

如果您调用.ToList(),所有当前结果都将复制到本地(.Net)对象,并且.ToList() 之后的所有命令都将在您的程序中执行。

如果您对 .Net 集合执行查询,然后尝试调用 null.ranking - 会抛出 NullReferenceException。同时在 SQL Server 上执行不会引发异常,因为在 SQL 中可以请求null 的子属性(它只会返回null)。

为了防止您的示例中出现异常:您可以过滤 ranking 等于 null 的项目,或者替换您的

orderby b.ranking 

到这样的事情(我假设 ranking 是 int)

orderby b != null ? b.ranking : -1


实现值也是如此。例如(假设Item 可能有Category,也可能没有):

// this will work, because it's executed on SQL-side
db.Items
      .Select(x=>new { CatId = (int?)x.Category.Id, x.Id})
      .ToList();

// this will throw NullRefException, because it's executed against collection in .Net environment, not on SQL Server.
db.Items
      .ToList()
      .Select(x=>new { CatId = (int?)x.Category.Id, x.Id}); 

PS:如果您使用 Resharper,它会在第一个示例中抱怨,不需要转换为 int?。不要相信! )

【讨论】:

    【解决方案2】:

    因为 ToList() 返回 IEnumerable,它不是 IQueryable

    澄清:

    使用 Linq to SQL 您隐含地使用了 IQueryable,因此这些选择、连接和排序被转换为 SQL 并在数据库服务器上执行。但是,将 updatedList1 转换为 List 会阻止 linq2sql 将整个语句转换为 SQL 查询,并且它的语句正在被一一执行,就像在普通 Linq 中一样。它不仅可能会引入一些错误(如上面答案中提到的 NullReferenceException),而且它的效率也远低于“纯”linq2sql 表达式。

    【讨论】:

    • 他尝试使用哪个 IQueryable 操作?我忘记了from ... where ... select 是如何转换为 LINQ 方法调用的。您当然可以在List<> 上致电.Where()
    • @Robert Harvey 是的。我在回答中添加了一些说明。
    【解决方案3】:

    join into 类似于 SQL 中的左内连接。因此,对于某些 a,fishcakes 可能是空的,因此对于某些 afishcakes.DefaultIfEmtpy() 可能会返回 null 试试

    var joinedlist = from a in updatedList1
                     join b in list2 on a.id = b.id
                     into fishcakes
                     from b in fishcakes
                     where b != null
                     orderby b.ranking
                     select new { blah=cakes, etc. }
    

    【讨论】:

      【解决方案4】:

      因为您试图加入 2 种不同(不兼容)的类型。如果你拿 list2 并对其执行类似的.ToList() 操作,这应该可以缓解症状。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多