change some logic to the repositories

This commit is contained in:
Francesco D'Amico
2025-03-30 15:56:39 +02:00
parent c48d1bb5b2
commit 2acb4b4117
14 changed files with 715 additions and 0 deletions
@@ -0,0 +1,45 @@
namespace Francesco.Recipes.World.Repositories.Category
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Category;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Microsoft.EntityFrameworkCore;
public class CategoryRepository : ICategoryRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
public CategoryRepository(FrancescosRecipesWorldDbContext context)
{
_context = context;
}
public async Task<Category> GetCategoryByIdAsync(Guid categoryId)
{
var category = await _context.Categories.FindAsync(categoryId);
return category ?? throw new InvalidDataException($"Category {categoryId} not found.");
}
public async Task<IEnumerable<Category>> GetAllCategoriesAsync()
{
return await _context.Categories.ToListAsync();
}
public async Task<IEnumerable<Recipe>> GetRecipesByCategoryAsync(Guid categoryId)
{
var category = await _context.Categories
.Include(c => c.Recipes)
.FirstOrDefaultAsync(c => c.Id == categoryId);
if (category == null)
{
throw new InvalidDataException($"Category {categoryId} not found.");
}
return category.Recipes;
}
}
}
@@ -0,0 +1,17 @@
namespace Francesco.Recipes.World.Repositories.Category
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Francesco.Recipes.World.Models.BackendModels.Category;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
public interface ICategoryRepository
{
Task<Category> GetCategoryByIdAsync(Guid categoryId);
Task<IEnumerable<Category>> GetAllCategoriesAsync();
Task<IEnumerable<Recipe>> GetRecipesByCategoryAsync(Guid categoryId);
}
}
@@ -0,0 +1,21 @@
namespace Francesco.Recipes.World.Repositories.Ingredient
{
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
public interface IIngredientRepository
{
Task<Ingredient> CreateIngredientToRecipeAsync(Recipe recipe, string ingredientName);
Task UpdateIngredientAsync(Ingredient ingredient);
Task<List<RecipeIngredient>> GetIngredientsByRecipeIdAsync(Guid recipeId);
Task<List<Ingredient>> GetIngredientsByNameAsync(string name);
Task RemoveIngredientFromRecipeAsync(Recipe recipe, Guid ingredientId);
Task<Ingredient> GetIngredientByIdAsync(Guid ingredientId);
}
}
@@ -0,0 +1,109 @@
namespace Francesco.Recipes.World.Repositories.Ingredient
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
using Microsoft.EntityFrameworkCore;
public class IngredientRepository : IIngredientRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
public IngredientRepository(
FrancescosRecipesWorldDbContext context)
{
_context = context;
}
public async Task<Ingredient> CreateIngredientToRecipeAsync(Recipe recipe, string ingredientName)
{
if (recipe is null)
{
throw new ArgumentException("Recipe not found", nameof(recipe));
}
var newIngredient = new Ingredient
{
Id = Guid.NewGuid(),
Name = ingredientName,
};
var recipeIngredient = new RecipeIngredient
{
Id = Guid.NewGuid(),
Recipe = recipe,
Ingredient = newIngredient,
Quantity = 1,
};
_context.Ingredients.Add(newIngredient);
_context.RecipeIngredients.Add(recipeIngredient);
await _context.SaveChangesAsync();
return newIngredient;
}
public async Task RemoveIngredientFromRecipeAsync(Recipe recipe, Guid ingredientId)
{
if (recipe == null)
{
throw new ArgumentNullException(nameof(recipe));
}
var ingredientToRemove = recipe.RecipeIngredients.FirstOrDefault(i => i.Id == ingredientId);
if (ingredientToRemove != null)
{
recipe.RecipeIngredients.Remove(ingredientToRemove);
await _context.SaveChangesAsync();
}
}
public async Task<List<Ingredient>> GetIngredientsByNameAsync(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return await _context.Ingredients.ToListAsync();
}
return await _context.Ingredients
.Where(i => i.Name.ToLower().Contains(name.ToLower()))
.ToListAsync();
}
public async Task<List<RecipeIngredient>> GetIngredientsByRecipeIdAsync(Guid recipeId)
{
return await _context.RecipeIngredients
.Include(ri => ri.Ingredient)
.Include(ri => ri.Unit)
.Where(ri => ri.Recipe.Id == recipeId)
.ToListAsync();
}
public async Task UpdateIngredientAsync(Ingredient ingredient)
{
if (ingredient == null)
{
throw new ArgumentNullException(nameof(ingredient));
}
var existingIngredient = await _context.Ingredients.FindAsync(ingredient.Id);
if (existingIngredient == null)
{
throw new InvalidOperationException($"Ingredient with ID {ingredient.Id} not found.");
}
existingIngredient.Name = ingredient.Name;
_context.Ingredients.Update(existingIngredient);
await _context.SaveChangesAsync();
}
public async Task<Ingredient> GetIngredientByIdAsync(Guid ingredientId)
{
var ingredient = await _context.Ingredients.FindAsync(ingredientId);
return ingredient ?? throw new InvalidDataException($"Address {ingredientId} not found.");
}
}
}
@@ -0,0 +1,9 @@
namespace Francesco.Recipes.World.Repositories.Instruction
{
using Francesco.Recipes.World.Models.BackendModels.Instruction;
public interface IInstructionRepository
{
Task<Instruction> GetInstructionAsync(Guid instructionId);
}
}
@@ -0,0 +1,21 @@
namespace Francesco.Recipes.World.Repositories.Instruction
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Instruction;
public class InstructionRepository : IInstructionRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
public InstructionRepository(FrancescosRecipesWorldDbContext context)
{
_context = context;
}
public async Task<Instruction> GetInstructionAsync(Guid instructionId)
{
var instruction = await _context.Instructions.FindAsync(instructionId);
return instruction ?? throw new InvalidDataException($"Instruction {instructionId} not found.");
}
}
}
@@ -0,0 +1,13 @@
namespace Francesco.Recipes.World.Repositories.MediaFile
{
public interface IMediaFileRepository
{
Task ReplaceInstructionImageAsync(Guid instructionId, IFormFile? newPhoto);
Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo);
Task ReplaceRecipeImageAsync(Guid recipeId, IFormFile? newPhoto);
Task UploadRecipeImageAsync(Guid recipeId, IFormFile? photo);
}
}
@@ -0,0 +1,101 @@
namespace Francesco.Recipes.World.Repositories.MediaFile
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
using Francesco.Recipes.World.Repositories.Instruction;
using Francesco.Recipes.World.Repositories.Recipe;
public class MediaFileRepository : IMediaFileRepository
{
private readonly IInstructionRepository _instructionRepository;
private readonly FrancescosRecipesWorldDbContext _context;
private readonly IRecipeRepository _recipeRepository;
public MediaFileRepository(IInstructionRepository instructionRepository, FrancescosRecipesWorldDbContext context, IRecipeRepository recipeRepository)
{
_instructionRepository = instructionRepository;
_context = context;
_recipeRepository = recipeRepository;
}
public async Task ReplaceInstructionImageAsync(Guid instructionId, IFormFile? newPhoto)
{
if (newPhoto is null)
{
throw new ArgumentNullException(nameof(newPhoto));
}
var instruction = await _instructionRepository.GetInstructionAsync(instructionId);
_context.RemoveRange(instruction.MediaFiles);
await _context.SaveChangesAsync();
await UploadInstructionImageAsync(instructionId, newPhoto);
}
public async Task ReplaceRecipeImageAsync(Guid recipeId, IFormFile? newPhoto)
{
if (newPhoto is null)
{
throw new ArgumentNullException(nameof(newPhoto));
}
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
_context.RemoveRange(recipe.MediaFiles);
await _context.SaveChangesAsync();
await UploadInstructionImageAsync(recipeId, newPhoto);
}
public async Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo)
{
if (photo is null)
{
throw new ArgumentNullException(nameof(photo));
}
var instruction = await _instructionRepository.GetInstructionAsync(instructionId);
using (var memoryStream = new MemoryStream())
{
await photo.CopyToAsync(memoryStream);
var instructionImage = new MediaFile
{
FileName = photo.FileName,
MimeType = photo.ContentType,
Data = memoryStream.ToArray(),
Instruction = instruction,
};
_context.Add(instructionImage);
await _context.SaveChangesAsync();
}
}
public async Task UploadRecipeImageAsync(Guid recipeId, IFormFile? photo)
{
if (photo is null)
{
throw new ArgumentNullException(nameof(photo));
}
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
using (var memoryStream = new MemoryStream())
{
await photo.CopyToAsync(memoryStream);
var recipeImage = new MediaFile
{
FileName = photo.FileName,
MimeType = photo.ContentType,
Data = memoryStream.ToArray(),
Recipe = recipe,
};
_context.Add(recipeImage);
await _context.SaveChangesAsync();
}
}
}
}
@@ -0,0 +1,23 @@
namespace Francesco.Recipes.World.Repositories.Recipe
{
using Francesco.Recipes.World.Models.BackendModels.Category;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.Unit;
public interface IRecipeRepository
{
Task<Recipe> GetRecipeAsync(Guid recipeId);
Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId);
Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId);
Task<IEnumerable<Recipe>> GetRecipesByNameOrIngredientAsync(string name, string ingredient);
Task<IEnumerable<Recipe>> GetRecipesByDifficultyAsync(Difficulty difficulty);
Task<Unit> AddUnitToRecipeAsync(string name, string symbol);
Task<Recipe> CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime);
}
}
@@ -0,0 +1,175 @@
namespace Francesco.Recipes.World.Repositories.Recipe
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Category;
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
using Francesco.Recipes.World.Models.BackendModels.Unit;
using Francesco.Recipes.World.Repositories.Ingredient;
using Francesco.Recipes.World.Repositories.Unit;
using Microsoft.EntityFrameworkCore;
public class RecipeRepository : IRecipeRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
private readonly IIngredientRepository _ingredientRepository;
private readonly IUnitRepository _unitRepository;
public RecipeRepository(
FrancescosRecipesWorldDbContext context, IIngredientRepository ingredientRepository, IUnitRepository unitRepository)
{
_context = context;
_ingredientRepository = ingredientRepository;
_unitRepository = unitRepository;
}
public async Task<Recipe> GetRecipeAsync(Guid recipeId)
{
var recipe = await _context.Recipes.FindAsync(recipeId);
return recipe ?? throw new InvalidDataException($"Address {recipeId} not found.");
}
public async Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId)
{
var recipe = await GetRecipeAsync(recipeId);
var unit = await _unitRepository.GetUnitByIdAsync(unitId);
if (quantity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(quantity), "Die Menge muss größer als 0 sein.");
}
var ingredients = await _ingredientRepository.GetIngredientsByNameAsync(ingredientName);
Ingredient ingredient;
if (ingredients == null || !ingredients.Any())
{
ingredient = new Ingredient
{
Id = Guid.NewGuid(),
Name = ingredientName,
};
_context.Ingredients.Add(ingredient);
await _context.SaveChangesAsync();
}
else
{
ingredient = ingredients.First();
}
var existingEntry = await _context.RecipeIngredients
.FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredient.Id);
if (existingEntry != null)
{
throw new InvalidOperationException("Das Rezept enthält diese Zutat bereits.");
}
var recipeIngredient = new RecipeIngredient
{
Recipe = recipe,
Ingredient = ingredient,
Unit = unit,
Quantity = quantity,
};
_context.Add(recipeIngredient);
await _context.SaveChangesAsync();
}
public async Task<Recipe> CreateRecipeForCategoryAsync(
Category category,
string name,
string description,
Difficulty difficulty,
int servings,
TimeSpan preparationTime,
TimeSpan cookingTime)
{
if (category == null)
{
throw new ArgumentNullException(nameof(category), "Category cannot be null.");
}
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name), "Name cannot be empty.");
}
if (servings <= 0)
{
throw new ArgumentOutOfRangeException(nameof(servings), "Servings must be greater than 0.");
}
var recipe = new Recipe
{
Id = Guid.NewGuid(),
Name = name,
Description = description,
Difficulty = difficulty,
Servings = servings,
PreparationTime = preparationTime,
CookingTime = cookingTime,
CreatedAt = DateTime.UtcNow,
Category = category,
};
_context.Recipes.Add(recipe);
await _context.SaveChangesAsync();
return recipe;
}
public async Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId)
{
var recipe = await GetRecipeAsync(recipeId);
var recipeIngredient = await _context.RecipeIngredients
.FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredientId);
if (recipeIngredient == null)
{
throw new ArgumentException("Diese Zutat ist nicht mit dem Rezept verknüpft.");
}
_context.RecipeIngredients.Remove(recipeIngredient);
await _context.SaveChangesAsync();
}
public async Task<IEnumerable<Recipe>> GetRecipesByNameOrIngredientAsync(string name, string ingredient)
{
var query = _context.Recipes
.Include(r => r.RecipeIngredients)
.ThenInclude(ri => ri.Ingredient)
.AsQueryable();
if (!string.IsNullOrWhiteSpace(name))
{
query = query.Where(r => r.Name.Contains(name, StringComparison.OrdinalIgnoreCase));
}
if (!string.IsNullOrWhiteSpace(ingredient))
{
var ingredientMatches = await _ingredientRepository.GetIngredientsByNameAsync(ingredient);
var ingredientIds = ingredientMatches.Select(i => i.Id).ToList();
if (ingredientIds.Any())
{
query = query.Where(r => r.RecipeIngredients.Any(ri => ingredientIds.Contains(ri.Ingredient.Id)));
}
}
return await query.ToListAsync();
}
public async Task<IEnumerable<Recipe>> GetRecipesByDifficultyAsync(Difficulty difficulty)
{
return await _context.Recipes
.Where(r => r.Difficulty == difficulty)
.ToListAsync();
}
public async Task<Unit> AddUnitToRecipeAsync(string name, string symbol)
{
return await _unitRepository.AddUnitAsync(name, symbol);
}
}
}
@@ -0,0 +1,19 @@
namespace Francesco.Recipes.World.Repositories.ShoppingList
{
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
public interface IShoppingListRepository
{
Task AddIngredientToShoppingListAsync(Guid recipeIngredientId, Guid shoppingListId);
Task RemoveRecipeIngredientFromShoppingListAsync(Guid recipeIngredientId);
Task RemoveRecipeFromShoppingListIfEmptyAsync(Guid recipeId, Guid shoppingListId);
Task<IEnumerable<ShoppingList>> GetShoppingListsByIngredientOfRecipeAsync(Guid ingredientId, Guid recipeId);
Task<Recipe?> GetRecipeByNameAndImageAsync(string recipeName, string imageFileName);
}
}
@@ -0,0 +1,106 @@
namespace Francesco.Recipes.World.Repositories.ShoppingList
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
using Microsoft.EntityFrameworkCore;
public class ShoppingListRepository : IShoppingListRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
public ShoppingListRepository(FrancescosRecipesWorldDbContext context)
{
_context = context;
}
public async Task AddIngredientToShoppingListAsync(Guid recipeIngredientId, Guid shoppingListId)
{
var existingEntry = await _context.RecipeIngredientsShoppingLists
.FirstOrDefaultAsync(risl => risl.RecipeIngredient.Id == recipeIngredientId && risl.ShoppingList.Id == shoppingListId);
if (existingEntry != null)
{
return;
}
var recipeIngredient = await _context.RecipeIngredients
.FirstOrDefaultAsync(ri => ri.Id == recipeIngredientId);
if (recipeIngredient == null)
{
throw new InvalidOperationException($"Recipe ingredient with ID {recipeIngredientId} not found.");
}
var shoppingList = await _context.ShoppingLists
.FirstOrDefaultAsync(sl => sl.Id == shoppingListId);
if (shoppingList == null)
{
throw new InvalidOperationException($"Shopping list with ID {shoppingListId} not found.");
}
var newEntry = new RecipeIngredientShoppingList
{
RecipeIngredient = recipeIngredient,
ShoppingList = shoppingList,
};
_context.RecipeIngredientsShoppingLists.Add(newEntry);
await _context.SaveChangesAsync();
}
public async Task RemoveRecipeIngredientFromShoppingListAsync(Guid recipeIngredientId)
{
var entry = await _context.RecipeIngredientsShoppingLists
.FirstOrDefaultAsync(risl => risl.RecipeIngredient.Id == recipeIngredientId);
if (entry != null)
{
_context.RecipeIngredientsShoppingLists.Remove(entry);
await _context.SaveChangesAsync();
}
}
public async Task RemoveRecipeFromShoppingListIfEmptyAsync(Guid recipeId, Guid shoppingListId)
{
var hasIngredients = await _context.RecipeIngredientsShoppingLists
.AnyAsync(risl => risl.RecipeIngredient.Recipe.Id == recipeId && risl.ShoppingList.Id == shoppingListId);
if (!hasIngredients)
{
var shoppingList = await _context.ShoppingLists
.Include(sl => sl.RecipeIngredientShoppingLists)
.FirstOrDefaultAsync(sl => sl.Id == shoppingListId);
if (shoppingList != null)
{
var recipeToRemove = shoppingList.RecipeIngredientShoppingLists
.FirstOrDefault(risl => risl.RecipeIngredient.Recipe.Id == recipeId);
if (recipeToRemove != null)
{
_context.RecipeIngredientsShoppingLists.Remove(recipeToRemove);
await _context.SaveChangesAsync();
}
}
}
}
public async Task<IEnumerable<ShoppingList>> GetShoppingListsByIngredientOfRecipeAsync(Guid ingredientId, Guid recipeId)
{
return await _context.ShoppingLists
.Where(sl => sl.RecipeIngredientShoppingLists.Any(risl => risl.RecipeIngredient.Ingredient.Id == ingredientId && risl.RecipeIngredient.Recipe.Id == recipeId))
.ToListAsync();
}
public async Task<Recipe?> GetRecipeByNameAndImageAsync(string recipeName, string imageFileName)
{
return await _context.Recipes
.Where(r => r.Name == recipeName && r.MediaFiles.Any(mf => mf.FileName == imageFileName))
.Include(r => r.MediaFiles.Where(mf => mf.FileName == imageFileName))
.FirstOrDefaultAsync();
}
}
}
@@ -0,0 +1,13 @@
namespace Francesco.Recipes.World.Repositories.Unit
{
using Francesco.Recipes.World.Models.BackendModels.Unit;
public interface IUnitRepository
{
Task<Unit> GetUnitByIdAsync(Guid unitId);
Task<Unit> AddUnitAsync(string name, string symbol);
Task<IEnumerable<Unit>> GetAllUnitsAsync();
}
}
@@ -0,0 +1,43 @@
namespace Francesco.Recipes.World.Repositories.Unit
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Unit;
using Microsoft.EntityFrameworkCore;
public class UnitRepository : IUnitRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
public UnitRepository(
FrancescosRecipesWorldDbContext context)
{
_context = context;
}
public async Task<Unit> GetUnitByIdAsync(Guid unitId)
{
var unit = await _context.Units.FindAsync(unitId);
return unit ?? throw new InvalidDataException($"Address {unitId} not found.");
}
public async Task<Unit> AddUnitAsync(string name, string symbol)
{
var unit = new Unit
{
Id = Guid.NewGuid(),
Name = name,
Symbol = symbol,
};
_context.Units.Add(unit);
await _context.SaveChangesAsync();
return unit;
}
public async Task<IEnumerable<Unit>> GetAllUnitsAsync()
{
return await _context.Units.ToListAsync();
}
}
}