2011-08-31 48 views
3

我在同樣的問題來在我的MVC 3應用程序。我有一種觀點來創建一個新產品,並且該產品可以被分配到一個或多個類別。這裏是我的EF的Code First模型類:我如何ModelBind與MVC 3和實體框架代碼第一次一個多一對多的關係?

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; } 
} 

所以,我創建了創建產品視圖視圖模型,包括產品和類別的列表:

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中是無效的,重新顯示與該類別的選擇做出完整的用戶視圖)。

[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中是無效的,並將它與下面的驗證錯誤返回編輯觀點:

The value '25,2' is invalid. // 25 and 2 being the CategoryIDs

這對我來說很有意義,它可以」將25和2綁定到實際的類別對象中,但是有沒有一種標準的方式來使用自定義的ModelBinder,這將允許我將ID轉換爲類別並將它們附加到上下文中?

回答

1

謝謝@Slauma,這讓我走上了正軌。這裏是我的創建和編輯發佈方法,詳細說明如何管理關係(編輯有點棘手,因爲它必須添加數據庫中不存在的項目,並刪除已刪除並存在於數據庫中的項目)。我向我的ProductEditViewModel添加了SelectedCategories屬性(整數列表),以保存表單的結果。

[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())); 
} 

對於編輯方法,我不得不在數據庫中查詢當前的產品,然後比較,與視圖模型。

[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())); 
} 

更代碼,我希望這會是這樣,但它實際上是利用存根,只添加/刪除發生了什麼變化相當有效。

+0

啊,在你檢查模型有效性之前,首先添加類別的好主意! – Slauma

+0

我正在嘗試做同樣的事情。儘管存根創建給我一個錯誤。 'id'不是一個SelectListItem嗎?發佈問題後,您似乎已經更改了ViewModel。 –

+0

@Omkar這是正確的,我的ViewModel現在已將SelectedCategories定義爲列表 Austin

1

什麼你可能嘗試如下:綁定到你的視圖模型,而不是Product在您的文章操作:

[HttpPost] 
public ActionResult Create(ProductEditViewModel viewModel) 
{ 
    if (ModelState.IsValid) 
    { 
     foreach (var value in viewModel.CategorySelections 
             .Where(c => c.Selected) 
             .Select(c => c.Value)) 
     { 
      // Attach "stub" entity only with key to make EF aware that the 
      // category already exists in the DB to avoid creating a new category 
      var category = new Category { CategoryID = int.Parse(value) }; 
      db.Categories.Attach(category); 

      viewModel.Product.Categories.Add(category); 
     } 
     db.Products.Add(viewModel.Product); 
     db.SaveChanges(); 
     return RedirectToAction("Index"); 
    } 

    return View(new ProductEditViewModel(
     viewModel.Product, db.Categories.ToList())); 
} 

我不知道但如果是這樣的「標準方式」。

編輯

當模特是無效的可以在我的例子不工作上面,因爲viewModel.Product.Categories集合是空的,所以你會得到在視圖中沒有所選類別的項目,而不是該用戶有項目的return情況之前選擇。

我不知道你是如何將集合綁定到視圖(你的「複選框列表」?),但是當使用允許多選擇的ListBox時,似乎有一個解決方案沿着這個答案: Challenges with selecting values in ListBoxFor。我只是在評論中詢問了Darin,如果所選項目id的列表也會在後期操作中綁定到ViewModel,並且他確認了這一點。

相關問題