首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在实体框架核心中正确编写种子方法?

如何在实体框架核心中正确编写种子方法?
EN

Stack Overflow用户
提问于 2019-12-25 13:21:29
回答 5查看 6.1K关注 0票数 3

我正在学习ASP.NET,并且在我的项目中使用EF核心。在运行迁移时,我想添加一些测试数据。下面是我的AppDbContext类和模型类:

代码语言:javascript
复制
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<Game> Games { get; set; }
    public DbSet<Studio> Studios { get; set; }
    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        var converter = new EnumToNumberConverter<Genre, byte>();

        builder.Entity<Game>().Property(item => item.Genre).HasConversion(converter);
    }
}

public class Person
{
    [Key]
    public int Id { get; set; }
    [Required]
    [StringLength(30)]
    public string Name { get; set; }
    [Required]
    [StringLength(30)]
    public string Surname { get; set; }
}

public class Game
{
    [Key]
    public int Id { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [Range(0.0, 1000.0)]
    public double Price { get; set; }

    public Genre Genre { get; set; }

    [Required]
    public Studio Studio { get; set; }
}

public class Studio
{
    [Key]
    public int Id { get; set; }
    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    public IEnumerable<Game> Games { get; set; }
    [Required]
    public decimal Budget { get; set; }
    public IEnumerable<Person> Employees { get; set; }
}

正如你所注意到的,在游戏和工作室实体之间有一个有点复杂的关系。游戏不仅有制作游戏的工作室,而且有他们开发的游戏列表。

如果我必须编写OnModelCreating方法,我将首先创建一些测试人员,然后使用它们来填充Studio类中的Employees字段。当我不得不创建游戏时,问题就会发生。工作室还不存在,如果我想先创建工作室,游戏是一个缺失的元素。

我必须手动创建一些游戏,忽略Studio引用,然后实例化工作室,然后手动将对象链接到一起,还是有更好的解决方案?诚挚的问候。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2019-12-26 20:52:04

也许是因为人们只读了标题,但是大家都跳到了如何在实体框架中播种的问题上。接下来,一些人提出了EF核中不存在的AddOrUpdate

EF核中的种子与EF6相比发生了巨大的变化。在EF6的Seed方法中,可以保存对象图,即包含引用和集合的对象。但是,AddOrUpdate方法有几个问题(我不会在这里详细说明它们),这使得它的行为很难跟踪,甚至如果您没有意识到它们,甚至很难纠正它们。

作为对此的(结束)反应,EF团队决定不再让EF决定是否应该添加或更新一个实体。种子应该添加一个实体,如果它是不存在的,或者不做任何其他的事情。永远不要更新。这些考虑因素大大简化了EF核心的播种机制:

  • 实体应该通过其硬编码的主键来标识。如果有必要,它们会插入到Server).
  • Foreign (在Sql IDENTITY INSERT ON键中也应该是硬的),coded.
  • Navigation属性不能填充。如果是,EF将抛出一个类似于InvalidOperationException的异常:不能添加实体类型'Studio‘的种子实体,因为它有导航'Games’集。要建立种子关系,需要将相关实体种子添加到“游戏”中,并指定外键值{'StudioId'}.

这意味着您必须将StudioId添加到Game中。StudioIdGenreIdPerson。不支持独立的关联(没有StudioIdStudioId引用)。(我认为这是一个意义深远的建筑决定)。

这样做,您的种子代码(稍微简化了一点)可能如下所示:

代码语言:javascript
复制
var games = new[]
{
    new Game{ Id = 1, Name = "Game1", StudioId = 1 },
    new Game{ Id = 2, Name = "Game2", StudioId = 1 },
};
var studio = new Studio
{
    Id = 1,
    Name = "Studio1",
};

modelBuilder.Entity<Studio>().HasData(studio);
modelBuilder.Entity<Game>().HasData(games);

循环引用StudioGame在这里并不重要,因为它只代表一个外键。

然而,对两个外键的循环引用是不可能的。假设Studio具有由DirectorId引用的Person类型的Director属性,并且董事也是雇员:

代码语言:javascript
复制
var director = new Person { Id = 1, Name = "Director1", StudioId = 1 }; // Employee of Studio1

var studio = new Studio
{
    Id = 1,
    Name = "Studio1",
    DirectorId = 1 // Director is "Director1"
};

现在出现了一个鸡与蛋问题:只有先插入另一个实体,才能插入两个实体。

InvalidOperationException:无法保存更改,因为要保存的数据中检测到循环依赖项。

我认为这是这个设计决定的另一个深远后果。在EF6中,即使它的AddOrUpdate瘫痪了,至少也可以调用SaveChanges两次来适应这种情况。

考虑到最重要的是,种子不是迁移友好的,我对数据播种的立场是:要么不支持它(我不介意),要么支持它。

票数 3
EN

Stack Overflow用户

发布于 2019-12-25 13:33:03

您应该使用ModelBuilder来种子数据:

代码语言:javascript
复制
modelBuilder.Entity<Post>().HasData(
    new Post() { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
票数 0
EN

Stack Overflow用户

发布于 2019-12-25 13:25:40

代码语言:javascript
复制
   protected override async void Seed(ApplicationContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data.


        context.Test.AddOrUpdate(x => x.Id,
                 new Test() { Id = 1, Name = "Test" }
        );
        await context.SaveChangesAsync();

    }

在PM中插入: 1.启用-移动2.添加-迁移初始3.更新-数据库

票数 -2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59478797

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档