在 EF Core6 中使用 Many2Many 的正确方法?
Posted
技术标签:
【中文标题】在 EF Core6 中使用 Many2Many 的正确方法?【英文标题】:Correct way to use Many2Many in EF Core6? 【发布时间】:2022-01-10 04:36:36 【问题描述】:我对 EF Core 6.0 还是很陌生。我们目前有一个要升级的项目,我们无法更改实际的表(由另一个程序使用),所以我们使用数据库优先方法。 所以我需要为用户添加一些权限(数据库是法语)我们目前有一个UsagerEW表(用户表),我们为Many2Many添加了一个权限表和一个联合表PermissionUsagerEW。在这里做 Scaffold-dbContect 后的结果是:
UsagerEW(主键是 Code_Int)
public partial class UsagerEW
public UsagerEW()
PermissionUsagerEW = new HashSet<PermissionUsagerEW>();
RefreshToken = new HashSet<RefreshToken>();
public string Code get; set;
public string Nom get; set;
public string Email get; set;
public string ModeLogin get; set;
public string PasswordTemp get; set;
public DateTime? PasswordTempExp get; set;
public int code_int get; set;
public virtual ICollection<PermissionUsagerEW> PermissionUsagerEW get; set;
Pemrssion 和 PermissionUsagerEW
public partial class Permission
public Permission()
PermissionUsagerEW = new HashSet<PermissionUsagerEW>();
public int id get; set;
public string code get; set;
public string description get; set;
public int? moduleId get; set;
public virtual Module module get; set;
public virtual ICollection<PermissionUsagerEW> PermissionUsagerEW get; set;
public partial class PermissionUsagerEW
public int id get; set;
public int permissionId get; set;
public int usagerCodeInt get; set;
public virtual Permission permission get; set;
public virtual UsagerEW usagerCodeIntNavigation get; set;
编译后,我可以从 UsagerEW 中“使用包含导航”并获取特定 UsagerEW 的 PermissionUsagerEW 列表。
现在就像我在应该支持 Many2Many 的 EF COre 6.0 我在 Permnission 类中添加了这个导航属性
public virtual ICollection<UsagerEW> UsagerEW get; set;
这在 UsagerEW 类中:
public virtual ICollection<Permission> Permission get; set;
但是我得到了执行错误,我只是尝试加载一些用户 wintout 任何包括:
UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).SingleOrDefault();
System.InvalidOperationException: '不能使用表 实体类型“PermissionUsagerEW”的“PermissionUsagerEW” (Dictionary
)' 因为它被用于实体类型 'PermissionUsagerEW' 和可能的其他实体类型,但有 没有链接关系。将外键添加到 'PermissionUsagerEW (Dictionary )' 在主键属性和 指向映射到的另一个实体类型上的主键 'PermissionUsagerEW'。'
脚手架检测FK:
modelBuilder.Entity<PermissionUsagerEW>(entity =>
entity.HasOne(d => d.permission)
.WithMany(p => p.PermissionUsagerEW)
.HasForeignKey(d => d.permissionId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PermissionUsager_Permission");
entity.HasOne(d => d.usagerCodeIntNavigation)
.WithMany(p => p.PermissionUsagerEW)
.HasForeignKey(d => d.usagerCodeInt)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PermissionUsager_Usager");
);
有什么想法吗?
---编辑 1 我更改了您的代码以反映脚手架 PermissionUsagerEW 表:
//--UsagewrEW
modelBuilder.Entity<UsagerEW>()
.HasKey(u => u.code_int);
modelBuilder.Entity<UsagerEW>()
.HasMany(u => u.Permissions)
.WithMany(p => p.Users)
.UsingEntity<PermissionUsagerEW>(
p => p.HasOne(e => e.permission)
.WithMany()
.HasForeignKey(e => e.permissionId),
p => p.HasOne(p => p.usagerCodeIntNavigation)
.WithMany()
.HasForeignKey(e => e.usagerCodeInt)
);
modelBuilder.Entity<PermissionUsagerEW>()
.HasOne(p => p.usagerCodeIntNavigation)
.WithMany()
.HasForeignKey(p => p.usagerCodeInt);
当使用 UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).Include(u => u.Permissions).SingleOrDefault();
现在我收到了这个错误:
Microsoft.Data.SqlClient.SqlException: '无效的列名 'UsagerEWcode_int'。'
我认为 EF 会尝试自动链接某些内容。我的解决方案中没有任何 UsagerEWcode_int。
编辑2: 有生成的SQL。奇怪的列名和一些重复......
SELECT [u].[code_int], [u].[Administrateur], [u].[Code], [u].[Email], [u].[EmpContact], [u].[Inactif], [u].[KelvinConfig], [u].[LectureSeule], [u].[ModeLogin], [u].[Nom], [u].[ParamRole], [u].[Password], [u].[PasswordTemp], [u].[PasswordTempExp], [u].[RestreintCommContrat], [u].[RestreintProjet], [u].[Role], [u].[UsagerAD], [u].[doitChangerPW], [u].[estSuperviseur], [u].[idSuperviseur], [u].[infoSession], [u].[paramRole2], [u].[permsGrps], [t].[id], [t].[Permissionid], [t].[UsagerEWcode_int], [t].[permissionId0], [t].[usagerCodeInt], [t].[id0], [t].[code], [t].[description], [t].[moduleId]
FROM [UsagerEW] AS [u]
LEFT JOIN (
SELECT [p].[id], [p].[Permissionid], [p].[UsagerEWcode_int], [p].[permissionId] AS [permissionId0], [p].[usagerCodeInt], [p0].[id] AS [id0], [p0].[code], [p0].[description], [p0].[moduleId]
FROM [PermissionUsagerEW] AS [p]
INNER JOIN [Permission] AS [p0] ON [p].[permissionId] = [p0].[id]
) AS [t] ON [u].[code_int] = [t].[usagerCodeInt]
WHERE [u].[Code] = @__usagerId_0
ORDER BY [u].[code_int], [t].[id]
【问题讨论】:
这个modelBuilder.Entity<PermissionUsagerEW>()
是不必要的,并且会导致虚假列。 p => p.usagerCodeIntNavigation
已在上一条语句中配置。
【参考方案1】:
您可以配置与现有数据库的直接多对多关系,并且可以在模型中包含链接实体或将其排除。 docs 中有几个例子。您可以将外键属性保留在模型中,也可以将它们替换为shadow properties。但是脚手架代码不会为您做任何这些。它为数据库模式创建了最简单的正确模型。
此外,您通常应该重命名实体和属性以符合 .NET 编码约定。
无论如何,这样的事情应该可以工作:
public partial class UsagerEW
public string Code get; set;
public string Nom get; set;
public string Email get; set;
public string ModeLogin get; set;
public string PasswordTemp get; set;
public DateTime? PasswordTempExp get; set;
public int code_int get; set;
public virtual ICollection<Permission> Permissions get; = new HashSet<Permission>();
public partial class Permission
public int Id get; set;
public string Code get; set;
public string Description get; set;
public int? ModuleId get; set;
//public virtual Module module get; set;
public virtual ICollection<UsagerEW> Users get; = new HashSet<UsagerEW>();
public partial class PermissionUsagerEW
public int Id get; set;
public int PermissionId get; set;
public int UsagerCodeInt get; set;
public virtual Permission Permission get; set;
public virtual UsagerEW User get; set;
public class Db : DbContext
protected override void OnModelCreating(ModelBuilder builder)
builder.Entity<UsagerEW>()
.HasKey(u => u.code_int);
builder.Entity<UsagerEW>()
.HasMany(u => u.Permissions)
.WithMany(p => p.Users)
.UsingEntity<PermissionUsagerEW>(
p => p.HasOne(e => e.Permission)
.WithMany()
.HasForeignKey(e => e.PermissionId),
p => p.HasOne(p => p.User)
.WithMany()
.HasForeignKey( e => e.UsagerCodeInt)
);
builder.Entity<PermissionUsagerEW>()
.HasOne(p => p.User)
.WithMany()
.HasForeignKey(p => p.UsagerCodeInt);
foreach (var prop in builder.Model.GetEntityTypes().SelectMany(e => e.GetProperties()))
prop.SetColumnName(char.ToLower(prop.Name[0]) + prop.Name.Substring(1));
base.OnModelCreating(builder);
但是当您在数据库优先的工作流程中工作时,深度自定义 EF 模型有一个缺点:您失去了从数据库重新生成 EF 模型的能力。
因此,您可以使用“不错”的自定义 EF 模型,或“普通”的脚手架模型。如果您自定义模型,则无法再重新生成它,需要手动对其进行更改以匹配未来的数据库更改。
您可以应用 一些 自定义,例如示例中基于约定的属性到列和实体到表的映射。但是将生成的“间接多对多”更改为“直接多对多”会阻止您通过脚手架重新生成 EF 模型。
【讨论】:
我同意我需要更改实体以匹配 .net 约定。但是当我需要多次搭建脚手架时,我怎么能做到这一点。现在我获得了用于添加一些属性的部分类,并覆盖了使用 Fluent 进行调优的上下文。但是,例如,如果 EF Scaffold 创建了一个导航属性,例如 usagerCodeInNavigation,因为 UsagerEW 没有 id 字段。我怎样才能覆盖它? 这是一个权衡。查看更新的答案。 感谢您的回答。我尝试你的代码,但我得到一个无效的列错误,检查我的编辑。 我了解您更新的答案。所以,对于一些不遵循 EF 规则的 OldDB,数据库优先的方式比较复杂。在这种情况下,像您这样的专家是什么?手动使用实体和 Fluent 代码创建旧数据库的 EF 对象映射并避免脚手架? 视情况而定™。两者都是合理的方法。以上是关于在 EF Core6 中使用 Many2Many 的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章
.net core6 检测ip网址是否可访问。不需要端口号。