AutoMapper отображает массив объектов в ответ

Случай: Пользователь может добавить еду, и это name, description, image и ingredients. Пользователь отправляет такую ​​полезную нагрузку

{
  "ingredients":[
    {   
        "id":1,
        "name":"Gooseberry",
        "kcal":41,
        "proteins":0.8,
        "fat":0.2,
        "carbohydrates":11.8,
        "type":null,
        "quantity":100
    }
  ],
  "title": "title",
  "image": null,
  "description":"\"<p>sdf</p>\"",
  "type":"breakfast"
}

Webapi потребляет полезную нагрузку и для Meal сохраняет данные в контексте БД, берет ingredients список и просматривает каждый элемент в foreach и добавляет mealId уникальный номер, чтобы были ссылки на ингредиенты для конкретного блюда.

Meal сущность:

    public class Meal
    {
      public int Id { get; set; }
      public string Uuid { get; set; }
      public string Title { get; set; }
      public string Description { get; set; }
      public string Image { get; set; }
      public string Author { get; set; }
      public DateTime? Created { get; set; }
      public string Type { get; set; }
      public virtual List<MealIngredientsList> Ingredients { get; set; }
    }

MealIngredientsList сущность:

    public class MealIngredientsList
    {
      public int Id { get; set; }
      public double Carbohydrates { get; set; }
      public double Fat { get; set; }
      public double Kcal { get; set; }
      public string Name { get; set; }
      public double Proteins { get; set; }
      public double Quantity { get; set; }
      public string Type { get; set; }
      public string MealId { get; set; }
      [ForeignKey("Uuid")]
      public virtual Meal Meal { get; set; }
    }

MealDto объект передачи данных:

    public class MealDto
    {
      public string Uuid { get; set; }
      public string Title { get; set; }
      public string Description { get; set; }
      public string Image { get; set; }
      public string Author { get; set; }
      public DateTime? Created { get; set; }
      public string Type { get; set; }
      public List<MealIngredientsListDto> Ingredients { get; set; }
    }

и MealIngredientsListDto:

    public class MealIngredientsListDto
    {
      public double Carbohydrates { get; set; }
      public double Fat { get; set; }
      public double Kcal { get; set; }
      public string Name { get; set; }
      public double Proteins { get; set; }
      public double Quantity { get; set; }
      public string Type { get; set; }
      public string MealId { get; set; }
    }

Итак, как я уже сказал, meal и ingredients правильно сохраняются в БД. У каждого ингредиента есть ссылка MealId на Meal, но я пытаюсь отобразить, чтобы ответ для еды содержал массив ingredients с ингредиентами, которые назначены для конкретного блюда.

Прямо сейчас ответ выглядит так, а ingredients — это пустой массив:

{
 "status": 200,
 "list": {
    "uuid": "e7206453",
    "title": "\"<p>sdf</p>\"",
    "description": "\"<p>sdf</p>\"",
    "image": null,
    "author": "982eb82e",
    "created": "2023-04-03T23:19:20.8039742+02:00",
    "type": "breakfast",
    "ingredients": []
  }
}

Мне удалось сопоставить пользовательские настройки, так что это не массив объектов ob, а объект со свойствами, и я сделал это с помощью

            CreateMap<UserProfileDto, UserSettingDto>()
            .ForMember(m => m.newsletterConsent, m => m.MapFrom(s => s.Settings))
            .ForMember(m => m.messageEmailNotification, m => m.MapFrom(s => s.Settings));

и используя .Include(u => u.Settings) на основе его DTO, и он отлично работает но я понятия не имею, как сопоставить это, чтобы получить ингредиенты в массив. Пробовал с _context.Meals.Where(x => x.Uuid == newUuid).Include(x => x.Ingredients).OrderByDescending(x => x.Created).ToList();, но не смог использовать профиль сопоставления для достижения этой цели.

У кого-нибудь есть решение/подсказка для этого?

Вы можете проверить, правильно ли сохранен MealIngredientsList в БД?

Florent Bunjaku 04.04.2023 15:43

Сохраняется правильно. Он содержит правильный MealId как ссылку на Meal.

Michał Sawicki 04.04.2023 16:22

убедитесь, что у вас есть сопоставление между MealIngredientsList и MealIngredientsListDto

Florent Bunjaku 04.04.2023 16:48

Почему между MealIngredientsList и MealIngredientsListDto? Meal и MealDto должны хранить MealIngredientsList в виде массива?

Michał Sawicki 04.04.2023 18:04

Automapper с этим справится за вас (вам также понадобится сопоставление между Meal и MealDto) docs.automapper.org/en/stable/…

Florent Bunjaku 04.04.2023 18:17

Что ж, именно поэтому я здесь - потому что понятия не имею, как к этому подступиться. ;)

Michał Sawicki 04.04.2023 18:43

Проверьте это также, надеюсь, это поможет docs.automapper.org/en/stable/Nested-mappings.html

Florent Bunjaku 04.04.2023 19:05

Я видел это раньше, но это не звонит в колокол. Но все равно спасибо.

Michał Sawicki 04.04.2023 19:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Рабочее решение (без сопоставления с DTO):

  public class Meal
  {
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] <-- mapping by MealId will cause not incrementing Id - use this
    public int Id { get; set; }
    public string MealId { get; set; }
    public string Title { get; set; }
    [NotMapped]
    public IEnumerable<MealIngredientsList> IngredientsList { get; set; }
    public string Description { get; set; }
    public string Image { get; set; }
    public string Author { get; set; }
    public DateTime? Created { get; set; }
    public string Type { get; set; }
    public virtual ICollection<MealIngredientsList> Ingredients { get; set; 
  }
}

 public class MealIngredientsList
 {
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public double Carbohydrates { get; set; }
    public double Fat { get; set; }
    public double Kcal { get; set; }
    public string Name { get; set; }
    public double Proteins { get; set; }
    public double Quantity { get; set; }
    public string Type { get; set; }
    [ForeignKey("Meal")] <- created foreign key MealId and model builder change default key for reference (by default is Id)
    public string MealId { get; }
    [JsonIgnore]  <-- to avoid infinite loop 'cause This error is caused by the circular reference between the Meal and MealIngredientsList entities. When you serialize the Meal entity to JSON, it also includes the related MealIngredientsList entities, which in turn reference the parent Meal entity, causing an infinite loop.
    public virtual Meal Meal { get; set; }
 }

Контекст БД для ссылки по определенному ключу (по умолчанию не по идентификатору)

            modelBuilder.Entity<Meal>()
            .HasKey(m => m.MealId);
        modelBuilder.Entity<MealIngredientsList>()
            .HasKey(mil => new { mil.Id, mil.MealId });

        modelBuilder.Entity<MealIngredientsList>()
            .HasOne(mil => mil.Meal)
            .WithMany(m => m.Ingredients)
            .HasForeignKey(mil => mil.MealId);

Услуга:

        var newMeal = new Meal
        {
            MealId = newUuid,
            Title = dto.Title,
            Description = dto.Description,
            Image = dto.Image,
            Author = user,
            Created = DateTime.Now.ToLocalTime(),
            Type = dto.Type,
            Ingredients = new List<MealIngredientsList>(), <- include reference to ingredients
        };

        foreach(var item in dto.IngredientsList)
        {
            var ingredients = new MealIngredientsList
            {
                Name = item.Name,
                Carbohydrates = item.Carbohydrates,
                Kcal = item.Kcal,
                Proteins = item.Proteins,
                Quantity = item.Quantity,
                Type = item.Type,
                Meal = newMeal, <-- assign reference for created Meal entity
            };
            newMeal.Ingredients.Add(ingredients);
        }

и в конце принимать все блюда с его ингредиентами

_context.Meals.Include(m => m.Ingredients).ToList();

и это работает.

Другие вопросы по теме