Case:
User can add meal and it's name
, description
, image
and ingredients
. User sends such payload
{
"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 consumes payload and for Meal
saves data in DB context and takes ingredients
list and go through each element in foreach
and adds mealId
unique number so there is reference ingredients to specific meal.
Meal
entity:
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
entity:
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
data transfer object:
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; }
}
and 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; }
}
So Like I said meal
and ingredients
are saved correctly in db. Each ingredient has a reference MealId
to Meal
but I am trying to map so that the response for meal contains ingredients
array with ingredients that are assigned to specific meal.
Right now response looks like this and ingredients
is an empty array:
{
"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": []
}
}
I managed to map user settings so this is not an array ob objects but object with properties and I did that with
CreateMap<UserProfileDto, UserSettingDto>()
.ForMember(m => m.newsletterConsent, m => m.MapFrom(s => s.Settings))
.ForMember(m => m.messageEmailNotification, m => m.MapFrom(s => s.Settings));
and using .Include(u => u.Settings)
based on it's DTOs and it works fine
but I have no clue how to map this to get the ingredients into an array.
Tried with _context.Meals.Where(x => x.Uuid == newUuid).Include(x => x.Ingredients).OrderByDescending(x => x.Created).ToList();
but cannot manage to use mapping profile to achieve that.
Does anyone have a solution/hint for that?
Working solution (without mapping to 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; }
}
DB context to make reference by specific key(not by default by Id)
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);
Service:
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);
}
and at the end take all meals with its' ingredients
_context.Meals.Include(m => m.Ingredients).ToList();
and it works.