'Best way to update Database with List where items have been deleted
I'm working on a recipe Webapp with Asp.NET 6.0 and Razor pages. There are three database tables : recipe, ingredient and food.
Each Recipe has a Name, Preperation and list of Ingredients. I like to do most of the CRUD operation on one page for each recipe. The Create page is working fine and now I want to extend the function to also edit the data on the same page. Here you can see the Create/Edit Page.
OnGetAsync loads Recipe and Ingredients if an ID is provided:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id != null)
{
Recipe = await _context.Recipes.Where(i => i.ID == id).FirstAsync();
IngredientList = await _context.Ingredients
.Include(r => r.Food1)
.Where(i => i.RecipeID == id)
.ToListAsync();
}
return Page();
}
And then there are functions to add or delete Ingredients to the IngredientList. No changes are made to the database until SaveRecipeAsync (Button Create) is called.
public async Task<IActionResult> OnPostSaveRecipeAsync(int? id)
{
var oneRecipe = new Recipe();
// Error für ModelState Food.Name abfangen.
ClearFieldErrors(key => key.Contains("Food.Name"));
if (!ModelState.IsValid)
{
return Page();
}
oneRecipe.ID = Recipe.ID;
oneRecipe.Name = Recipe.Name;
oneRecipe.Preperation = Recipe.Preperation;
oneRecipe.Ingredients = IngredientList;
_context.Recipes.Update(oneRecipe);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Problem:
- I'm struggling with how to save the changed data from the ingredients table. The Items removed from the IngredientList are not removed in the database. Adding and editing of entries is working.
Solutions I tried:
- Delete the Ingredient directly in the context / database when deleting it in the IngredientList. Will go against the appraoch to save all at once and I need to find out if it is already in the database or only in the IngredientList.
- Delete all corresponding Ingredients connected to the recipeID and create the others again. Sounds not that smart. Regards, Cado
Solution 1:[1]
I just went with the solution to write all "deleted Ingredients" to another hidden list:
[BindProperty]
public List<Ingredient> DeletedIngredientList { get; set; } = new List<Ingredient>();
public IActionResult OnPostRemoveIngredientAsync(int id)
{
var delIngredient = IngredientList[id];
if (delIngredient.RecipeID == 0) //not good for the first ingredient...
{
DeletedIngredientList.Add(IngredientList[id]);
}
//Remove from list for view effect
IngredientList.Remove(IngredientList[id]);
ModelState.Clear();
return Page();
}
public async Task<IActionResult> OnPostSaveRecipeAsync(int? id)
{
[...]
// Delete ingredients
foreach (Ingredient i in DeletedIngredientList)
{
Ingredient delIng = _context.Ingredients.Where(ing => ing.ID == i.ID).FirstOrDefault();
if(delIng != null)
_context.Ingredients.Remove(delIng);
}
await _context.SaveChangesAsync();
[...]
}
I had to use this DeletedIngredientList on the view, and only the value which is used is available afterwards. In my case the ID:
@for (int i = 0; i < @Model.DeletedIngredientList.Count; i++)
{
<input type="hidden" asp-for="@Model.DeletedIngredientList[i].ID" />
}
Let me know if there is a more elegant solution available :)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Cado |
