EF Core Include() ThenInlude() добавляет только одну запись к каждому элементу коллекции

#c# #entity-framework #.net-core #entity-framework-core

#c# #entity-framework #.net-ядро #entity-framework-core

Вопрос:

Построение ASP.NET Основной API, который предоставляет данные из существующей базы данных. Все необходимые объекты основаны на данных из нескольких таблиц, поэтому для каждого создано представление. Вывод API — JSON с объектами hirerachy, созданными с использованием includes между наборами баз данных.

Существует родительская сущность Projects, которая включает дочерние компоненты, в которую входят пользователи, и некоторые другие, но они не являются частью проблемы. Упрощенные DTO:

     public class RrojectDTO
    {
        public int projectId { get; set; }
        public string componentCode { get; set; }
        public virtual ICollection<ComponentDTO> componentsArray { get; set; }
    }
    
    public class ComponentDTO
    {
         public Guid componentId { get; set; }
         public virtual RrojectDTO Project { get; set; }
         public string componentCode { get; set; }
         public virtual ICollection<UserDTO> usersArray { get; set; }
         ...
    }

    public class UserDTO
    {
         public int userId { get; set; }
         public virtual ComponentDTO Component { get; set; }
         public Guid componentId { get; set; }
         ...
    }
  

Вот как выглядит класс DbContext:

 public MyDbContext(DbContextOptions options)
   : base(options)
    {
        ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTrackingWithIdentityResolution;            
    }

    public virtual DbSet<ProjectDTO> Projects{ get; set; }
    public virtual DbSet<ComponentDTO> Components { get; set; }
    public virtual DbSet<UserDTO> Users { get; set; }
    ....


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<ProjectDTO>(entity =>
        {
            entity.HasKey(f => f.componentCode);
            entity.ToView("ProjectsArrayView", "LAB");
            entity.HasMany(p => p.componentsArray).WithOne(t => t.Project)
            .HasForeignKey(t => t.componentCode);

        });

        modelBuilder.Entity<ComponentDTO>(entity =>
        {
            entity.HasKey(f => f.componentId);
            entity.ToView("ComponentArrayView", "LAB");
            entity.HasMany(p => p.usersArray).WithOne(t => t.Component)
            .HasForeignKey(t => t.componentId);
            ....
        });

        modelBuilder.Entity<UserDTO>(entity =>
        {
            entity.HasKey(f => f.userId);
            entity.ToView("UsersArrayView", "LAB");
            ...
        });
  

Я соединяю эти объекты со следующим кодом:

 `var projects = context.Projects.Where(p => p.projectId == Guid.Parse("9C373F8F-CAB8-4914-83D9-4864B2E3E7B3")).Include(co => co.componentsArray).ThenInclude(j => j.usersArray).ToList();`
  

Изначально в проект включено больше, но другие, похоже, работают нормально. Хотя один из примеров работает не так, как ожидалось, например: есть один проект, в котором есть 3 компонента, те же 2 пользователя (User1 и User2) работают над каждым компонентом, если вы запрашиваете каждый набор баз данных по ключевым параметрам, вы должны иметь возможность видеть соответствующие данные. результат проекта: 1 проект -> с 3 компонентами -> каждый компонент содержит только 1 пользователя вместо двух.

Мой вопрос: почему.ThenInlcude() добавляет только User1 для каждого компонента, но игнорирует User2 ? чего мне здесь не хватает?

Если я запрашиваю только компоненты DbSet и Include(x=> x.usersArray) — результат все тот же, Пользователь1 на компонент. Кроме того, если я добавляю User3, User4 — все равно заполняется User1.

Когда я нашел запрос через SQL profiler, он содержал обоих пользователей (не может поделиться запросом из-за политик). Просмотрел около 20 статей, но я думаю, что мне здесь не хватает чего-то основного, пожалуйста, дайте мне знать, если я могу что-то добавить, чтобы улучшить это qeustion.

Ответ №1:

Это реляционная база данных. Каждая запись глубокого уровня должна иметь ключи всех предыдущих уровней. Я не понимаю, как ваш пользователь связан с компонентом. Добавьте в свой класс внешний ключ UserDTO componentID, если каждый пользователь может работать только с одним компонентом, или создайте таблицу UserComponent, содержащую userId и componentID, чтобы сохранить отношение «многие ко многим».

 public class UserDTO
    {
         public int userId { get; set; }
         public int componentId {get;set;}
         public virtual ComponentDTO Component { get; set; }
         public Guid projectId  { get; set; }
         ...
    }
  

Комментарии:

1. Спасибо за ваш ответ, я немного перепутал, у UserDTO нет «ProjectID», это «componentID», который является внешним ключом. Исправлено в вопросе.

2. Но это означает, что каждый пользователь может работать только с одним компонентом. Это правда?

3. На самом деле нет, каждый компонент может обрабатываться N пользователями. Это то, чего я пытаюсь достичь здесь, и именно поэтому я создаю отношения один (компонент) для многих (пользователей). Я где-то ошибаюсь?

4. Да, как я уже сказал в своем ответе, тогда у вас должна быть еще одна таблица -UserComponent, содержащая userId и componentID

5. Большое вам спасибо! Я только что понял, что я смотрел только на одну сторону и не понял, что это должно быть много-ко-многим.