Отношение «один ко многим» не извлекает данные в entity framework

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

Вопрос:

Я в процессе изучения C# и .NET и EF (с шаблоном aspnetboiler), и мне пришла в голову идея создать какой-нибудь фиктивный проект, чтобы я мог практиковаться. Но последние 4 часа я застрял с этой ошибкой и надеюсь, что кто-нибудь здесь сможет мне помочь.

То, что я создаю( ну, по крайней мере, я думаю, что создаю это правильно), — это 2 класса под названием «Ингредиент» и «Мастер».

Я хочу использовать его для классификации ингредиентов с помощью «Мастер-класса».

Например, такой ингредиент, как

  • Куриная грудка
  • куриная ножка

Оба они принадлежат Мясу ( ведьма вводится в базу данных «Мастер»), и вот мой код

Ингредиент.cs

    public class Ingrident : Entity
{

    public string Name { get; set; }
    public Master Master { get; set; }
    public int MasterId { get; set; }

}
 

Мастер.cs

 public class Master : Entity

    {
        public string Name { get; set; }
        public List<Ingrident> Ingridents { get; set; } = new();
    
    }
 

IngridientAppService.cs

 public List<IngridientDto> GetIngWithParent()
    {
        var result = _ingRepository.GetAllIncluding(x => x.Master);
         //Also I try this but doesn`t work 
        // var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
        return ObjectMapper.Map<List<IngridientDto>>(result);
    }
 

IngridientDto.cs

   [AutoMap(typeof(IndexIngrident.Entities.Ingrident))]

public class IngridientDto : EntityDto
{
    public string Name { get; set; }
    public List<MasterDto> Master { get; set; }

    public int MasterId { get; set; }
 }
 

MasterDto.cs

     [AutoMap(typeof(IndexIngrident.Entities.Master))]

    public class MasterDto : EntityDto
    {
     public string Name { get; set; }
    }
 

Когда я создавал ( для последней практики ) отношения M -> M, этот подход с .getAllIncluding работал, но теперь, когда у меня есть Один ->> Много, это не сработает.

Надеюсь, кто-нибудь сможет мне помочь или, по крайней мере, дать какой-нибудь хороший намек.

Хорошего дня!

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

1. Создайте повторный проект на GitHub, который разветвляется от шаблона aspnetboilerplate/module-zero-core-шаблона .

Ответ №1:

Прямо скажем, примеры, на которые вы, вероятно, ссылаетесь (в отношении репозитория и т. Д.), Чрезмерно сложны и в большинстве случаев не то, что вы хотели бы реализовать.

Первая проблема, которую я вижу, заключается в том, что, хотя ваши сущности настроены на соотношение «1 ко многим» от Мастера к ингредиентам, ваши DTO настроены от ингредиента к мастерам, что определенно не будет отображаться должным образом.

Начните с самого простого. Избавьтесь от репозитория и избавьтесь от DTO. Я не уверен, что делает базовый класс «Сущность», но я предполагаю, что он предоставляет общее ключевое свойство, называемое «Идентификатор». Для начала я бы, наверное, тоже отказался от этого. Когда дело доходит до первичных ключей, обычно существует два подхода к именованию: каждая таблица использует PK, называемый «Id», или каждая таблица использует PK с именем таблицы, суффиксом «Id». Т. е. «Id» против «Ингредиент». Лично я нахожу, что второй вариант делает это очень ясным при соединении FKs и PKs, учитывая, что у них будет одно и то же имя.

Когда дело доходит до представления отношений с помощью свойств навигации, одной важной деталью является обеспечение связи свойств навигации с их соответствующими свойствами FK, если таковые имеются, или лучше использовать свойства тени для FKs.

Например, с вашей таблицей ингредиентов, избавляясь от базового класса сущности:

 [Table("Ingredients")]
public class Ingredient : Entity
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int IngredientId { get; set; }
    public string Name { get; set; }
    public int MasterId { get; set; }
    [ForeignKey("MasterId")]
    public virtual Master Master { get; set; }
}
 

В этом примере используются атрибуты EF, чтобы помочь EF определить, как разрешить свойства сущности для соответствующих таблиц и столбцов, а также взаимосвязь между Ингредиентом и Основным. EF может многое из этого решить по соглашению, но полезно понимать и применять его явно, потому что в конечном итоге вы столкнетесь с ситуациями, когда соглашение работает не так, как вы ожидаете.

Идентификация (первичного)ключа и указание на то, что это столбец идентификаторов, также указывает EF на то, что база данных автоматически заполнит PK. (Настоятельно рекомендуется)

Со стороны Мастера мы делаем что-то подобное:

 [Table("Masters")]
public class Master : Entity
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int MasterId { get; set; }
    public string Name { get; set; }
    [InverseProperty("Master")]
    public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
 

Снова мы обозначаем Первичный ключ, и для нашей коллекции ингредиентов мы сообщаем EF, какое свойство на другой стороне (Ингредиент) он должен использовать, чтобы связать со списком ингредиентов этого Мастера с помощью InverseProperty атрибута.

Атрибуты-это всего лишь один из вариантов настройки отношений и т. Д. Другие варианты-использовать реализующие классы конфигурации IEntityConfiguration<TEntity> (ядро EF) или настроить их как часть OnModelCreating события в DbContext. Этот последний вариант я бы рекомендовал только для очень небольших проектов, так как он может быстро начать становиться чем-то вроде метода Бога. Вы можете разделить его на вызовы различных частных методов, но тогда вы можете просто использовать IEntityConfiguration классы.

Теперь, когда вы идете за ингредиентами с его Мастером, или Мастером с его ингредиентами:

 using (var context = new AppDbContext())
{
    var ingredients = context.Ingredients
        .Include(x => x.Master)
        .Where(x => x.Master.Name.Contains("chicken"))
        .ToList();
    // or

    var masters = context.Master
        .Include(x => x.Ingredients)
        .Where(x => x.Name.Contains("chicken"))
        .ToList();

   // ...
}
 

Шаблоны репозиториев-это более продвинутая концепция, для реализации которой есть несколько веских причин, но по большей части они не являются необходимыми и являются анти-шаблоном в реализациях EF. Я считаю, что общие репозитории всегда являются анти-шаблоном для реализаций EF. Т. е. Repository<Ingredient> Главная причина не использование репозиториев, особенно общих репозиториев с EF, означает, что вы автоматически повышаете сложность своей реализации и/или ограничиваете возможности, которые EF может привнести в ваше решение. Как вы видите из своего примера, простое прохождение желаемой нагрузки через репозиторий означает запись в сложных Expression<Func<TEntity>> параметрах, и это просто покрывает желаемую загрузку. Поддержка проецирования, разбивки на страницы, сортировки и т. Д. Добавляет еще больше сложности котельной плите или ограничивает ваше решение и производительность без этих возможностей, которые EF может предоставить сразу.

Некоторые веские причины рассмотреть возможность изучения реализаций репозиториев /w EF:

  • Облегчите модульное тестирование. (Репозитории легче имитировать, чем DbContexts/наборы баз данных)
  • Централизация правил данных низкого уровня, таких как аренда, мягкое удаление и авторизация.

Некоторые плохие (хотя и очень распространенные) причины для рассмотрения репозиториев:

  • Абстрагирование кода от ссылок или знаний о зависимости от EF.
  • Абстрагирование кода, чтобы можно было заменить EF.

Проектирование в DTO или ViewModels является важным аспектом создания эффективных и безопасных решений с помощью EF. Неясно, что такое «ObjectMapper», является ли это экземпляром автоматического отображения или чем-то еще. Я бы настоятельно рекомендовал начать понимать проекцию с помощью Select синтаксиса Linq для заполнения желаемого DTO из моделей. Первое ключевое отличие при правильном использовании проекции заключается в том, что при проектировании графа объектов вам не нужно беспокоиться о быстрой загрузке связанных объектов. Любая связанная сущность / свойство, на которые ссылается ваша проекция ( Select ), будет автоматически загружена по мере необходимости. Позже, если вы захотите использовать такой инструмент, как Automapper, чтобы помочь устранить беспорядок Select операторов, вам потребуется настроить конфигурацию сопоставления, а затем использовать ProjectTo метод Automapper, а не Map . ProjectTo работает с IQueryable реализацией EF, чтобы разрешить ваше сопоставление с SQL точно так же, как Select это делает, где Map нужно будет вернуть все загруженное, чтобы заполнить соответствующие данные. ProjectTo и Select может привести к более эффективным запросам, которые могут лучше использовать преимущества индексации, чем нетерпеливая загрузка целых графов объектов. (Меньше данных по проводу между базой данных и сервером/приложением) Map по-прежнему очень полезно, например, в сценариях, когда вы хотите скопировать значения обратно из DTO в загруженный объект.

Ответ №2:

Сделай это вот так

 public class Ingrident:Entity
{

    public string Name { get; set; }
    [ForeignKey(nameof(MasterId))]
    public Master Master { get; set; }
    public int MasterId { get; set; }

}