2

I made an API for learning using .NET.

This is a simple API where I have Pizzas and Ingredients. I want to get all the pizzas in the database with their ingredients. Something Like this:

[
  {
    "id": 2,
    "name": "Pizza XYZ",
    "price": 4.5,
    "isPizzaOfTheWeek": false,
    "amount": 15,
    "pizzaIngredients": [
      {
         id: 1,
         name: 'Onion',
         price: '2.00',
         cost: '8.00'
      }
    ]
  },
]

My entities are these:

public class Pizza
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    [Column(TypeName = "decimal(5,2)")]
    public decimal Price { get; set; }      
    public bool IsPizzaOfTheWeek { get; set; }
    public int Amount { get; set; }
    
    public List<PizzaIngredient> PizzaIngredients { get; set; } = null!;
}

public class Ingredient
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    
    [Column(TypeName = "decimal(5,2)")]
    public decimal Price { get; set; }      // Price per 100 g

    public List<PizzaIngredient> PizzaIngredients { get; set; } = null!;
}

public class PizzaIngredient
{
    [Key]
    public int PizzaId { get; set; }
    public Pizza Pizza { get; set; } = null!;
    
    [Key]
    public int IngredientId { get; set; }
    public Ingredient Ingredient { get; set; } = null!;
    
    [Column(TypeName = "decimal(5,2)")]       
    public decimal Cost { get; set; } // Total Cost of this ingredient for this pizza
}

The problem is that I don't know how to do this.

I try using : var pizzas = await _context.Pizza.Include(p => p.PizzaIngredients).ThenInclude(pi => pi.Ingredient).ToListAsync();

This function brings me all the ingredients data but it's also bringing me repetitive data because "ingredients" have property which is also a List of PizzaIngredients.

I hope everything is clear. If more information is needed, I will write it.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Diunlen
  • 33
  • 4
  • Answer would be almost the same as one called "my personal approach" [here](https://stackoverflow.com/a/62985944/2501279) - create a DTO/Model to represent exactly what you want to return here and map fetched data to it. – Guru Stron Dec 05 '22 at 16:40
  • I created the Ingredient Model with the data that I want. Now, How can I fill this new Model with the information in the database? – Diunlen Dec 05 '22 at 17:33
  • Use `Select` in the query a look into tool like automapper. – Guru Stron Dec 05 '22 at 18:23

1 Answers1

0

Usually I advise to remove the mapping table if there is no need to have additional properties in it. Move the price to the pizza because it applies to it. Unless you make a order table. Ypu don't need to do anything in FluentApi for that many-to-many

Models

public class Pizza
{
    public Pizza()
    {
       //Initialize the collection in Hashset for fast search
       this.Ingredients = new HashSet<Ingredients>();
    }

    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    [Column(TypeName = "decimal(5,2)")]
    public decimal Price { get; set; }      
    public bool IsPizzaOfTheWeek { get; set; }
    public int Amount { get; set; }
    public decimal Cost { get; set; }
    
    //Use a collection instead of a list to be able to convert it to HashSet
    public virtual ICollection<Ingredient> Ingredients { get; set; }
}

public class Ingredient
{
    public Ingredient()
    {
       //Initialize the collection in Hashset for fast search
       this.Pizzas = new HashSet<Pizza>();
    }

    [Key]
    public int Id { get; set; }
    public string Name { get; set; } = null!;
    
    [Column(TypeName = "decimal(5,2)")]
    public decimal Price { get; set; }      // Price per 100 g

    //Use a collection instead of a list to be able to convert it to HashSet
    public virtual ICollection<Pizza> Pizzas { get; set; }
}

//DtoModel
public class PizzaDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }      
    public bool IsPizzaOfTheWeek { get; set; }
    public int Amount { get; set; }
    public decimal Cost { get; set; } //Total cost of ingredients prices
    
    public List<Ingredient> Ingredients { get; set; };
}

Service

//Qerry
//All pizza ids you want with single query
var ids = new List<int> { 1, 4, 6, 22 };

var pizzas = await this.context.Pizzas
    .Where(x => ids.Contains(x.Id)) // Pizzas by ids
    .Where(x => x.Ingredients.Any(x => x.Name == name)) //Pizzas by ingredient
    .Select(x => new PizzaDto
    {
       Id = x.Id,
       Name = x.Name,
       Price = x.Price,
       IsPizzaOfTheWeek = x.IsPizzaOfTheWeek,
       Amount = x.Amount ,
       Cost = x.Ingredients.Sum(x => x.Price),
       Ingredients = x.Ingredients,
    }).ToListAsync();


//Save
var pizza = new Pizza
{
   Name = name,
   Price = price,
   IsPizzaOfTheWeek = isPizzaOfTheWeek,
   Amount = amount ,
   Cost = ingredients.Sum(x => x.Price),
   Ingredients = ingredients, //List of ingreadients
};

await this.context.AddAsync(pizza);
await this.context.SaveChangesAsync();
Darkk L
  • 889
  • 1
  • 4
  • 14
  • I want to store the price of each ingredient in each pizza. Because with that price I can calculate how much of an ingredient is in the pizza. For example, if the price of an ingredient is 0.50 per 100g and I want 500g for a pizza, the cost of this ingredient for that pizza would be 2.50. For that reason, I store the Cost in PizzaIngredient. – Diunlen Dec 05 '22 at 21:57
  • Ok , But where do you keep the amount of the ingredient for the current pizza ? – Darkk L Dec 06 '22 at 06:12
  • If you add Order entity you can store all you need in it, like: ingredients, client, total cost, additionals, order time. For example, your client may ask for additional chees. – Darkk L Dec 06 '22 at 06:20
  • I store the amount of an ingredient for the current pizza in the PizzaIngredients Entity. Specifically, in the Cost attribute. If the cost of that ingredient (say onion) is 8.00. That means the pizza has 400g of onion because each 100g costs 2.00. – Diunlen Dec 06 '22 at 21:41
  • Sorry, but this is not my example. I can delete it if it doesn't work for you at all – Darkk L Dec 07 '22 at 06:05