创建菜谱时,我的 ASP.NET Core MVC 应用程序遇到问题。该问题涉及无法将配方成分保存到数据库中。虽然主要配方详细信息已成功保存,但
RecipeIngredients
部分仍为空。
以下是我的代码相关部分的概述:
在
CreateModel
类中,我有一个方法 OnPostAsync
可以处理配方成分的保存。 Recipe.RecipeIngredient
属性应填充所选成分和数量。
在 HTML 表单中,我有一个部分,用户可以从下拉列表中选择成分,输入数量,然后根据所选成分自动选择单位。使用 JavaScript 动态添加成分。
我有一个
RecipeIngredient
类代表食谱和成分之间的关系。此类包括 IngredientID
、Quantity
,以及对 Recipe
和 Ingredient
实体的引用。
我已验证
RecipeIngredientsInput
列表已在 OnGet
方法中正确初始化。
JavaScript 函数
addIngredientRow()
似乎可以正确地将成分添加到表单中。
问题似乎与数据未保存到数据库中的
RecipeIngredient
表有关。
调试:我尝试通过在
OnPostAsync
方法中放置断点来调试应用程序,并且 RecipeIngredientsInput
中的值似乎没有保存到数据库中。
如果有人遇到类似的问题或了解可能导致问题的原因,我将非常感谢您的帮助。此外,如果您需要更多详细信息或代码片段,请告诉我。
食谱
Create.cshtml.cs
:
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc;
using proiect1.Models;
using System;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authorization;
namespace proiect1.Pages.Recipes
{
[Authorize(Roles = "Admin")]
public class CreateModel : RecipeCategoriesPageModel
{
private readonly proiect1.Data.proiect1Context _context;
private readonly IWebHostEnvironment _environment;
public CreateModel(proiect1.Data.proiect1Context context, IWebHostEnvironment environment)
{
_context = context;
_environment = environment;
}
[BindProperty]
public Recipe Recipe { get; set; }
public List<SelectListItem> AllIngredients { get; set; }
public List<RecipeIngredientInputModel> RecipeIngredientsInput { get; set; }
[BindProperty]
public RecipePhoto RecipePhoto { get; set; }
[BindProperty]
public IFormFile Photo { get; set; }
public IActionResult OnGet()
{
var recipe = new Recipe();
recipe.RecipeCategories = new List<RecipeCategory>();
recipe.RecipeIngredients = new List<RecipeIngredient>();
PopulateAssignedCategoryData(_context, recipe);
// Recipe.PublishingDate = DateTime.Now;
PopulateIngredientsDropdown();
RecipeIngredientsInput = new List<RecipeIngredientInputModel>();
return Page();
}
private void PopulateIngredientsDropdown()
{
var ingredients = _context.Ingredient.Select(i => new SelectListItem
{
Value = i.Id.ToString(),
Text = i.Name,
Group = new SelectListGroup { Name = i.Unit }
})
.ToList();
AllIngredients = ingredients;
}
public async Task<IActionResult> OnPostAsync(string[] selectedCategories)
{
if (selectedCategories != null)
{
Recipe.RecipeCategories = selectedCategories
.Select(cat => new RecipeCategory { CategoryId = int.Parse(cat) })
.ToList();
}
if (RecipeIngredientsInput != null)
{
Recipe.RecipeIngredients = RecipeIngredientsInput
.Select(input => new RecipeIngredient
{
IngredientID = input.IngredientID,
Quantity = input.Quantity
})
.ToList();
}
if (Photo != null && Photo.Length > 0)
{
var directoryPath = Path.Combine(_environment.WebRootPath, "Recipe Photos");
// Verifică dacă directorul există, altfel creează-l
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
var photoPath = Path.Combine(directoryPath, Photo.FileName);
using (var stream = new FileStream(photoPath, FileMode.Create))
{
await Photo.CopyToAsync(stream);
}
RecipePhoto recipePhoto = new RecipePhoto
{
Image = Photo,
ImagePath = Photo.FileName
};
_context.RecipePhotos.Add(recipePhoto);
Recipe.Photo = Photo.FileName;
}
Recipe.PublishingDate = DateTime.Now;
_context.Recipe.Add(Recipe);
await _context.SaveChangesAsync();
Recipe.Photo = Photo.FileName;
return RedirectToPage("./Index");
}
}
public class RecipeIngredientInputModel
{
public int IngredientID { get; set; }
public decimal Quantity { get; set; }
}
}
Recipe.cshtml
:
@page
@model proiect1.Pages.Recipes.CreateModel
@{
ViewData["Title"] = "Create";
}
<script>
function addIngredientRow() {
var table = document.getElementById('ingredientTable');
var row = table.insertRow(-1);
// C1
var select = document.createElement('select');
select.name = 'RecipeIngredientsInput[' + table.rows.length + '].IngredientID';
select.className = 'form-control';
//data-unit
@foreach (var ingredient in Model.AllIngredients)
{
<text>
var option = document.createElement('option');
option.value = "@ingredient.Value";
option.text = "@ingredient.Text";
option.setAttribute('data-unit', "@ingredient.Group.Name");
select.add(option);
</text>
}
row.insertCell(0).appendChild(select);
// C2
var quantityInput = document.createElement('input');
quantityInput.type = 'text';
quantityInput.name = 'RecipeIngredientsInput[' + table.rows.length + '].Quantity';
quantityInput.className = 'form-control';
row.insertCell(1).appendChild(quantityInput);
// C3
var unitText = document.createElement('span');
unitText.className = 'form-control';
unitText.innerHTML = '';
row.insertCell(2).appendChild(unitText);
select.addEventListener('change', function () {
unitText.innerHTML = this.options[this.selectedIndex].getAttribute('data-unit');
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
addIngredientRow();
});
} else {
addIngredientRow();
}
</script>
<form method="post" enctype="multipart/form-data">
<h1>Create</h1>
<h4>Recipe</h4>
<hr />
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label asp-for="Recipe.Title" class="control-label"></label>
<input asp-for="Recipe.Title" class="form-control" />
<span asp-validation-for="Recipe.Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Recipe.Description" class="control-label"></label>
<input asp-for="Recipe.Description" class="form-control" />
<span asp-validation-for="Recipe.Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Recipe.Instructions" class="control-label"></label>
<input asp-for="Recipe.Instructions" class="form-control" />
<span asp-validation-for="Recipe.Instructions" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Recipe.RecipeIngredients" class="control-label">Ingredients</label>
<table id="ingredientTable">
<tr>
<th>Select Ingredient</th>
<th>Enter Quantity</th>
<th>Unit</th>
</tr>
</table>
<button type="button" onclick="addIngredientRow()">Add Ingredient</button>
<span asp-validation-for="Recipe.RecipeIngredients" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Recipe.Photo" class="control-label"></label>
<div class="custom-file">
<input type="file" asp-for="Photo" class="custom-file-input" accept="image/*" />
</div>
<span asp-validation-for="Photo" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Recipe.PublishingDate" class="control-label"></label>
<input asp-for="Recipe.PublishingDate" type="hidden" />
<span asp-validation-for="Recipe.PublishingDate" class="text-danger"></span>
</div>
<div class="form-group">
<div class="table">
<table>
<tr>
@{
int cnt = 0;
foreach (var cat in Model.AssignedCategoryDataList)
{
if (cnt++ % 3 == 0)
{
@:</tr><tr>
}
@:<td>
<input type="checkbox"
name="selectedCategories"
value="@cat.CategoryID"
@(Html.Raw(cat.Assigned ?"checked=\"checked\"" : "")) />
@cat.CategoryID @: @cat.Name
@:</td>
}
@:</tr>
}
</table>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
</form>
Recipe.cs
:
using System.ComponentModel.DataAnnotations;
namespace proiect1.Models
{
public class Recipe
{
public int Id { get; set; }
[Display(Name = "Recipe Title")]
public string? Title { get; set; }
[Display(Name = "Recipe Description")]
public string? Description { get; set; }
[Display(Name = "Recipe Instructions")]
public string? Instructions { get; set; }
// [Display(Name = "Recipe Photo")]
public string? Photo { get; set; }
[DataType(DataType.Date)]
public DateTime PublishingDate { get; set; }
public ICollection<RecipeIngredient>? RecipeIngredients { get; set; }
public int? SavedRecipeId { get; set; }
public SavedRecipe? SavedRecipe { get; set; }
public ICollection<RecipeCategory>? RecipeCategories { get; set; }
public int? UserId { get; set; }
}
}
Ingredient.cs
:
namespace proiect1.Models
{
public class Ingredient
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Unit { get; set; } //grame, lingurita, ml,...
public ICollection<RecipeIngredient>? RecipeIngredients { get; set; }
}
}
RecipeIngredient.cs
:
using proiect1.Models;
using System.ComponentModel.DataAnnotations.Schema;
namespace proiect1.Models
{
public class RecipeIngredient
{
public int Id { get; set; }
[ForeignKey("Recipe")]
public int RecipeID { get; set; }
public Recipe? Recipe { get; set; }
public int IngredientID { get; set; }
public Ingredient? Ingredient { get; set; }
[Column(TypeName = "decimal(6, 0)")]
public decimal Quantity { get; set; }
}
}
并不是数据库没有存储成分,而是数据没有正确绑定。有两个地方需要修改。
- Create.cshtml.cs需要[bindproperty]属性
[BindProperty] //1.the attribute needs to be added on RecipeIngredientsInput
public List<RecipeIngredientInputModel> RecipeIngredientsInput { get; set; }
- cshtml中基于行的数字可能会导致计数错误,请将其替换为Create.cshtml中的索引。
<script>
var index = 0;
function addIngredientRow() {
var table = document.getElementById('ingredientTable');
var row = table.insertRow(-1);
// C1
var select = document.createElement('select');
select.name = 'RecipeIngredientsInput[' + index + '].IngredientID';
select.className = 'form-control';
//data-unit
@foreach (var ingredient in Model.AllIngredients)
{
<text>
var option = document.createElement('option');
option.value = "@ingredient.Value";
option.text = "@ingredient.Text";
option.setAttribute('data-unit', "@ingredient.Group.Name");
select.add(option);
</text>
}
row.insertCell(0).appendChild(select);
// C2
var quantityInput = document.createElement('input');
quantityInput.type = 'text';
quantityInput.name = 'RecipeIngredientsInput[' + index + '].Quantity';
quantityInput.className = 'form-control';
row.insertCell(1).appendChild(quantityInput);
// C3
var unitText = document.createElement('span');
unitText.className = 'form-control';
unitText.innerHTML = '';
row.insertCell(2).appendChild(unitText);
select.addEventListener('change', function () {
unitText.innerHTML = this.options[this.selectedIndex].getAttribute('data-unit');
});
index++;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
addIngredientRow();
});
} else {
addIngredientRow();
}
</script>