我如何ModelBind与MVC 3和Entity Framework code首先一个多一对多的关系? [英] How do I ModelBind a many-to-many relationship with MVC 3 and Entity Framework Code First?
问题描述
我在同样的问题,在未来我的MVC 3应用程序。我有一个视图以创建一个新的产品,并且产品可以被分配到一个或多个类别。这里是我的EF code首先模型类:
I'm coming across the same problem in my MVC 3 applications. I've got a view to create an new product and that product can be assigned to one or more categories. Here are my EF Code First Model Classes:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
所以,我创建了创建产品视图视图模型,包括产品和类别的列表:
So, I create a view model for the create product view and include the product and a list of the categories:
public class ProductEditViewModel
{
public Product Product { get; set; }
public List<SelectListItem> CategorySelections { get; set; }
public ProductEditViewModel(Product product, List<Category> categories)
{
this.Product = product;
CategorySelections = categories.Select(c => new SelectListItem()
{
Text = c.Name,
Value = c.CategoryID.ToString(),
Selected = (product != null ? product.Categories.Contains(c) : false)
}).ToList();
}
}
所以,我渲染为名称的输入和复选框为每个类别的列表的视图(名为Product.Categories)。当我得到的形式调回我要保存的产品,其相关的类别(或如的ModelState是无效的,重新显示与类别选择用户所做的完整视图)。
So, I render a view with an input for the name and a list of checkboxes for each category (named "Product.Categories"). When my form gets posted back I want to save the product with its associated categories (or if the ModelState is invalid, to redisplay the view with the category selections the user made intact).
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(new ProductEditViewModel(product, db.Categories.ToList()));
}
当我这样做,并选择一个或多个类别的ModelState中是无效的,并将它与下面的验证错误返回编辑观点:
When i do that and select one or more categories, the ModelState is invalid and it returns the Edit view with the following validation error:
值为'25 2'是无效的。 // 25和2作为CategoryIDs
The value '25,2' is invalid. // 25 and 2 being the CategoryIDs
这是有道理的,我认为这不能结合25和2成实际类别对象,但有使用自定义的模型绑定器,让我来标识转化为分类,并将其附加到上下文的标准方式?
It makes sense to me that it can't bind 25 and 2 into actual category objects, but is there a standard way to use a custom ModelBinder that would allow me to translate IDs into Categories and attach them to the context?
推荐答案
感谢@Slauma,这让我在正确的轨道上。这是我的创建和编辑方法后,详细说明如何管理关系(编辑是有点棘手,因为它有添加不存在于数据库中,并删除已被删除的项目,并在数据库中确实存在项目)。我添加了一个SelectedCategories属性(整数列表)我ProductEditViewModel持有从形式的结果。
Thanks @Slauma, that got me on the right track. Here is my Create and Edit post methods that detail how to manage the relationships (the edit is a bit trickier, because it has to add items that don't exist in the database and delete items that have been removed and do exist in the database). I added a SelectedCategories property (List of ints) to my ProductEditViewModel to hold the result from the form.
[HttpPost]
public ActionResult Create(ProductEditViewModel)
{
viewModel.Product.Categories = new List<Category>();
foreach (var id in viewModel.SelectedCategories)
{
var category = new Category { CategoryID = id };
db.Category.Attach(category);
viewModel.Product.Categories.Add(category);
}
if (ModelState.IsValid)
{
db.Products.Add(viewModel.Product);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(new ProductEditViewModel(viewModel.Product, GetCategories()));
}
有关编辑的方法,我不得不在数据库中查询当前的产品,然后比较,与视图模型。
For the Edit method I had to query the database for the current product and then compare that with the viewModel.
[HttpPost]
public ActionResult Edit(ProductEditViewModel viewModel)
{
var product = db.Products.Find(viewModel.Product.ProductID);
if (ModelState.IsValid)
{
UpdateModel<Product>(product, "Product");
var keys = product.CategoryKeys; // Returns CategoryIDs
// Add categories not already in database
foreach (var id in viewModel.SelectedCategorys.Except(keys))
{
var category = new Category { CategoryID = id }; // Create a stub
db.Categorys.Attach(category);
product.Categories.Add(Category);
}
// Delete categories not in viewModel, but in database
foreach (var id in keys.Except(viewModel.SelectedCategories))
{
var category = product.Categories.Where(c => c.CategoryID == id).Single();
product.Categories.Remove(category);
}
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
// Update viewModel categories so it keeps users selections
foreach (var id in viewModel.SelectedCategories)
{
var category = new Category { CategoryID = id }; // Create a stub
db.Categories.Attach(category);
viewModel.Product.Categories.Add(category);
}
}
return View(new ProductEditViewModel(viewModel.Product, GetCategories()));
}
这是更code,我希望它会是这样,但它实际上是pretty高效利用存根,只添加/删除了什么变化。
It is more code that I was hoping it would be, but it is actually pretty efficient with using the stubs and only adding/deleting what has changed.
这篇关于我如何ModelBind与MVC 3和Entity Framework code首先一个多一对多的关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!