【问题标题】:LINQ LEFT JOIN on Nullable<int>Nullable<int> 上的 LINQ LEFT JOIN
【发布时间】:2015-05-10 06:43:41
【问题描述】:

我在下面有两张表:

Project(
    ProjectID       INT,
    Name            VARCHAR(200),
    AssignedUserID  INT NULL
)
User(
    UserID      INT,
    LastName    VARCHAR(50),
    FirstName   VARCHAR(50)
)

我正在使用实体框架数据库优先方法。所以在我的模型中:

Class Project{
    public int ProjectID;
    public string Name;
    public Nullable<int> AssignedUserID;
}

Class User{
    public int UserID;
    public string LastName;
    public string FirstName;
}

我要查询所有PROJECT及其分配的用户:

SELECT
    p.ProjectID,
    p.Name,
    u.LastName,
    u.FirstName
FROM Project p
LEFT JOIN Users u ON u.UserID = p.AssignedUserID

翻译成 Linq:

var projectDetails =
    from p in context.Project
    join u in context.User
        on p.AssignedUserID equals u.UserID into lj
    from x in lj.DefaultIfEmpty()
        select new {
            ProjectID = p.ProjectID,
            ProjectName = p.Name,
            UserLastName = u.LastName,
            UserFirstName = u.FirstName
        }

但是,我收到一个错误:

连接子句中的一个表达式的类型不正确。 调用“加入”时类型推断失败。

我在 int? and int comparison when LEFT OUTER JOIN in Linq issue 上尝试了这两种解决方案,但仍然出现以下错误:

使用解决方案:(int)(p.AssignedUserID ?? default(int))

LINQ to Entities 无法识别方法“Int32 ToInt32(Int32)” 方法,并且该方法不能翻译成商店表达式。

使用解决方案GetValueOrDefault()

LINQ to Entities 无法识别方法 'Int32 GetValueOrDefault(Int32)' 方法,该方法不能翻译 到商店表达式中。

LEFT JOINNullable&lt;int&gt;int 上的表现如何?

【问题讨论】:

    标签: c# linq entity-framework join


    【解决方案1】:

    当我必须对一个可为空的字段进行联接时,我会这样做

    原始 Linq:

    var projectDetails =
        from p in context.Project
        join u in context.User
            on p.AssignedUserID equals u.UserID into lj
        from x in lj.DefaultIfEmpty()
            select new {
                ProjectID = p.ProjectID,
                ProjectName = p.Name,
                UserLastName = u.LastName,
                UserFirstName = u.FirstName
            }
    

    修改后的 Linq:

    var projectDetails =
        from p in context.Project
        join u in context.User
            on new {User = p.AssignedUserID} equals new {User = (int?)u.UserID} into lj
        from x in lj.DefaultIfEmpty()
            select new {
                ProjectID = p.ProjectID,
                ProjectName = p.Name,
                UserLastName = x.LastName,
                UserFirstName = x.FirstName
            }
    

    问题是您试图将 int 与 int? 连接起来,这会给出错误消息,将 UserId 的 int 转换为可为空的 int,将解决您的问题。

    【讨论】:

    • 仍然出现此错误:LINQ to Entities does not recognize the method 'Int32 ToInt32(Int32)' method, and this method cannot be translated into a store expression.
    • 你能显示所有代码吗,因为似乎还有别的事情发生
    • 很抱歉,您的回答有效。在导致错误的select 部分上有一些对Convert.Int32 的调用。感谢您的宝贵时间。
    • 在选择中可以使用 prop = (x.Field.HasValue) ? x.Field.Value : [null 的默认值]
    • 在 EF Core 2 中,此方法会导致客户端评估。
    【解决方案2】:

    如果连接中的任何列是Nullable 类型,我们需要使用.Value 获取其实际值,如下所示:-

        join u in context.User
            on p.AssignedUserID.Value equals u.UserID into lj
    

    或者你也可以这样做:-

    join u in context.User
                on new { p.AssignedUserID } equals new { AssignedUserID = u.UserID } into lj
    

    【讨论】:

    • 你需要有new {AssignedUserID = p.AssignedUserID } equals new { AssignedUserID = u.UserID }
    • @JenishRabadiya - 不,这不是强制性的。两者都会推断出与AssignedUserID 相同的名称
    • @RahulSingh:仍然出现同样的错误:The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'。它甚至不允许我编译。
    • @RahulSingh 我在stackover flow线程上读到它是强制性的。可能是我弄错了。这是这样的线程stackoverflow.com/questions/19184019/…
    • 很抱歉,您的回答有效。在导致错误的选择部分上有一些对 Convert.Int32 的调用。谢谢你的时间。 +1
    【解决方案3】:

    你实际上做了很多 EF 应该为你做的工作(这种情况经常发生,尤其是 join)。

    您需要在 Project 类上使用导航属性,而不仅仅是您现在拥有的 FK:

    编辑:为 EF Database-First 更新了以下 POCO(即使用 partial 类)。

    // generated code
    public partial class Project
    {
        public int ProjectId { get; set; }
        public string Name { get; set; }
        public int? AssignedUserId { get; set; }
    }
    
    public class User
    {
        public int UserId { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
    }
    
    // your code
    public partial class Project
    {
        [ForeignKey("AssignedUserId")]
        public User User { get; set; }
    }
    

    请注意,每次重新生成 Project 类的生成版本时,您必须记住添加 partial 关键字,但这仍然更好,因为 — 将添加的内容保存在单独的 partial 中类声明——partial 关键字是唯一你必须记住要在生成的类中更改的东西。

    所以您的查询如下:

    var projectDetails = from p in context.Projects
                         select new
                         {
                             p.ProjectId,
                             p.Name,
                             p.User.LastName,
                             p.User.FirstName
                         };
    

    让我们看看它是什么样子的:

    Console.WriteLine(projectDetails.ToString());
    

    这会产生以下 SQL:

    SELECT
        [Extent1].[ProjectId] AS [ProjectId],
        [Extent1].[Name] AS [Name],
        [Extent2].[LastName] AS [LastName],
        [Extent2].[FirstName] AS [FirstName]
        FROM [Projects] AS [Extent1]
        LEFT OUTER JOIN [Users] AS [Extent2] ON [Extent1].[AssignedUserId] = [Extent2].[UserId]
    

    这似乎正是您想要的。

    【讨论】:

    • 很好的答案 +1,但表格没有明确的 FK 关系。不加FK还有其他方法吗?
    • 我不明白你的意思?您已经将AssignedUserId 作为属性,见鬼,作为基础架构中的字段......那不是外键吗?我所做的只是添加一个导航属性,这是使用实体框架的全部意义......没有它,我不知道为什么你不会只使用 LINQ-to-SQL。
    • 不,它是一个 FK,没有声明为一个。
    • ForeignKeyAttribute 的使用并没有使其成为架构中的一员……它只是告诉 EF UserAssignedUserId 的关系。我从你的原始模型开始,用它来创建数据库,添加了我的导航属性User,它在没有修改的情况下针对同一个数据库运行。
    • 哦,所以我只需要在EF/生成的类中添加ForeignKeyAttribute即可
    猜你喜欢
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多