我如何ModelBind与MVC 3和Entity Framework code首先一个多一对多的关系? [英] How do I ModelBind a many-to-many relationship with MVC 3 and Entity Framework Code First?

查看:113
本文介绍了我如何ModelBind与MVC 3和Entity Framework code首先一个多一对多的关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在同样的问题,在未来我的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆