如果EF Core 5以多对多关系播种数据 [英] Seeding data in many-to-many relation if EF Core 5

查看:95
本文介绍了如果EF Core 5以多对多关系播种数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有用户实体

 公共类用户{public int UserId {get;放;}公共字符串FirstName {get;放;}公共字符串LastName {get;放;}公用字符串电子邮件{get;放;}公共字符串密码{get;放;}公共ICollection< Technology>技术{放;} = new List< Technology>();} 

我有技术实体

 公共类技术{public int TechnologyId {放;}公共字符串TitleTechnology {get;放;}public int GroupId {get;放;}公众团体团体{get;放;}公共ICollection< User>用户{get;放;} = new List< User>();} 

我想创建多对多关系,所以我有这样的 OnModelCreating 方法:

 受保护的重写void OnModelCreating(ModelBuilder modelBuilder){var groupList = new List< Group>{新组{GroupId = 1,TitleGroup =".NET"}};var technologyList = new List< Technology>{新技术{TechnologyId = 1,GroupId = 1,TitleTechnology =".NET 5"},新技术{TechnologyId = 2,GroupId = 1,TitleTechnology =".NET Framework 4.8"},新技术{TechnologyId = 3,GroupId = 1,TitleTechnology ="EF 6"},新技术{TechnologyId = 4,GroupId = 1,TitleTechnology ="ASP.NET MVC 5"}};var userList = new List< User>{新用户{UserId = 1,FirstName ="Serhii",LastName ="Yurko",电子邮件="test",密码="test",技术=新列表<技术>{technologyList [0],technologyList [1]}}};modelBuilder.Entity< Technology>().HasOne(exp => exp.Group).WithMany(exp => exp.Technologies).HasForeignKey(exp => exp.GroupId);modelBuilder.Entity< User>().HasMany(p => p.Technologies).WithMany(p => p.Users).UsingEntity(j => j.ToTable("UserTechnology")));modelBuilder.Entity< User>().HasData(userList);modelBuilder.Entity< Group>().HasData(groupList);modelBuilder.Entity< Technology>().HasData(technologyList);base.OnModelCreating(modelBuilder);} 

当我要创建迁移时,会收到这样的异常-

无法添加实体类型用户"的种子实体,因为它具有导航技术"集.要播种关系,请添加实体种子发送到"TechnologyUser(Dictionary< string,object>)"并且指定外键值{'UsersUserId'}.考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'以查看涉及的属性值.

如何建立适当的关系?

解决方案

模型种子数据方法具有一些已知的局限性.最值得注意的是,它无法通过从导航属性推断关系来插入相关数据,并且必须显式添加外键值.有关详细信息,请参见-模型种子数据的限制

根据您的情况,最好使用自定义初始化逻辑方法,如此处所述-

取决于您在哪里以及如何使用它.我自己通过 IHost 扩展方法(例如-

)使用它

 公共静态异步任务GenerateSeedDataAsync(此IHost主机){使用(var scope = host.Services.CreateScope()){var provider = scope.ServiceProvider;var dbCtx = provider.GetRequiredService< DbCtx>();dbCtx.Database.Migrate();等待AddEntities(dbCtx);}} 

因此,每次应用程序启动时,我们都可以选择检查数据库是否需要种子,例如-

 公共静态void Main(string [] args){IHost主机= CreateHostBuilder(args).Build();host.GenerateSeedDataAsync().Wait();host.Run();} 

I have User entity

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>();
}

I have Technology entity

public class Technology
{
    public int TechnologyId { 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>();
}

I want to create many-to-many relation, so I have such OnModelCreating method:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var groupList = new List<Group>
    {
        new Group {GroupId = 1, TitleGroup = ".NET"}
    };

    var technologyList = new List<Technology>
    {
        new Technology {TechnologyId = 1, GroupId = 1, TitleTechnology = ".NET 5"},
        new Technology {TechnologyId = 2, GroupId = 1, TitleTechnology = ".NET Framework 4.8"},
        new Technology {TechnologyId = 3, GroupId = 1, TitleTechnology = "EF 6"},
        new Technology {TechnologyId = 4, GroupId = 1, TitleTechnology = "ASP.NET MVC 5"}
    };

    var userList = new List<User>
    {
        new User
        {
            UserId = 1, FirstName = "Serhii", LastName = "Yurko", Email = "test", Password = "test",
            Technologies = new List<Technology> {technologyList[0], technologyList[1]}
        }
    };

    modelBuilder.Entity<Technology>().HasOne(exp => exp.Group).WithMany(exp => exp.Technologies).HasForeignKey(exp => exp.GroupId);
    modelBuilder.Entity<User>().HasMany(p => p.Technologies).WithMany(p => p.Users)
        .UsingEntity(j => j.ToTable("UserTechnology"));

    modelBuilder.Entity<User>().HasData(userList);
    modelBuilder.Entity<Group>().HasData(groupList);
    modelBuilder.Entity<Technology>().HasData(technologyList);
    base.OnModelCreating(modelBuilder);
}

When I want to create migration I receive such an exception -

The seed entity for entity type 'User' cannot be added because it has the navigation 'Technologies' set. To seed relationships, add the entity seed to 'TechnologyUser (Dictionary<string, object>)' and specify the foreign key values {'UsersUserId'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.

How to create proper relations?

解决方案

The Model seed data approach has a few known limitations. Most notably, it cannot insert related data by inferring the relations from navigation properties, and you must explicitly add the foreign-key values. For details, see - Limitations of model seed data

In your case, it is better to go with the Custom initialization logic approach, as described here - Custom initialization logic (don't forget to read the Warning carefully).

An implementation of Custom initialization logic approach :
For the Models -

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>();
}

with the configuration -

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Technology>()
        .HasOne(exp => exp.Group)
        .WithMany(exp => exp.Technologies)
        .HasForeignKey(exp => exp.GroupId);

    modelBuilder.Entity<User>()
        .HasMany(p => p.Technologies)
        .WithMany(p => p.Users);
}

a possible implementation might look like -

private static async Task AddEntities(DbCtx dbCtx)
{
    List<Group> groupList;
    if (!dbCtx.Groups.Any())
    {
        groupList = new List<Group>
        {
            new Group { TitleGroup = ".NET"}
        };
        dbCtx.Groups.AddRange(groupList);
    }
    else
    {
        groupList = dbCtx.Groups.ToList();
    }

    List<Technology> technologyList;
    if (!dbCtx.Technologies.Any())
    {
        technologyList = new List<Technology>
        {
            new Technology { Group = groupList[0], TitleTechnology = ".NET 5"},
            new Technology { Group = groupList[0], TitleTechnology = ".NET Framework 4.8"},
            new Technology { Group = groupList[0], TitleTechnology = "EF 6"},
            new Technology { Group = groupList[0], TitleTechnology = "ASP.NET MVC 5"}
        };
        dbCtx.Technologies.AddRange(technologyList);
    }
    else
    {
        technologyList = dbCtx.Technologies.ToList();
    }

    List<User> userList;
    if (!dbCtx.Users.Any())
    {
        userList = new List<User>
        {
            new User
            {
                Name = "Serhii", Technologies = new List<Technology> { technologyList[0], technologyList[1]}
            }
        };
        dbCtx.Users.AddRange(userList);
    }

    await dbCtx.SaveChangesAsync();
}

Its up to you where and how you use it. I myself use it from an IHost extension method like -

public static async Task GenerateSeedDataAsync(this IHost host)
{
    using (var scope = host.Services.CreateScope())
    {
        var provider = scope.ServiceProvider;
        var dbCtx = provider.GetRequiredService<DbCtx>();
        dbCtx.Database.Migrate();

        await AddEntities(dbCtx);
    }
}

so that every time the application starts, we have the option to check whether the database needs seeding, like -

public static void Main(string[] args)
{
    IHost host = CreateHostBuilder(args).Build();
    host.GenerateSeedDataAsync().Wait();
    host.Run();
}

这篇关于如果EF Core 5以多对多关系播种数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆