当我执行ToList()时,为什么这个LINQ to SQL查询会中断?

时间:2010-12-03 15:55:34

标签: c# linq-to-sql

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

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为null。在执行ToList之后,它不再合并结果。我已经检查了updatedList1,并且我使myMethod总是返回true,所以基本上问题来自于使用ToList()。

我知道它可能与延迟执行有关,但我没有最模糊的想法。它应该完全一样。

有人有任何建议吗?

4 个答案:

答案 0 :(得分:6)

调用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?。不要相信! )

答案 1 :(得分:2)

join into类似于SQL中的左内连接。因此,对于某些a,fishcakes可能为空,因此fishcakes.DefaultIfEmtpy()可能会为某些a返回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. }

答案 2 :(得分:0)

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

澄清:

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

答案 3 :(得分:0)

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