C# Entity Framework Core 5.0如何将LINQ转换为多对多连接以使用ASP.NET成员身份的交集表
问题: 如何将LINQ查询转换为对内部联接两个表并具有谓词的子选择执行左外部联接 上下文: 我正在使用C# Entity Framework Core 5.0如何将LINQ转换为多对多连接以使用ASP.NET成员身份的交集表,c#,sql-server,linq,entity-framework-core,entity-framework-6,C#,Sql Server,Linq,Entity Framework Core,Entity Framework 6,问题: 如何将LINQ查询转换为对内部联接两个表并具有谓词的子选择执行左外部联接 上下文: 我正在使用EFCore工具反向工程功能从entityframework6(EF6)升级到entityframeworkcore 5(EFCore)。我有一个查询,它使用LINQ查询系统上AspNet\u用户和AspNet\u角色表之间的多对多关系。查询通过AspNet\u UsersInRoles交集表抽象出连接。因此,在EF6中,我有以下LINQ: (from r in this.DbContext.a
EFCore工具
反向工程功能从entityframework6(EF6)
升级到entityframeworkcore 5(EFCore)
。我有一个查询,它使用LINQ查询系统上AspNet\u用户
和AspNet\u角色
表之间的多对多关系。查询通过AspNet\u UsersInRoles
交集表抽象出连接。因此,在EF6中,我有以下LINQ:
(from r in this.DbContext.aspnet_Users
where r.UserId == dpass.UserId
select r.aspnet_Roles.Select(x => x.RoleName)
).FirstOrDefault();
生成以下SQL查询(使用SQL Server探查器检索):
这将生成以下SQL查询(使用EFCore的新.ToQueryString()
方法检索):
aspnet\u角色
[Index(nameof(RoleId), Name = "aspnet_UsersInRoles_index")]
public partial class aspnet_UsersInRoles
{
[Key]
public Guid UserId { get; set; }
[Key]
public Guid RoleId { get; set; }
[ForeignKey(nameof(RoleId))]
[InverseProperty(nameof(aspnet_Roles.aspnet_UsersInRoles))]
public virtual aspnet_Roles Role { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty(nameof(aspnet_Users.aspnet_UsersInRoles))]
public virtual aspnet_Users User { get; set; }
}
public partial class aspnet_Roles
{
public aspnet_Roles()
{
aspnet_UsersInRoles = new HashSet<aspnet_UsersInRoles>();
}
public Guid ApplicationId { get; set; }
[Key]
public Guid RoleId { get; set; }
[Required]
[StringLength(256)]
public string RoleName { get; set; }
[Required]
[StringLength(256)]
public string LoweredRoleName { get; set; }
[StringLength(256)]
public string Description { get; set; }
[ForeignKey(nameof(ApplicationId))]
[InverseProperty(nameof(aspnet_Applications.aspnet_Roles))]
public virtual aspnet_Applications Application { get; set; }
[InverseProperty("Role")]
public virtual ICollection<aspnet_UsersInRoles> aspnet_UsersInRoles { get; set; }
}
[Index(nameof(ApplicationId), nameof(LastActivityDate), Name = "aspnet_Users_Index2")]
public partial class aspnet_Users
{
public aspnet_Users()
{
Users = new HashSet<Users>();
aspnet_PersonalizationPerUser = new HashSet<aspnet_PersonalizationPerUser>();
aspnet_UsersInRoles = new HashSet<aspnet_UsersInRoles>();
}
public Guid ApplicationId { get; set; }
[Key]
public Guid UserId { get; set; }
[Required]
[StringLength(256)]
public string UserName { get; set; }
[Required]
[StringLength(256)]
public string LoweredUserName { get; set; }
[StringLength(16)]
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
[Column(TypeName = "datetime")]
public DateTime LastActivityDate { get; set; }
[ForeignKey(nameof(ApplicationId))]
[InverseProperty(nameof(aspnet_Applications.aspnet_Users))]
public virtual aspnet_Applications Application { get; set; }
[InverseProperty("User")]
public virtual aspnet_Membership aspnet_Membership { get; set; }
[InverseProperty("User")]
public virtual aspnet_Profile aspnet_Profile { get; set; }
[InverseProperty("aspUser")]
public virtual ICollection<Users> Users { get; set; }
[InverseProperty("User")]
public virtual ICollection<aspnet_PersonalizationPerUser> aspnet_PersonalizationPerUser { get; set; }
[InverseProperty("User")]
public virtual ICollection<aspnet_UsersInRoles> aspnet_UsersInRoles { get; set; }
}
公共部分类aspnet\u角色
{
公共aspnet_角色()
{
aspnet_UsersInRoles=new HashSet();
}
公共Guid应用程序ID{get;set;}
[关键]
公共Guid RoleId{get;set;}
[必需]
[StringLength(256)]
公共字符串RoleName{get;set;}
[必需]
[StringLength(256)]
公共字符串LowereRoleName{get;set;}
[StringLength(256)]
公共字符串说明{get;set;}
[ForeignKey(name of(ApplicationId))]
[InverseProperty(名称(aspnet_Applications.aspnet_Roles))]
公共虚拟aspnet_应用程序应用程序{get;set;}
[反向属性(“角色”)]
公共虚拟ICollection aspnet_UsersInRoles{get;set;}
}
aspnet\u用户
[Index(nameof(RoleId), Name = "aspnet_UsersInRoles_index")]
public partial class aspnet_UsersInRoles
{
[Key]
public Guid UserId { get; set; }
[Key]
public Guid RoleId { get; set; }
[ForeignKey(nameof(RoleId))]
[InverseProperty(nameof(aspnet_Roles.aspnet_UsersInRoles))]
public virtual aspnet_Roles Role { get; set; }
[ForeignKey(nameof(UserId))]
[InverseProperty(nameof(aspnet_Users.aspnet_UsersInRoles))]
public virtual aspnet_Users User { get; set; }
}
public partial class aspnet_Roles
{
public aspnet_Roles()
{
aspnet_UsersInRoles = new HashSet<aspnet_UsersInRoles>();
}
public Guid ApplicationId { get; set; }
[Key]
public Guid RoleId { get; set; }
[Required]
[StringLength(256)]
public string RoleName { get; set; }
[Required]
[StringLength(256)]
public string LoweredRoleName { get; set; }
[StringLength(256)]
public string Description { get; set; }
[ForeignKey(nameof(ApplicationId))]
[InverseProperty(nameof(aspnet_Applications.aspnet_Roles))]
public virtual aspnet_Applications Application { get; set; }
[InverseProperty("Role")]
public virtual ICollection<aspnet_UsersInRoles> aspnet_UsersInRoles { get; set; }
}
[Index(nameof(ApplicationId), nameof(LastActivityDate), Name = "aspnet_Users_Index2")]
public partial class aspnet_Users
{
public aspnet_Users()
{
Users = new HashSet<Users>();
aspnet_PersonalizationPerUser = new HashSet<aspnet_PersonalizationPerUser>();
aspnet_UsersInRoles = new HashSet<aspnet_UsersInRoles>();
}
public Guid ApplicationId { get; set; }
[Key]
public Guid UserId { get; set; }
[Required]
[StringLength(256)]
public string UserName { get; set; }
[Required]
[StringLength(256)]
public string LoweredUserName { get; set; }
[StringLength(16)]
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
[Column(TypeName = "datetime")]
public DateTime LastActivityDate { get; set; }
[ForeignKey(nameof(ApplicationId))]
[InverseProperty(nameof(aspnet_Applications.aspnet_Users))]
public virtual aspnet_Applications Application { get; set; }
[InverseProperty("User")]
public virtual aspnet_Membership aspnet_Membership { get; set; }
[InverseProperty("User")]
public virtual aspnet_Profile aspnet_Profile { get; set; }
[InverseProperty("aspUser")]
public virtual ICollection<Users> Users { get; set; }
[InverseProperty("User")]
public virtual ICollection<aspnet_PersonalizationPerUser> aspnet_PersonalizationPerUser { get; set; }
[InverseProperty("User")]
public virtual ICollection<aspnet_UsersInRoles> aspnet_UsersInRoles { get; set; }
}
[索引(nameof(ApplicationId)、nameof(LastActivityDate)、Name=“aspnet\u Users\u Index2”)]
公共部分类aspnet\u用户
{
公共aspnet_用户()
{
Users=newhashset();
aspnet_PersonalizationPerUser=new HashSet();
aspnet_UsersInRoles=new HashSet();
}
公共Guid应用程序ID{get;set;}
[关键]
公共Guid用户标识{get;set;}
[必需]
[StringLength(256)]
公共字符串用户名{get;set;}
[必需]
[StringLength(256)]
公共字符串LoweredUserName{get;set;}
[第16段]
公共字符串MobileAlias{get;set;}
公共布尔是非对称的{get;set;}
[列(TypeName=“datetime”)]
公共日期时间LastActivityDate{get;set;}
[ForeignKey(name of(ApplicationId))]
[InverseProperty(名称(aspnet_Applications.aspnet_Users))]
公共虚拟aspnet_应用程序应用程序{get;set;}
[反向属性(“用户”)]
公共虚拟aspnet_成员身份aspnet_成员身份{get;set;}
[反向属性(“用户”)]
公共虚拟aspnet_配置文件aspnet_配置文件{get;set;}
[反向属性(“aspUser”)]
公共虚拟ICollection用户{get;set;}
[反向属性(“用户”)]
公共虚拟ICollection aspnet_PersonalizationPerUser{get;set;}
[反向属性(“用户”)]
公共虚拟ICollection aspnet_UsersInRoles{get;set;}
}
如果我是你,我会这样写这个查询:
var result = context.UserRoles
.Where(x => x.UserId == ID_TO_SEARCH)
.Join(
context.Roles,
ur => ur.RoleId,
r => r.Id,
(ur, role) => new
{
ur,
role
}
)
.Select(x => x.role.Name)
.FirstOrDefault();
这就产生了一个查询,对我来说,这个查询非常好,也更加优雅:
SELECT TOP(1) [a0].[Name]
FROM [AspNetUserRoles] AS [a]
INNER JOIN [AspNetRoles] AS [a0] ON [a].[RoleId] = [a0].[Id]
WHERE [a].[UserId] = N''
更新:
如果我正确理解评论中的问题,则此查询将选择一个角色名称,如LEFT JOIN:
var rolesQuery = context.UserRoles
.Join(
context.Roles,
ur => ur.RoleId,
r => r.Id,
(ur, r) => new
{
ur,
r
}
);
var result = context.Users
.Where(x => x.Id == "")
.Select(u => new
{
Name = u.UserName,
Role = rolesQuery
.Where(sub=> sub.ur.UserId == u.Id)
.Select(sub=> sub.r.Name)
.FirstOrDefault()
})
.FirstOrDefault();
这将导致下一个SQL:
SELECT TOP(1) [a1].[UserName] AS [Name], (
SELECT TOP(1) [a0].[Name]
FROM [AspNetUserRoles] AS [a]
INNER JOIN [AspNetRoles] AS [a0] ON [a].[RoleId] = [a0].[Id]
WHERE [a].[UserId] = [a1].[Id]) AS [Role]
FROM [AspNetUsers] AS [a1]
WHERE [a1].[Id] = N''
正如您所看到的,并没有左连接,但sub-select将以与左连接类似的方式返回数据。不幸的是,基于lambda的查询不支持完全左连接,编写真正左连接的唯一选项可以使用类似SQL的iQuery来丰富
我在EF core 5库中看到一个名为
LeftJoin()
的方法,但它抛出NotImplementedException。我认为这是稍后将发布的内容如果您只需要特定用户的角色名称,查询应该简化:
var查询=
从this.DbContext.aspnet_用户中的r
其中r.UserId==dpass.UserId
来自r.aspnet_角色中的ro
选择ro.RoleName;
首先,两个LINQ查询并不等价-原始查询从主表aspnet\u Users
开始,而转换后的查询从联接表aspnet\u UsersInRoles
开始。这立刻产生了不同。第二,为什么要重写原始查询?EFC 5支持多对多隐式联接表,其方式与EF6完全相同,因此只需放置相同的导航属性,删除显式联接实体(但由于它使用非常规名称而对其进行配置),就可以只使用现有查询。@IvanStoev我已提供了对我的问题的更新。我想如果EFCore支持多对多关系,那么我就不会出现这些错误。所以,我试着重新编写LINQ。你知道我如何解决这个问题吗?@IvanStoev:除非EFC5添加了它(我没有听说过),否则EF Core不支持隐式多对多。您需要明确地执行此操作,即,假装这是与您自己创建的交叉表的两个一对多关系:“当您定义多对多关系时,像这样的链接表[…]EF6.x为您创建了此表,但EF Core采用更精简的方法,它不这样做–您需要这样做。”@Flater它是中的顶级功能。文档。我需要UserId
和RoleName
。但是,我得到的错误与我在今天早上的问题更新中提供的错误相同。关于如何修复它有什么想法吗?用户ID是已知的,您不必选择它。异常显示您的导航属性aspnet\u角色有问题。很难说发生了什么。@dantety89感谢您提供此查询的lambda版本。作为学习练习,我如何将UserId
列添加到输出中?正如@SvyatoslavDanyliv提到的,我不需要