Refactoring some Code and clean Code

This commit is contained in:
franc
2025-04-09 15:37:44 +02:00
parent d248ccb1db
commit 127a60b621
19 changed files with 132 additions and 114 deletions
@@ -1,13 +1,8 @@
namespace Francesco.Recipes.World.Controller.Category
{
using Francesco.Recipes.World.Models.BackendModels.Category;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Repositories.Category;
using Microsoft.AspNetCore.Mvc;
[ValidateAntiForgeryToken]
[Route("Category")]
public class CategoryController : Controller
{
private readonly ICategoryRepository _categoryRepository;
@@ -19,7 +14,7 @@
// GET: /Category
[HttpGet]
public async Task<ActionResult<IEnumerable<Category>>> Index()
public async Task<IActionResult> Index()
{
var categories = await _categoryRepository.GetAllCategoriesAsync();
return View(categories);
@@ -29,16 +24,16 @@
[HttpGet("{id:guid}")]
public async Task<IActionResult> Details(Guid id)
{
var category = await _categoryRepository.GetCategoryByIdAsync(id);
return View(category);
var category = await _categoryRepository.GetCategoryByIdAsync(id);
return View(category);
}
// GET: /Category/{id}/recipes
[HttpGet("{id:guid}/recipes")]
public async Task<ActionResult<IEnumerable<Recipe>>> GetRecipesByCategory(Guid id)
public async Task<IActionResult> GetRecipesByCategory(Guid id)
{
var recipes = await _categoryRepository.GetRecipesByCategoryAsync(id);
return View(recipes);
var recipes = await _categoryRepository.GetRecipesByCategoryAsync(id);
return Ok(recipes);
}
}
}
@@ -4,9 +4,7 @@
using Francesco.Recipes.World.Repositories.MediaFile;
using Microsoft.AspNetCore.Mvc;
[ValidateAntiForgeryToken]
[Route("categories/{categoryId}/Recipe")]
[Route("Category/{categoryId}/Recipe")]
public class MediaFileController : Controller
{
private readonly IMediaFileRepository _mediaFileRepository;
@@ -20,6 +18,7 @@
// POST: /UploadImage
[HttpPost("UploadImage")]
[AutoValidateAntiforgeryToken]
public async Task<IActionResult> UploadImage(Guid recipeId, IFormFile? mediaFile)
{
if (mediaFile is null)
@@ -44,5 +43,32 @@
{
return View();
}
[HttpPost("ReplaceInstructionImage")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ReplaceInstructionImage(Guid instructionId, Guid mediaFileIdToReplace, IFormFile? newPhoto)
{
if (newPhoto is null)
{
return BadRequest("Photo is required.");
}
using (var memoryStream = new MemoryStream())
{
await newPhoto.CopyToAsync(memoryStream);
var newMediaData = memoryStream.ToArray();
try
{
await _mediaFileRepository.ReplaceInstructionImageAsync(instructionId, mediaFileIdToReplace, newPhoto.FileName, newPhoto.ContentType, newMediaData);
return Ok("Image replaced successfully.");
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
}
}
}
@@ -1,6 +1,5 @@
namespace Francesco.Recipes.World.Controller.Recipe
{
using System.ComponentModel.DataAnnotations;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Repositories.Category;
using Francesco.Recipes.World.Repositories.Favorit;
@@ -14,8 +13,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
[ValidateAntiForgeryToken]
[Route("Recipe")]
public class RecipeController : Controller
{
private readonly IRecipeRepository _recipeRepository;
@@ -24,13 +21,18 @@
private readonly IIngredientRepository _ingredientRepository;
private readonly IMediaFileRepository _mediaFileRepository;
private readonly IInstructionRepository _instructionRepository;
private readonly IFavoritRepository _favoritRepository;
private readonly IFavoriteRepository _favoriteRepository;
[Display(Name = "Schwierigkeitsgrad")]
[BindProperty(SupportsGet = true)]
public Difficulty? SelectedDifficulty { get; set; }
public IReadOnlyCollection<Recipe> Recipes { get; set; }
public RecipeController(IRecipeRepository recipeRepository, IUnitRepository unitRepository, ICategoryRepository categoryRepository, IIngredientRepository ingredientRepository, IMediaFileRepository mediaFileRepository, IInstructionRepository instructionRepository, IFavoritRepository favoritRepository)
public RecipeController(
IRecipeRepository recipeRepository,
IUnitRepository unitRepository,
ICategoryRepository categoryRepository,
IIngredientRepository ingredientRepository,
IMediaFileRepository mediaFileRepository,
IInstructionRepository instructionRepository,
IFavoriteRepository favoriteRepository)
{
_recipeRepository = recipeRepository;
_unitRepository = unitRepository;
@@ -39,11 +41,9 @@
Recipes = new List<Recipe>();
_mediaFileRepository = mediaFileRepository;
_instructionRepository = instructionRepository;
_favoritRepository = favoritRepository;
_favoriteRepository = favoriteRepository;
}
public IReadOnlyCollection<Recipe> Recipes { get; set; }
// GET: /Recipe/{recipeId}/AddOrCreateIngredient
[HttpGet("{recipeId}/AddOrCreateIngredient")]
public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId)
@@ -71,24 +71,20 @@
[HttpGet("CategoryRecipes")]
public async Task<IActionResult> CategoryRecipes()
{
var categories = await _categoryRepository.GetAllCategoriesAsync();
var viewModel = new List<CategoryRecipesViewModel>();
var categories = await _categoryRepository.GetAllCategoriesWithRecipesAsync();
foreach (var category in categories)
var viewModel = categories.Select(c => new CategoryRecipesViewModel
{
var recipes = await _categoryRepository.GetRecipesByCategoryAsync(category.Id);
viewModel.Add(new CategoryRecipesViewModel
{
Category = category,
Recipes = recipes,
});
}
Category = c,
Recipes = c.Recipes,
}).ToList();
return View(viewModel);
}
// POST: /Recipe/{recipeId}/AddOrCreateIngredient
[HttpPost("{recipeId}/AddOrCreateIngredient")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId, string ingredientName, int quantity, Guid unitId)
{
if (quantity <= 0)
@@ -123,6 +119,7 @@
// POST: /Recipe/Create/{categoryId}
[HttpPost("Create/{categoryId}")]
[AutoValidateAntiforgeryToken]
public async Task<IActionResult> Create(Guid categoryId, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime, IFormFile? photo)
{
if (string.IsNullOrWhiteSpace(name))
@@ -153,6 +150,7 @@
return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
}
await _recipeRepository.CreateRecipeForCategoryAsync(categoryEntity, name, description, difficulty, servings, preparationTime, cookingTime);
var recipe = await _recipeRepository.CreateRecipeForCategoryAsync(categoryEntity, name, description, difficulty, servings, preparationTime, cookingTime);
if (photo != null)
{
@@ -176,13 +174,13 @@
ViewBag.RecipeId = recipeId;
ViewBag.IngredientId = ingredientId;
ViewBag.IngredientName = ingredient.Name;
return View();
}
// POST: /Recipe/{recipeId}/RemoveIngredient/{ingredientId}
[HttpPost("{recipeId}/RemoveIngredient/{ingredientId}")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveIngredientConfirmed(Guid recipeId, Guid ingredientId)
{
await _recipeRepository.RemoveIngredientFromRecipeAsync(recipeId, ingredientId);
@@ -219,6 +217,7 @@
// POST: /Recipe/{recipeId}/AddInstruction
[HttpPost("{recipeId}/AddInstruction")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddInstruction(Guid recipeId, string description, int number)
{
try
@@ -239,23 +238,25 @@
[HttpGet("Favorites")]
public async Task<IActionResult> Favorites()
{
var favoriteRecipes = await _favoritRepository.GetFavoriteRecipesAsync();
var favoriteRecipes = await _favoriteRepository.GetFavoriteRecipesAsync();
return View(favoriteRecipes);
}
// POST: /Recipe/AddFavorite
[HttpPost("AddFavorite")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddFavorite(Guid recipeId)
{
await _favoritRepository.AddFavoriteAsync(recipeId);
await _favoriteRepository.AddFavoriteAsync(recipeId);
return RedirectToAction("Details", new { recipeId });
}
// POST: /Recipe/RemoveFavorite
[HttpPost("RemoveFavorite")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveFavorite(Guid recipeId)
{
await _favoritRepository.RemoveFavoriteAsync(recipeId);
await _favoriteRepository.RemoveFavoriteAsync(recipeId);
return RedirectToAction("Details", new { recipeId });
}
}
@@ -1,11 +1,11 @@
namespace Francesco.Recipes.World.Controllers
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models;
using Francesco.Recipes.World.Repositories.ShoppingList;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
[Route("ShoppingList")]
public class ShoppingListController : Controller
{
private readonly IShoppingListRepository _shoppingListRepository;
@@ -18,7 +18,8 @@
}
[HttpPost("CreateOrAddIngredients")]
public async Task<IActionResult> CreateOrAddIngredients([FromBody] CreateOrAddIngredientsRequest request)
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateOrAddIngredients([FromBody] CreateOrAddIngredientRequestModel request)
{
if (request == null || request.IngredientIds == null || !request.IngredientIds.Any())
{
@@ -39,12 +40,5 @@
return Json(new { shoppingListId = shoppingList.Id });
}
public class CreateOrAddIngredientsRequest
{
public Guid RecipeId { get; set; }
public List<Guid> IngredientIds { get; set; } = new ();
}
}
}
@@ -10,7 +10,7 @@
public virtual ShoppingList ShoppingList { get; set; } = new ShoppingList();
public virtual Recipe Recipe { get; set; } = new ();
public virtual Recipe Recipe { get; set; } = new Recipe();
public virtual ICollection<RecipeIngredientShoppingList> SelectedIngredients { get; set; } = new List<RecipeIngredientShoppingList>();
}
@@ -0,0 +1,9 @@
namespace Francesco.Recipes.World.Models
{
public class CreateOrAddIngredientRequestModel
{
public Guid RecipeId { get; set; }
public List<Guid> IngredientIds { get; set; } = new ();
}
}
+2 -2
View File
@@ -39,7 +39,7 @@ builder.Services.AddScoped<IMediaFileRepository, MediaFileRepository>();
builder.Services.AddScoped<IInstructionRepository, InstructionRepository>();
builder.Services.AddScoped<IFavoritRepository, FavoritRepository>();
builder.Services.AddScoped<IFavoriteRepository, FavoritRepository>();
var app = builder.Build();
@@ -64,6 +64,6 @@ app.MapDefaultControllerRoute();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
pattern: "{controller=Category}/{action=Index}/{id?}");
app.Run();
@@ -41,5 +41,12 @@
return category.Recipes;
}
public async Task<IEnumerable<Category>> GetAllCategoriesWithRecipesAsync()
{
return await _context.Categories
.Include(c => c.Recipes)
.ToListAsync();
}
}
}
@@ -13,5 +13,7 @@
Task<IEnumerable<Category>> GetAllCategoriesAsync();
Task<IEnumerable<Recipe>> GetRecipesByCategoryAsync(Guid categoryId);
Task<IEnumerable<Category>> GetAllCategoriesWithRecipesAsync();
}
}
@@ -4,7 +4,7 @@
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Microsoft.EntityFrameworkCore;
public class FavoritRepository : IFavoritRepository
public class FavoritRepository : IFavoriteRepository
{
private readonly FrancescosRecipesWorldDbContext _context;
@@ -2,7 +2,7 @@
{
using Francesco.Recipes.World.Models.BackendModels.Recipe;
public interface IFavoritRepository
public interface IFavoriteRepository
{
Task<IEnumerable<Recipe>> GetFavoriteRecipesAsync();
@@ -37,7 +37,6 @@ namespace Francesco.Recipes.World.Repositories.Ingredient
existingRecipeIngredient.Ingredient = recipeIngredient.Ingredient;
existingRecipeIngredient.Unit = recipeIngredient.Unit;
_context.RecipeIngredients.Update(existingRecipeIngredient);
await _context.SaveChangesAsync();
}
@@ -77,7 +76,6 @@ namespace Francesco.Recipes.World.Repositories.Ingredient
existingIngredient.Name = ingredient.Name;
_context.Ingredients.Update(existingIngredient);
await _context.SaveChangesAsync();
}
@@ -1,7 +1,6 @@
namespace Francesco.Recipes.World.Repositories.Instruction
{
using Francesco.Recipes.World.Models.BackendModels.Instruction;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
public interface IInstructionRepository
{
@@ -11,6 +10,6 @@
Task<List<Instruction>> GetInstructionsByRecipeIdAsync(Guid recipeId);
Task RemoveInstructionFromRecipeAsync(Recipe recipe, Guid instructionId);
Task RemoveInstructionFromRecipeAsync(Guid recipeId, Guid instructionId);
}
}
@@ -2,7 +2,6 @@
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.Instruction;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Repositories.Recipe;
using Microsoft.EntityFrameworkCore;
@@ -25,19 +24,19 @@
public async Task<Instruction> CreateInstructionToRecipeAsync(Guid recipeId, string description, int number)
{
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
if (string.IsNullOrWhiteSpace(description))
if (string.IsNullOrWhiteSpace(description))
{
throw new ArgumentException("Description cannot be empty", nameof(description));
}
if (number <= 0)
if (number <= 0)
{
throw new ArgumentOutOfRangeException(nameof(number), "Number must be greater than 0.");
}
var newInstruction = new Instruction
var newInstruction = new Instruction
{
Id = Guid.NewGuid(),
Description = description,
@@ -45,24 +44,28 @@
Recipe = recipe,
};
_context.Instructions.Add(newInstruction);
await _context.SaveChangesAsync();
_context.Instructions.Add(newInstruction);
await _context.SaveChangesAsync();
return newInstruction;
return newInstruction;
}
public async Task RemoveInstructionFromRecipeAsync(Recipe recipe, Guid instructionId)
public async Task RemoveInstructionFromRecipeAsync(Guid recipeId, Guid instructionId)
{
if (recipe == null)
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
if (recipe.Instructions == null || !recipe.Instructions.Any())
{
throw new ArgumentNullException(nameof(recipe));
await _context.Entry(recipe)
.Collection(r => r.Instructions)
.LoadAsync();
}
var instructionToRemove = recipe.Instructions.FirstOrDefault(i => i.Id == instructionId);
var instructionToRemove = recipe.Instructions?.FirstOrDefault(i => i.Id == instructionId);
if (instructionToRemove != null)
{
recipe.Instructions.Remove(instructionToRemove);
recipe.Instructions?.Remove(instructionToRemove);
await _context.SaveChangesAsync();
}
}
@@ -2,7 +2,7 @@
{
public interface IMediaFileRepository
{
Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediafileId, IFormFile? newPhoto);
Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, string fileName, string mimeType, byte[] newMediaData);
Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo);
@@ -2,6 +2,7 @@
{
using Francesco.Recipes.World.Data;
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
using Francesco.Recipes.World.Models.BackendModels.Recipe;
using Francesco.Recipes.World.Repositories.Instruction;
using Francesco.Recipes.World.Repositories.Recipe;
@@ -18,13 +19,8 @@
_recipeRepository = recipeRepository;
}
public async Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, IFormFile? newPhoto)
public async Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, string fileName, string mimeType, byte[] newMediaData)
{
if (newPhoto is null)
{
throw new ArgumentNullException(nameof(newPhoto));
}
var instruction = await _instructionRepository.GetInstructionAsync(instructionId);
var mediaToReplace = instruction.MediaFiles.FirstOrDefault(m => m.Id == mediaFileIdToReplace);
@@ -36,22 +32,17 @@
_context.MediaFiles.Remove(mediaToReplace);
await _context.SaveChangesAsync();
using (var memoryStream = new MemoryStream())
var newMedia = new MediaFile
{
await newPhoto.CopyToAsync(memoryStream);
Id = Guid.NewGuid(),
FileName = fileName,
MimeType = mimeType,
Data = newMediaData,
Instruction = instruction,
};
var newMedia = new MediaFile
{
Id = Guid.NewGuid(),
FileName = newPhoto.FileName,
MimeType = newPhoto.ContentType,
Data = memoryStream.ToArray(),
Instruction = instruction,
};
_context.MediaFiles.Add(newMedia);
await _context.SaveChangesAsync();
}
_context.MediaFiles.Add(newMedia);
await _context.SaveChangesAsync();
}
public async Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo)
@@ -89,11 +80,6 @@
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
if (recipe == null)
{
throw new InvalidOperationException("The specified recipe does not exist.");
}
var isImage = mediaFile.ContentType.StartsWith("image/");
var isVideo = mediaFile.ContentType.StartsWith("video/");
@@ -104,21 +90,11 @@
if (isImage)
{
var existingImage = recipe.MediaFiles?.FirstOrDefault(m => m.MimeType?.StartsWith("image/") == true);
if (existingImage != null)
{
_context.MediaFiles.Remove(existingImage);
await _context.SaveChangesAsync();
}
await RemoveExistingMediaAsync(recipe, "image/");
}
else if (isVideo)
{
var existingVideo = recipe.MediaFiles?.FirstOrDefault(m => m.MimeType?.StartsWith("video/") == true);
if (existingVideo != null)
{
_context.MediaFiles.Remove(existingVideo);
await _context.SaveChangesAsync();
}
await RemoveExistingMediaAsync(recipe, "video/");
}
using var memoryStream = new MemoryStream();
@@ -136,5 +112,15 @@
_context.MediaFiles.Add(newMedia);
await _context.SaveChangesAsync();
}
private async Task RemoveExistingMediaAsync(Recipe recipe, string mediaTypePrefix)
{
var existingMedia = recipe.MediaFiles?.FirstOrDefault(m => m.MimeType?.StartsWith(mediaTypePrefix) == true);
if (existingMedia != null)
{
_context.MediaFiles.Remove(existingMedia);
await _context.SaveChangesAsync();
}
}
}
}
@@ -13,7 +13,7 @@
Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId);
Task<IEnumerable<Recipe>> GetRecipesByNameOrIngredientAsync(string name, string ingredient);
Task<IEnumerable<Recipe>> GetRecipesByNameAndIngredientAsync(string name, string ingredient);
Task<IReadOnlyCollection<Recipe>> GetRecipesByDifficultyAsync(Difficulty? difficulty);
@@ -37,6 +37,7 @@
.Include(r => r.RecipeIngredients)
.ThenInclude(ri => ri.Unit)
.Include(r => r.MediaFiles)
.Include(r => r.Instructions)
.FirstOrDefaultAsync(r => r.Id == recipeId);
}
@@ -53,7 +54,7 @@
var ingredients = await _ingredientRepository.GetIngredientsByNameAsync(ingredientName);
Ingredient ingredient;
if (ingredients == null || !ingredients.Any())
if (!ingredients.Any())
{
ingredient = new Ingredient
{
@@ -131,7 +132,6 @@
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);
@@ -144,7 +144,7 @@
await _context.SaveChangesAsync();
}
public async Task<IEnumerable<Recipe>> GetRecipesByNameOrIngredientAsync(string name, string ingredient)
public async Task<IEnumerable<Recipe>> GetRecipesByNameAndIngredientAsync(string name, string ingredient)
{
var query = _context.Recipes
.Include(r => r.RecipeIngredients)
@@ -114,7 +114,6 @@
public async Task<IEnumerable<RecipeIngredientShoppingList>> GetIngredientsOfRecipeInListAsync(Guid shoppingListRecipeId)
{
return await _context.RecipeIngredientsShoppingLists
.AsNoTracking()
.Include(i => i.RecipeIngredient)
.ThenInclude(ri => ri.Ingredient)
.Include(i => i.RecipeIngredient.Unit)
@@ -168,7 +167,6 @@
public async Task<Recipe?> GetRecipeByNameAndImageAsync(string recipeName, string imageFileName)
{
return await _context.Recipes
.AsNoTracking()
.Where(r => r.Name == recipeName && r.MediaFiles.Any(mf => mf.FileName == imageFileName))
.Include(r => r.MediaFiles.Where(mf => mf.FileName == imageFileName))
.FirstOrDefaultAsync();