c# - 如何在 Entity Framework Core 中正确编写种子方法?

标签 c# asp.net entity-framework-core

我正在学习 ASP.NET 并在我的项目中使用 EF Core。我想在运行迁移时植入一些测试数据。这是我的 AppDbContext 类和我的模型类:

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

正如您可能注意到的,Game 和 Studio 实体之间的关系有点复杂。不仅游戏有创建游戏的工作室,而且工作室有他们开发的游戏列表。

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

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

最佳答案

也许是因为人们只读了标题,但每个人都跳到了如何在 Entity Framework 中播种,句号。接下来,有人想出了 AddOrUpdate这在 EF 核心中不存在。

与 EF6 相比,EF 核心中的播种发生了巨大变化。在 EF6 的 Seed方法可以保存对象图,即包含引用和集合的对象。然而,AddOrUpdate方法有几个问题(我不会在这里详细说明),如果您没有意识到这些问题,它们的行为很难理解甚至无法正确执行。

作为对此的(过度) react ,EF 团队决定不再让 EF 确定是否应添加或更新实体。播种应该添加一个实体(如果它不存在),否则什么都不做。永不更新。这些考虑导致 EF 核心中大大(过度)简化的播种机制:

  • 实体应由其硬编码的主键标识 .如有必要,它们会插入 IDENTITY INSERT ON (在 SQL Server 中)。
  • 外键也应该是硬编码的。
  • 无法填充导航属性。如果是,EF 将抛出异常,例如

    InvalidOperationException: The seed entity for entity type 'Studio' cannot be added because it has the navigation 'Games' set. To seed relationships you need to add the related entity seed to 'Game' and specify the foreign key values {'StudioId'}.

这意味着您必须添加 StudioIdGame .和 StudioIdGenreIdPerson .不支持独立关联(不带 StudioStudioId 引用)。 (我认为这是一个影响深远的架构决策)。

这样做,您的播种代码经过稍微简化后可能如下所示:

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有一个Director类型属性 PersonDirectorId 提及并且主管也是雇员:

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: Unable to save changes because a circular dependency was detected in the data to be saved.

我认为这是该设计决策的另一个深远影响。在 EF6 中,即使有残缺的 AddOrUpdate , 至少可以调用 SaveChanges两次以适应这种情况。

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

关于c# - 如何在 Entity Framework Core 中正确编写种子方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59478797/

相关文章:

c# - Xamarin.Forms 使用 SQLite.Net.Async(NUGET 部分)

c# - 如何使用 Linq 获取 sitecore 字段

c# - 使用 Javascript 从 ASP.NET FileUpload 控件加载图像

.net - dotnet ef 迁移列表在迁移文件夹中找不到迁移

c# - 使用 SqlDataReader 时为 "Invalid attempt to call Read when reader is closed"

c# - ObjectContext.SaveChanges 和重复的主键

c# - 在 Web API 中正确使用 async/await

c# - 无法将 RegularExpressionValidator 添加到面板

asp.net-core - 在 Asp.Net Core 中注入(inject) DbContext。具体类型还是接口(interface)?

c# - 尝试插入数据时,Entity Framework Core 中的一对多关系出现错误