LINQ LEFT JOIN on Nullable<int>

LINQ LEFT JOIN on Nullable<int>

下面有两个表格:

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

我正在使用 Entity Framework 数据库优先方法。所以在我的模型中:

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
        }

但是,我收到一个错误:

The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.

我已经在 int? and int comparison when LEFT OUTER JOIN in Linq issue 上尝试了两种解决方案,但它仍然给我这些错误:

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

LINQ to Entities does not recognize the method 'Int32 ToInt32(Int32)' method, and this method cannot be translated into a store expression.

使用解决方案GetValueOrDefault():

LINQ to Entities does not recognize the method 'Int32 GetValueOrDefault(Int32)' method, and this method cannot be translated into a store expression.

LEFT JOINNullable<int>int 上表现如何?

如果 join 中的任何列属于 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

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

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

编辑:为 EF 数据库优先更新了以下 POCO(即使用 partial classes)。

// 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 class 时,请记住在生成的版本上添加 partial 关键字,但这仍然更好,因为 -通过将添加内容保存在单独的 partial class 声明中 — partial 关键字是 only 您必须记住要更改的内容生成 class.

所以您的查询很简单:

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]

这似乎正是您想要的。

当我必须在可为空的字段上执行联接时,这就是我所做的

原始 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 转换为可为 null 的 int,将解决您的问题。