在 EF Core6 中使用 Many2Many 的正确方法?
Correct way to use Many2Many in EF Core6?
我是 EF Core 6.0 的新手。我们目前有一个项目要升级,我们无法更改实际的 tables(由另一个程序使用)所以我们使用数据库 fisrt approch。
所以我需要在用户上添加一些权限(数据库是法语的)我们目前有一个 UsagrEW table(用户 table),我们添加一个权限 Table 和一个联合 table 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 class
中添加了这个导航属性
public virtual ICollection<UsagerEW> UsagerEW { get; set; }
这在 UsageREW class:
public virtual ICollection<Permission> Permission { get; set; }
但是我遇到了执行错误或者我只是尝试加载一些用户 wintout 任何包括:
UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).SingleOrDefault();
System.InvalidOperationException: 'Cannot use table
'PermissionUsagerEW' for entity type 'PermissionUsagerEW
(Dictionary<string, object>)' since it is being used for entity type
'PermissionUsagerEW' and potentially other entity types, but there is
no linking relationship. Add a foreign key to 'PermissionUsagerEW
(Dictionary<string, object>)' on the primary key properties and
pointing to the primary key on another entity type mapped to
'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 table:
//--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);
测试时
UserEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).Include(u => u.Permissions).SingleOrDefault();
现在我得到这个错误:
Microsoft.Data.SqlClient.SqlException: 'Invalid column name
'UsagerEWcode_int'.'
我认为 EF 会尝试 link 自动执行某些操作。我的解决方案中没有任何 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]
您可以配置与现有数据库的直接多对多关系,并且可以在模型中包含链接实体或将其排除。 docs. And you can leave the foreign key properties in the model, or you can replace them with 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 模型,或“普通”的脚手架模型。如果自定义模型,则无法再重新生成它,需要手动更改它以匹配将来的数据库更改。
不过,您可以应用 一些 自定义,例如基于约定的 属性 到列和实体到 table 映射例子。但是把生成的“间接多对多”改成“直接多对多”会阻止你通过脚手架重新生成EF模型
我是 EF Core 6.0 的新手。我们目前有一个项目要升级,我们无法更改实际的 tables(由另一个程序使用)所以我们使用数据库 fisrt approch。 所以我需要在用户上添加一些权限(数据库是法语的)我们目前有一个 UsagrEW table(用户 table),我们添加一个权限 Table 和一个联合 table 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 class
中添加了这个导航属性public virtual ICollection<UsagerEW> UsagerEW { get; set; }
这在 UsageREW class:
public virtual ICollection<Permission> Permission { get; set; }
但是我遇到了执行错误或者我只是尝试加载一些用户 wintout 任何包括:
UsagerEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).SingleOrDefault();
System.InvalidOperationException: 'Cannot use table 'PermissionUsagerEW' for entity type 'PermissionUsagerEW (Dictionary<string, object>)' since it is being used for entity type 'PermissionUsagerEW' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'PermissionUsagerEW (Dictionary<string, object>)' on the primary key properties and pointing to the primary key on another entity type mapped to '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 table:
//--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);
测试时 UserEW user = _EWContext.UsagerEW.Where(u=>u.Code == usagerId).Include(u => u.Permissions).SingleOrDefault();
现在我得到这个错误:
Microsoft.Data.SqlClient.SqlException: 'Invalid column name 'UsagerEWcode_int'.'
我认为 EF 会尝试 link 自动执行某些操作。我的解决方案中没有任何 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]
您可以配置与现有数据库的直接多对多关系,并且可以在模型中包含链接实体或将其排除。 docs. And you can leave the foreign key properties in the model, or you can replace them with 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 模型,或“普通”的脚手架模型。如果自定义模型,则无法再重新生成它,需要手动更改它以匹配将来的数据库更改。
不过,您可以应用 一些 自定义,例如基于约定的 属性 到列和实体到 table 映射例子。但是把生成的“间接多对多”改成“直接多对多”会阻止你通过脚手架重新生成EF模型