Linq to Entity 经常有人问能不能实现对Include的内容进行where过滤的Left join,返回的结果集是同时满足主表和子表过滤条件的数据。 答案是可以,但必须使用Select匿名对象,有时候我们希望我们的导航属性里只返回过滤过的对象。
麻烦但却可以实现,不过只推荐使用在取数据的情况下,数据获取完Context最好销毁以免引起混淆。原因是导航属性其实只是用来获取所有关联数据,代表的是一个关系,在Delay Load模式下更是如此。我们返回的结果不应该用导航属性来存储,
不过人总是懒的,所有这样用也有道理。
举个例子:一个主表Patron 与 从表 PatronIdentification
现在我要取出满足PatronName = A 的Patron 以及所有这些Patron相关的IdentificationNumber = aaaa 的 PatronIdentification。返回的结果集必须是一个List<Patron>
可以这样写:
using (BE50Beta_AdminEntities1 context = new BE50Beta_AdminEntities1()) { Listlist = new List (); context.Patron.Include(p => p.PatronIdentification).Where(p=>p.PatronName=="A").Select(p => new { Patron = p, PatronIdentification = p.PatronIdentification.Where(k => k.IdentificatinNumber == "aaaa") }).ToList().ForEach( p=>{p.Patron.PatronIdentification = p.PatronIdentification.ToList(); resultPatron.Add(p.Patron);} ); }
这样返回的结果集就是所有PatronName= A 的 Patron 及 IdentificationNumber = aaaa的PatronIdentification 记录
但是这时得到的是个匿名类型对象,我们实在不愿意再花时间定于一个新类去解析,我们想把相关的PatronIdentification List 放到该Patron的导航属性下,于是我们在ForEach里对Patron导航属性重新赋了值,并最终将结果集加入到一个符合返回类型的List中
不要天真的以为不用ForEach时重新对导航属性赋值,虽然这个时候,匿名对象中的Patron就是Patron类型,且PatronIdentification导航属性已经有只值了,也许就是你过滤出来的结果。但千万记住他们是不一样的。
导航属性不仅加载你本次搜索出来的结果,而且还加载已经存在于Context中的结果。所以假如在查询这个从表前你已经一次性查询过整个从表了,那么恭喜你这个导航属性将永远是所有集而不是你想过滤得到的结果。
最后,由Sql Profile可得生成的脚本如下:
SELECT [Project1].[PatronID] AS [PatronID], [Project1].[PatronNumber] AS [PatronNumber], [Project1].[PatronName] AS [PatronName], [Project1].[IsActive] AS [IsActive], [Project1].[C1] AS [C1], [Project1].[PatronIdentificatinID] AS [PatronIdentificatinID], [Project1].[PatronID1] AS [PatronID1], [Project1].[CountryID] AS [CountryID], [Project1].[IdentificatinNumber] AS [IdentificatinNumber]FROM ( SELECT [Extent1].[PatronID] AS [PatronID], [Extent1].[PatronNumber] AS [PatronNumber], [Extent1].[PatronName] AS [PatronName], [Extent1].[IsActive] AS [IsActive], [Extent2].[PatronIdentificatinID] AS [PatronIdentificatinID], [Extent2].[PatronID] AS [PatronID1], [Extent2].[CountryID] AS [CountryID], [Extent2].[IdentificatinNumber] AS [IdentificatinNumber], CASE WHEN ([Extent2].[PatronIdentificatinID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] FROM [dbo].[Patron] AS [Extent1] LEFT OUTER JOIN [dbo].[PatronIdentification] AS [Extent2] ON ([Extent1].[PatronID] = [Extent2].[PatronID]) AND ('aaaa' = [Extent2].[IdentificatinNumber]) WHERE 'A' = [Extent1].[PatronName]) AS [Project1]ORDER BY [Project1].[PatronID] ASC, [Project1].[C1] ASC
可见,脚本正常,不会影响性能