EF 2.1 中的种子数据与相关实体

Seed data in EF 2.1 with related entities

使用 ASP.NET Core 2.1 和 Entity Framework Core 2.1 我有以下实体:

public class Category {
  public Int32 Id { get; set; }
  public String Name { get; set; }
}

public class Post {
  public Int32 Id { get; set; }
  public Int32 CategoryId { get; set; }
  public String Title { get; set; }
  public String Content { get; set; }
  public virtual Category Category { get; set; }
}

对于播种类别,我有以下内容:

modelBuilder.Entity<Category>().HasData(
  new { Id = 1, Name = "Reading" },
  new { Id = 2, Name = "Travelling" }
);

但是,对于播种 posts,我从 YML 文件中获取数据:

var posts = _service.GetPostsFromYMLFiles(path);

modelBuilder.Entity<Post>().HasData(posts);

posts 取自 YML 文件,如下:

new { CategoryName = "Reading", Title = "A", Content = "A Content" },
new { CategoryName = "Travelling", Title = "B", Content = "B Content" }

这些 YML 文件是由第三方创建的,我无法更改它们。

要播种这些 post,我认为我需要:

  1. 为每个CategoryName获取对应的Category Id;
  2. 为每个post生成一个ID。

步骤 (2) 看起来很简单,但我怎样才能完成步骤 (1)?

To seed these posts I believe I need to:

Get the correspondent Category Id for each CategoryName;

Generate an ID for each post.

是的!你是对的!您必须在 Post 中使用 CategoryId 而不是 CategoryName,因为 CategoryId 是关系键,而不是 CategoryName。所以你的 YML 内容应该是这样的:

new {Id = 1, CategoryId = 1, Title = "A", Content = "A Content" },
new {Id = 2, CategoryId = 2, Title = "B", Content = "B Content" }

然后在DbConextOnModelCreating如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      base.OnModelCreating(modelBuilder);
      
      var posts = _service.GetPostsFromYMLFiles(path);

      modelBuilder.Entity<Category>().HasData(
          new { Id = 1, Name = "Reading"},
          new { Id = 2, Name = "Travelling" }
      );

      modelBuilder.Entity<Post>().HasData(posts);        
}

These YML files are created by a third party and I cannot change them.

然后你必须编写一个自定义服务方法,你将使用 CategoryName 检查 YMl 文件中的每个 post 然后你必须制作新的 Post 列表,你将在其中替换CategoryNameCateogryId 及其适当的值如下:

public class YmlPost
{
    public string CategoryName { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}


public class SeedDataService
{
    static List<Category> categoryList = new List<Category>()
    {
        new Category { Id = 1, Name = "Reading" },
        new Category { Id = 2, Name = "Traveling" }
    };

    public static List<Category> GetCategoriesForSeeding()
    {
       return categoryList;
    }
    public static List<Post> GetPostsForSeeding()
    {
        List<YmlPost> postListFromYML = _service.GetPostsFromYMLFiles(path);

        List<Post> posts = postListFromYML.Select((p, i) => new Post
            { 
              Id = i+1, // index is 0 based that's why you have to add 1.
              Title = p.Title,
              Content = p.Content,
              CategoryId = categoryList.Single(c => c.Name == 
                           p.CategoryName).Id
            }).ToList();
        return posts;
    }
}

然后在DbConextOnModelCreating如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      base.OnModelCreating(modelBuilder);
      
      var categories = SeedDataService.GetCategoriesForSeeding();
      modelBuilder.Entity<Category>().HasData(categories);
      
      var posts = SeedDataService.GetPostsForSeeding();
      modelBuilder.Entity<Post>().HasData(posts);
           
}

不要将类别播种到位,而是先将它们保存到变量中:

var categories = new[] 
{
    new Category { Id = 1, Name = "Reading" },
    new Category { Id = 2, Name = "Traveling" }
};

然后,将其传递给 HasData:

modelBuilder.Entity<Category>().HasData(categories);

现在,您可以在内存中查询此列表以获取相关类别,同时将 YML 数据映射到 Post 实体:

var posts = _service.GetPostsFromYMLFiles(path);
modelBuilder.Entity<Post>().HasData(posts.Select((p, i) => new Post
{
    Id = i,
    Title = p.Title,
    Content = p.Content,
    CategoryId = categories.Single(c => c.Name == p.CategoryName).Id
});

CategoryId 是不可为空的,因此如果 YML 中命名的特定类别实际上不存在,您可能想要创建一个回退。您甚至可以考虑从 post YML 中自己播种类别,以确保其中的所有内容确实存在:

var categories = posts.Select(p => p.CategoryName).Distinct().Select((c, i) => new Category
{
    Id = i,
    Name = c
}).ToArray();