【问题标题】:Seed data in EF 2.1 with related entitiesEF 2.1 中的种子数据与相关实体
【发布时间】:2018-10-02 12:11:31
【问题描述】:

使用 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" }
);

但是,对于播种帖子,我从 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 文件是由第三方创建的,我无法更改它们。

要播种这些帖子,我认为我需要:

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

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

【问题讨论】:

    标签: asp.net-core entity-framework-core asp.net-core-2.1 entity-framework-core-2.1


    【解决方案1】:

    要播种这些帖子,我认为我需要:

    获取每个 CategoryName 对应的 Category Id;

    为每个帖子生成一个 ID。

    是的!你是对的!您必须在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" }
    

    然后在OnModelCreatingDbConext中如下:

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

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

    然后您必须编写一个自定义服务方法,在其中您将使用CategoryName 检查 YMl 文件中的每个帖子,然后您必须创建 Post 的新列表,您将在其中将 CategoryName 替换为 CateogryId 及其适当的取值如下:

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

    然后在OnModelCreating中的DbConext如下:

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

    【讨论】:

      【解决方案2】:

      先将类别保存到变量中,而不是原地播种:

      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 中命名的特定类别实际上不存在,您可能希望创建一个后备。您甚至可以考虑从帖子 YML 中播种类别本身,以确保其中的所有内容都实际存在:

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

      【讨论】:

      • 我只是在输入代码,没有注意。我通过将索引传递给Select 来修复它。
      • i 的值会在您的选择中连续动态设置吗?
      • 连续。它来自Select 本身,因此它会在迭代项目时为其编制索引。
      猜你喜欢
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      • 1970-01-01
      • 2019-04-03
      • 2017-04-08
      • 2019-02-17
      • 2020-04-09
      相关资源
      最近更新 更多