Исходные данные в EF 2.1 со связанными сущностями

Используя 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;
  2. Создайте идентификатор для каждого сообщения.

Шаг (2) кажется простым, но как мне выполнить шаг (1)?


person Miguel Moura    schedule 02.10.2018    source источник


Ответы (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();
person Chris Pratt    schedule 02.10.2018
comment
Я просто набирал код и не обращал внимания. Я исправил это, передав индекс Select. - person Chris Pratt; 02.10.2018
comment
Будет ли значение i устанавливаться последовательно и динамически в вашем выборе? - person TanvirArjel; 02.10.2018
comment
Серийно. Это из самого Select, поэтому он индексирует элементы по мере их повторения. - person Chris Pratt; 02.10.2018

Я считаю, что для заполнения этих постов мне нужно:

Получите соответствующий идентификатор категории для каждого CategoryName;

Создайте идентификатор для каждого сообщения.

Да! Ты прав! вы должны использовать CategoryId вместо CategoryName в Post, потому что CategoryId является реляционным ключом, а не CategoryName. Таким образом, ваш YML-контент должен выглядеть следующим образом:

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

Затем в OnModelCreating из DbConext следующим образом:

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 созданы третьей стороной, и я не могу их изменить.

Затем вам нужно написать пользовательский метод службы, в котором вы будете проверять каждое сообщение в файле YML с помощью CategoryName, затем вам нужно создать новый список 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);
           
}
person TanvirArjel    schedule 02.10.2018