多对多关系基本示例 (MVC3)
我有一个 MVC3 C# 项目,其中有 FoodItem 和 FoodItemCategory 的模型。这两个模型如下所示:
public class FoodItem
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<FoodItemCategory> Categories { get; set; }
public DateTime CreateDate { get; set; }
}
public class FoodItemCategory {
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<FoodItem> FoodItems { get; set; }
public DateTime CreateDate { get; set; }
}
我有一个最初从脚手架生成的 _CreateOrEdit.cshtml 视图,我将其修改为包含所有类别,并选中食品所属的框。一种食品可以具有许多或所有类别。该视图如下所示:
@model StackOverFlowIssue.Models.FoodItem
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Categories)
</div>
<div class="editor-field">
@foreach (var FoodItemCategory in (IEnumerable<StackOverFlowIssue.Models.FoodItemCategory>)ViewBag.Categories){
<input type="checkbox" name="FoodItemCategoryId" value="@FoodItemCategory.ID"
@foreach(var c in Model.Categories){
if(c.ID == FoodItemCategory.ID){
@String.Format("checked=\"checked\"")
}
}
/>
@FoodItemCategory.Name
<br />
}
</div>
@Html.Hidden("CreateDate", @DateTime.Now)
正如您所看到的,我有一个嵌套循环,它为每个类别创建一个复选框,在创建每个类别时,我会循环遍历并检查模型的“类别”属性中的特定类别。如果存在,我设置复选框的选中属性。如果您选中一个框并单击“保存”,在控制器上的 HttpPost 操作上,我将执行以下操作:
[HttpPost]
public ActionResult Edit(FoodItem foodItem)
{
if (ModelState.IsValid)
{
var cList = Request["CategoryId"].Split(',');
List<FoodItemCategory> categories = new List<FoodItemCategory>();
foreach (var c in cList) {
var ci = Convert.ToInt32(c);
FoodItemCategory category = context.FoodItemCategories.Single(x => x.ID == ci);
categories.Add(category);
}
context.Entry(foodItem).State = EntityState.Modified;
restaurant.Categories = categories;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(foodItem);
}
我能够保存类别一次。如果我返回视图,然后单击“保存”,我会收到以下错误:
无法将重复值插入到唯一索引中。 [ 表名称 = >FoodItemCategoryFoodItems,约束名称 = PK_FoodItemCategoryFoodItems_00000000000000A8 ] 描述:执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其在代码中的来源的更多信息。
异常详细信息:System.Data.SqlServerCe.SqlCeException:无法将重复值插入到唯一索引中。 [ 表名称 = FoodItemCategoryFoodItems,约束名称 = >PK_FoodItemCategoryFoodItems_00000000000000A8 ]
来源错误:
第 97 行:context.Entry(foodItem).State = EntityState.Modified; 第 98 行:foodItem.Categories = 类别; 第 99 行:context.SaveChanges(); 第 100 行: return RedirectToAction("Index"); 第 101 行: }
不确定这是否重要,但我正在使用 SQLServer Compact Edition 4。我的处理方式正确吗?对于这样的事情,正常的编码实践是什么?我知道同样的情况每天都会发生,因为在博客等许多情况下都使用相同的关系模型。
I have a MVC3 C# project that I have a model of FoodItem and FoodItemCategory. The two models are shown as follows:
public class FoodItem
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<FoodItemCategory> Categories { get; set; }
public DateTime CreateDate { get; set; }
}
public class FoodItemCategory {
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<FoodItem> FoodItems { get; set; }
public DateTime CreateDate { get; set; }
}
I have a _CreateOrEdit.cshtml view that was initially generated from the Scaffolder and I modified it to include all of the Categories and check the box that the food item belongs to. A food item could have many or all of the categories. The view looks like the following:
@model StackOverFlowIssue.Models.FoodItem
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Description)
@Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Categories)
</div>
<div class="editor-field">
@foreach (var FoodItemCategory in (IEnumerable<StackOverFlowIssue.Models.FoodItemCategory>)ViewBag.Categories){
<input type="checkbox" name="FoodItemCategoryId" value="@FoodItemCategory.ID"
@foreach(var c in Model.Categories){
if(c.ID == FoodItemCategory.ID){
@String.Format("checked=\"checked\"")
}
}
/>
@FoodItemCategory.Name
<br />
}
</div>
@Html.Hidden("CreateDate", @DateTime.Now)
As you can see, I have a nested loop that creates a checkbox for each category, and while it is creating each category, I loop through and check for that particular category in the Categories property of my model. If it exists, I set the checked property of the checkbox. If you check a box and click save, on the HttpPost action on the controller, I am performing the following:
[HttpPost]
public ActionResult Edit(FoodItem foodItem)
{
if (ModelState.IsValid)
{
var cList = Request["CategoryId"].Split(',');
List<FoodItemCategory> categories = new List<FoodItemCategory>();
foreach (var c in cList) {
var ci = Convert.ToInt32(c);
FoodItemCategory category = context.FoodItemCategories.Single(x => x.ID == ci);
categories.Add(category);
}
context.Entry(foodItem).State = EntityState.Modified;
restaurant.Categories = categories;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(foodItem);
}
I am able to save the categories one time. If I go back into the view, and just click save, I receive the following error:
A duplicate value cannot be inserted into a unique index. [ Table name = >FoodItemCategoryFoodItems,Constraint name = PK_FoodItemCategoryFoodItems_00000000000000A8 ]
Description: An unhandled exception occurred during the execution of the current web request. Please >review the stack trace for more information about the error and where it originated in the code.Exception Details: System.Data.SqlServerCe.SqlCeException: A duplicate value cannot be inserted into >a unique index. [ Table name = FoodItemCategoryFoodItems,Constraint name = >PK_FoodItemCategoryFoodItems_00000000000000A8 ]
Source Error:
Line 97: context.Entry(foodItem).State = EntityState.Modified;
Line 98: foodItem.Categories = categories;
Line 99: context.SaveChanges();
Line 100: return RedirectToAction("Index");
Line 101: }
Not sure if it matters but I am using SQLServer Compact Edition 4. Am I going about this the right way? What is the normal coding practice for something like this? I know this same situation occurs daily since this same relationship model is used in many situations like blogs, etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
尝试这样的事情(未经测试):
这看起来效率很低,但因为您正在处理多对多关系,可以在视图中添加或删除关系,所以这是唯一的方法。原因是:
更多关于分离对象图和处理关系此处描述< /a>.它是关于 ObjectContext API 的,但 DbContext API 只是它的包装器,因此相同的限制仍然存在。
Try something like this (untested):
This can look pretty inefficient but because you are dealing with many-to-many relation where relations can be added or removed in the view it is the only way to do that. The reasons are:
More about detached object graphs and processing relations is described here. It is about ObjectContext API but DbContext API is just wrapper around that so the same limitations still exist.
除了Ladislav的答案之外,您还可以使用 http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx 这使得:
In addition to Ladislav's answer, you can get rid of the Request[].split() part by using http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx which makes: