C# 如果EF Core 5,则以多对多关系播种数据
我有一个用户实体C# 如果EF Core 5,则以多对多关系播种数据,c#,asp.net,entity-framework-core,C#,Asp.net,Entity Framework Core,我有一个用户实体 public class User { public int UserId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public string Password { get; set; } public ICollection
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
}
当我想要创建迁移时,我会收到这样一个异常-
无法添加实体类型“User”的种子实体,因为该实体具有
导航“技术”集。要种子关系,请添加
“技术用户词典”的实体种子和
指定外键值{'UsersUserId'}。考虑使用
“DbContextOptions Builder.EnableSensivedAtalogging”以查看
涉及的属性值
如何创建正确的关系?为类添加一些属性
public class User
{
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public ICollection<Technology> Technologies { get; set; }
public ICollection<UserTechnology> UserTechnologies { get; set; }
}
public class Technology
{
[Key]
public int TechnologyId { get; set; }
public string TitleTechnology { get; set; }
public int GroupId { get; set; }
[ForeignKey(nameof(GroupId))]
[InverseProperty("Technologies")]
public Group Group { get; set; }
public ICollection<User> Users { get; set; }
public ICollection<UserTechnology> UserTechnologies { get; set; }
}
public class UserTechnology
{
public int UserId{ get; set; }
public User User { get; set; }
public int Technology TechnologyId{ get; set; }
public Technology Technology{ get; set; }
}
public partial class Group()
{
[Key]
public int Id { get; set; }
......
[InverseProperty(nameof(Technology.Group))]
public virtual ICollection<Technology> Technologies{ get; set; }
}
并向dbcontext添加一些代码
modelBuilder.Entity<UserTechnology>()
.HasKey(t => new { t.UserId, t.Techn});
modelBuilder.Entity<UserTechnology>()
.HasOne(pt => pt.User)
.WithMany(p => p.UserTechnologies)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserTechnology>()
.HasOne(pt => pt.Technology)
.WithMany(t => t.UserTechnologies)
.HasForeignKey(pt => pt.TechologyId);
向类添加一些属性
public class User
{
[Key]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public ICollection<Technology> Technologies { get; set; }
public ICollection<UserTechnology> UserTechnologies { get; set; }
}
public class Technology
{
[Key]
public int TechnologyId { get; set; }
public string TitleTechnology { get; set; }
public int GroupId { get; set; }
[ForeignKey(nameof(GroupId))]
[InverseProperty("Technologies")]
public Group Group { get; set; }
public ICollection<User> Users { get; set; }
public ICollection<UserTechnology> UserTechnologies { get; set; }
}
public class UserTechnology
{
public int UserId{ get; set; }
public User User { get; set; }
public int Technology TechnologyId{ get; set; }
public Technology Technology{ get; set; }
}
public partial class Group()
{
[Key]
public int Id { get; set; }
......
[InverseProperty(nameof(Technology.Group))]
public virtual ICollection<Technology> Technologies{ get; set; }
}
并向dbcontext添加一些代码
modelBuilder.Entity<UserTechnology>()
.HasKey(t => new { t.UserId, t.Techn});
modelBuilder.Entity<UserTechnology>()
.HasOne(pt => pt.User)
.WithMany(p => p.UserTechnologies)
.HasForeignKey(pt => pt.UserId);
modelBuilder.Entity<UserTechnology>()
.HasOne(pt => pt.Technology)
.WithMany(t => t.UserTechnologies)
.HasForeignKey(pt => pt.TechologyId);
在EF core中,不能直接为导航属性设置种子。您必须删除行技术=。。。从用户初始化 如上所述,通过扩展UsingEntity调用,可以通过其自己的HasData调用为连接表设定种子,如下所示: .UsingEntityj=>j .ToTableUserTechnology .HasDatanew[] { {UsersID=1,TechnologiesID=1}, {UsersID=1,TechnologiesID=2} } ; 如您所见,隐藏连接实体由一个匿名类型填充,该匿名类型具有与表字段相同的属性名+类型。这要求您了解这些外键字段的命名约定,我希望我猜对了
通过使用相当笨拙的连接表的完全初始化,您可以自己完成这一切,如和中所述。这允许您强制使用自己的FK属性名称,并在种子设定代码中相应地使用它们。在EF core中,您不能直接为导航属性设定种子。您必须删除行技术=。。。从用户初始化 如上所述,通过扩展UsingEntity调用,可以通过其自己的HasData调用为连接表设定种子,如下所示: .UsingEntityj=>j .ToTableUserTechnology .HasDatanew[] { {UsersID=1,TechnologiesID=1}, {UsersID=1,TechnologiesID=2} } ; 如您所见,隐藏连接实体由一个匿名类型填充,该匿名类型具有与表字段相同的属性名+类型。这要求您了解这些外键字段的命名约定,我希望我猜对了 通过使用相当笨拙的连接表的完全初始化,您可以自己完成这一切,如和中所述。这允许您强制使用自己的FK属性名称,并在种子设定代码中相应地使用它们。模型种子数据方法有一些已知的限制。最值得注意的是,它无法通过从导航属性推断关系来插入相关数据,并且必须显式添加外键值。详情请参阅- 在您的情况下,最好使用这里描述的自定义初始化逻辑方法-不要忘记仔细阅读警告 自定义初始化逻辑方法的实现: 对于模型-
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
}
public class Technology
{
public int Id { get; set; }
public string TitleTechnology { get; set; }
public int GroupId { get; set; }
public Group Group { get; set; }
public ICollection<User> Users { get; set; } = new List<User>();
}
public class Group
{
public int Id { get; set; }
public string TitleGroup { get; set; }
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
}
因此,每当应用程序启动时,我们都可以选择检查数据库是否需要种子设定,如-
public static void Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();
host.GenerateSeedDataAsync().Wait();
host.Run();
}
模型种子数据方法有一些已知的局限性。最值得注意的是,它无法通过从导航属性推断关系来插入相关数据,并且必须显式添加外键值。详情请参阅-
在您的情况下,最好使用这里描述的自定义初始化逻辑方法-不要忘记仔细阅读警告
自定义初始化逻辑方法的实现:
对于模型-
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
}
public class Technology
{
public int Id { get; set; }
public string TitleTechnology { get; set; }
public int GroupId { get; set; }
public Group Group { get; set; }
public ICollection<User> Users { get; set; } = new List<User>();
}
public class Group
{
public int Id { get; set; }
public string TitleGroup { get; set; }
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
}
因此,每当应用程序启动时,我们都可以选择检查数据库是否需要种子设定,如-
public static void Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();
host.GenerateSeedDataAsync().Wait();
host.Run();
}
这只是模拟多对多关联的另一种方法。“我不认为奥普要求的是另一种选择。”格塔诺尔,一开始我是按照你的方式考虑的。并将发布几乎相同的答案。但在此之后,我想知道是否添加了第三个表,也许ef不需要添加这些额外的数据。这只是一个实验,我想看看结果。@GertArnold-但你打破了它。如果第一个解决方案不起作用,我会提供另一个解决方案,但你已经发布了。这只是建模多对多关联的另一种方法。“我不认为奥普要求的是另一种选择。”格塔诺尔,一开始我是按照你的方式考虑的。并将发布几乎相同的答案。但在此之后,我想知道是否添加了第三个表,也许ef不需要添加这些额外的数据。这只是一个实验,我想看看结果。@GertArnold-但你打破了它。如果第一个解决方案不起作用,我会提供另一个解决方案,但你已经发布了。虽然我的回答简单且技术上回答了眼前的问题,但我必须说,在这种情况下,我也更喜欢定制种子。主要是因为
它看起来不仅仅是一些基本数据的简单播种。这是商业数据。我甚至会更进一步,使用指定的业务方法来创建这样的数据。例如,创建一个用户可能涉及到业务逻辑。因此,只能通过该逻辑创建用户。在数据层中不应该有任何阴暗的角落也会发生这种情况,绕过所有这些规则。这意味着:这种方法和连接可以+1,但我会在这里调用业务逻辑方法。@GertArnold谢谢。我非常感谢你的建议,我必须同意这将是一个更专业的方法。虽然我的回答简单且技术上回答了眼前的问题,但我必须说,在这种情况下,我也更喜欢定制种子。主要是因为这看起来不仅仅是一些基本数据的简单播种。这是商业数据。我甚至会更进一步,使用指定的业务方法来创建这样的数据。例如,创建一个用户可能涉及到业务逻辑。因此,只能通过该逻辑创建用户。在数据层中不应该有任何阴暗的角落也会发生这种情况,绕过所有这些规则。这意味着:这种方法和连接可以+1,但我会在这里调用业务逻辑方法。@GertArnold谢谢。我非常感谢你的建议,我必须同意这将是一种更专业的方法。
public static void Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();
host.GenerateSeedDataAsync().Wait();
host.Run();
}