实体框架Linq查询中的空安全导航

时间:2015-11-05 18:54:18

标签: .net entity-framework linq

客户有零个或一个语言关联。我想做以下

db.Customer.Select(x => new {
     x.Id, x.Name, 
     Language = new { x.Language?.Id, x.Language?.Name }
})

但是这甚至无法编译,因为linq表达式似乎不知道如何处理null safe get运算符(?.)。我该怎么做相同的?我希望生成的查询为LEFT OUTER JOIN LanguageLanguage密钥为null,或者对象为null。

1 个答案:

答案 0 :(得分:3)

只需使用

db.Customer.Select(x => new {
     x.Id, x.Name, 
     Language = new { (int?)x.Language.Id, x.Language.Name }
})

应该像这样生成SQL(当然表名和列名可能不同)

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Language_Id] AS [Language_Id], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Customers] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Languages] AS [Extent2] ON [Extent1].[Language_Id] = [Extent2].[Id]

这是类似问题的另一个example,请看一下生成的SQL。

更新但是,我们需要在投影值类型属性时包含T?强制转换,否则无法实现查询(ToList()失败, InvalidOperationException “附加信息:转换为值类型'System.Int32'失败,因为具体化值为null。结果类型的泛型参数或查询必须使用可空类型。”

Update2:另一种选择是使用此构造

db.Customer.Select(x => new {
     x.Id, x.Name, 
     Language = x.Language != null ? new { x.Language.Id, x.Language.Name } : null
})

SQL看起来像这样

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    CASE WHEN ([Extent2].[ID] IS NOT NULL) THEN 1 END AS [C1],
    [Extent1].[Language_Id] AS [Language_Id], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Customers] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Languages] AS [Extent2] ON [Extent1].[Language_Id] = [Extent2].[Id]

并且在具体化时,将生成 null Language成员(与先前的方法相比,该方法生成所有成员都设置为 null 的实例)