【问题标题】:Left join after a into group in Linq using entity framework (core)使用实体框架(核心)在 Linq 中进入组后左加入
【发布时间】:2016-05-31 08:33:29
【问题描述】:

问题:我想使用 linq 语法(实体框架 7)在所需的输出中生成下面的确切 sql

问题的目标是生成下面的准确sql! 所需的输出

select a.AppUserId, u.Email, a.FirstName, a.MiddleName, a.LastName, a.IsInternal, a.AspNetUserId, a.PictureLink, a.SignatureLink, a.PhoneNumber, a.Extension, a.FaxNumber, a.MobileNumber, a.Skype, r.Name as 'Role', a.SupervisorId, a.BackUpId, a.HasAutoAssignClaims, a.IsActive
from AppUser a
join AspNetUsers u on a.AspNetUserId = u.Id
left join AspNetUserRoles ur on u.Id = ur.UserId
left join AspNetRoles r on ur.RoleId = r.Id

我只能获得完全相同的 sql,但使用内部连接。我似乎无法获得两个左连接。下面的代码是我如何生成内部连接以及生成左连接的失败尝试。

SELECT [a].[AppUserId], [a].[FirstName], [a].[MiddleName], [a].[LastName], [a].[IsInternal], [a].[AspNetUserId], [a].[PictureLink], [a].[SignatureLink], [a].[PhoneNumber], [a].[Extension], [a].[FaxNumber], [a].[MobileNumber], [a].[Skype], [a].[SupervisorId], [a].[BackUpId], [a].[HasAutoAssignClaims], [a].[IsActive]
FROM [AppUser] AS [a]
INNER JOIN [AspNetUsers] AS [b] ON [a].[AspNetUserId] = [b].[Id]
INNER JOIN [AspNetUserRoles] AS [c] ON [b].[Id] = [c].[UserId]
INNER JOIN [AspNetRoles] AS [d] ON [a].[RoleId] = [d].[Id]

带有内连接的代码可以工作,但我想要左连接......:

 var query = (
                              //INNER JOIN 
                              from a in _dbCtx.AppUser
                              join b in _dbCtx.Users
                              on a.AspNetUserId equals b.Id 


                              ////LEFT JOIN
                              join c in _dbCtx.UserRoles
                              on b.Id equals c.UserId 

                              //    //    //LEFT JOIN  (if you wanted right join the easiest way is to flip the order of the tables. 
                              join d in _dbCtx.Roles
                              on a.RoleId equals d.Id





                          select new
                          {
                              AppUserId = a.AppUserId,
                              //Email = b.Email,
                              FirstName = a.FirstName,
                              MiddleName = a.MiddleName,
                              LastName = a.LastName,
                              IsInternal = a.IsInternal,
                              AspNetUserId = a.AspNetUserId,
                              PictureLink = a.PictureLink,
                              SignatureLink = a.SignatureLink,
                              PhoneNumber = a.PhoneNumber,
                              Extension = a.Extension,
                              FaxNumber = a.FaxNumber,
                              MobileNumber = a.MobileNumber,
                              Skype = a.Skype,
                              //Role = d.Name != null ? string.Empty :d.Name ,
                              SupervisorId = a.SupervisorId,
                              BackUpId = a.BackUpId,
                              HasAutoAssignClaims = a.HasAutoAssignClaims,
                              IsActive = a.IsActive

                          }).ToList();

带有左连接的代码...我缺少一些概念不起作用

不起作用的是,在 g2.RoleId 等于 d.Id 进入 group3 行时,g2 不可用。那么如何让 c.RoleId 可用于我的下一个左连接呢?基本上在你分组后你显然不能再使用它了。

        var LeftJoin= (
                          //INNER JOIN 
                          from a in _dbCtx.AppUser
                          join b in _dbCtx.Users
                          on a.AspNetUserId equals b.Id 


                          ////LEFT JOIN
                          join c in _dbCtx.UserRoles
                          on b.Id equals c.UserId into group2
                          from g2 in group2.DefaultIfEmpty() //makes it left join 



                          join d in _dbCtx.Roles
                          on g2.RoleId equals d.Id into group3
                          from g3 in group3.DefaultIfEmpty()

                          select new
                          {
                              AppUserId = a.AppUserId,
                              Email = b.Email,
                              FirstName = a.FirstName,
                              MiddleName = a.MiddleName,
                              LastName = a.LastName,
                              IsInternal = a.IsInternal,
                              AspNetUserId = a.AspNetUserId,
                              PictureLink = a.PictureLink,
                              SignatureLink = a.SignatureLink,
                              PhoneNumber = a.PhoneNumber,
                              Extension = a.Extension,
                              FaxNumber = a.FaxNumber,
                              MobileNumber = a.MobileNumber,
                              Skype = a.Skype,
                              Role = g3.Name != null ? string.Empty :g3.Name ,
                              SupervisorId = a.SupervisorId,
                              BackUpId = a.BackUpId,
                              HasAutoAssignClaims = a.HasAutoAssignClaims,
                              IsActive = a.IsActive

                          }).ToList();

【问题讨论】:

  • 你应该详细说明:some concept doesnt work
  • 我详细阐述了当我在 g2.RoleId 等于 d.Id 到 group3 中使用时,g2.RoleId 不再可用。总体目标是使用左连接以任何方法复制上面的查询。
  • 你说的不可用是什么意思?从句法上看,这似乎是一个正确的陈述。
  • 它给出了序列没有元素的异常。那是因为在你对它们进行分组之后,它就不能再被调用了。
  • 您的LeftJoin 查询是正确的。在 EF6 中,它生成的正是您所期望的(应该如此)。在 EF7(核心)中它不起作用,同样适用于许多其他有效查询,所以我想说 EF7 还没有准备好使用。

标签: entity-framework linq


【解决方案1】:

这是一种使用非常糟糕的查询生成数据的变通方法。它生成一系列对确实产生相同结果集的数据库的调用。但是,这绝对不是这项工作的最佳查询。我还在等待RC2,我会更新答案。

 var query = (
                           //INNER JOIN 
                           from a in _dbCtx.AppUser
                           join b in _dbCtx.Users
                           on a.AspNetUserId equals b.Id

                           from c in _dbCtx.UserRoles
                           .Where(x => b!=null && x.UserId == b.Id)
                           .DefaultIfEmpty()

                           from d in _dbCtx.Roles
                             .Where(x => a !=null &&  x.Id == a.RoleId)
                             .DefaultIfEmpty()


                           select new
                           {
                               AppUserId = a.AppUserId,
                              Email = b.Email,
                              FirstName = a.FirstName,
                               MiddleName = a.MiddleName,
                               LastName = a.LastName,
                               IsInternal = a.IsInternal,
                               AspNetUserId = a.AspNetUserId,
                               PictureLink = a.PictureLink,
                               SignatureLink = a.SignatureLink,
                               PhoneNumber = a.PhoneNumber,
                               Extension = a.Extension,
                               FaxNumber = a.FaxNumber,
                               MobileNumber = a.MobileNumber,
                               Skype = a.Skype,
                               Role = d.Name != null ? string.Empty :d.Name ,
                               SupervisorId = a.SupervisorId,
                               BackUpId = a.BackUpId,
                               HasAutoAssignClaims = a.HasAutoAssignClaims,
                               IsActive = a.IsActive

                           }).ToList();

【讨论】:

    【解决方案2】:

    如果我理解正确,您的问题是 EF 没有生成 LEft 连接。如果是,那么解决方案非常简单,例如您的实体应该具有可为空的属性

     public class SomeClass
        {
            public int Id { get; set; }
            public int? CategoryId { get; set; }
            public Category Category {get;set:}
        }
    

    我脑海中的一个选项

    _dbCtx.SqlQuery<T>(SqlStringHEre).ToList()
    

    其他选项和aspnet表我会做不同的

    var query = _dbCtx.AppUser
    .Include(apu=>apu.AspNetUser)
    .Include(apu=>apu.AspNetUser.Roles)
    .Include(apu=>apu.AspNetUser.Roles.Select(r=>r.Role))
    .ToList();
    

    但是当我们在 cmets 中讨论 IdentityUserRole 没有对角色的参考时,这是一个问题,所以让我们解决这个问题。 创建类

     public class UserToRole : IdentityUserRole<int>
        {
            public Role Role { get; set; }
        }
    

    然后扩展你的用户类

     public class YourUser : IdentityUser<int, IdentityUserLogin<int>, UserToRole, IdentityUserClaim<int>>
    

    现在你可以做你想做的事了

    _db.Users.Select(u=>u.Roles.Select(r=>r.Role.Name))
    

    【讨论】:

    • 不幸的是,生成的 sql 不等同于我上面的查询。其中也不包含查询中非常重要的角色名称。它也不是最好的sql。记住我想要的是生成我在上面的问题中提出的确切 sql。 SELECT [a].[UserId], [a].[RoleId] FROM [AspNetUserRoles] AS [a] INNER JOIN (SELECT DISTINCT * FROM [AppUser] AS [apu] INNER JOIN [AspNetUsers] AS [a] ON [apu ].[AspNetUserId] = [a].[Id] INNER JOIN [AspNetUsers] AS [a0] ON [apu].[AspNetUserId] = [a0].[Id] ORDER BY [a].[Id]
    • @hidden 你能解释一下为什么你需要完全相同的查询吗?
    • 因为我不敢相信不可能生成一个简单的左连接...我得到了内连接等价物,为什么做一个简单的左连接这么难。
    • 如果你需要那个确切的 sql,为什么不把那个 sql 放在一个存储过程中,然后从实体框架中调用呢?
    • @hidden 你是什么意思没有角色属性 _context .Users.Include(u=>u.Roles) 我刚刚在我的项目中尝试过它可以工作
    【解决方案3】:

    万一有人提出这个问题,认为它没有得到回答,答案目前隐藏在问题之后的评论链中。我只是在这里解释关键的 cmets。

    问题是 Entity Framework 7 目前是 release candidate 并且有一些错误。其中一个错误 (Left Join doesn't work if the filter is composed on top) 导致 OP 指出的左连接失败。

    目前的解决方案是恢复到 Entity Framework 6,或者暂时使用存储过程或内联 SQL,直到错误得到修复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-18
      • 2020-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-08
      • 2020-04-03
      相关资源
      最近更新 更多