2011-08-31 3 views
3

MVC 3 응용 프로그램에서 같은 문제가 발생합니다. 새로운 제품을 만들 수있는 견해가 있으며 그 제품을 하나 이상의 카테고리에 할당 할 수 있습니다. 여기 내 EF 코드 첫 번째 모델 클래스는 다음과 같습니다어떻게 ModelBind MVC 3과 Entity Framework Code First를 사용하여 다 - 대 - 다 관계를 바인딩합니까?

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를 실제 범주 객체에 바인딩하지만 ID를 범주로 변환하고 컨텍스트에 첨부 할 수있는 사용자 정의 ModelBinder를 사용하는 표준 방법이 있습니까?

답변

1

감사합니다 @Slauma, 그게 바로 궤도에 있습니다. 다음은 관계를 관리하는 방법을 자세히 설명하는 작성 및 편집 게시 방법입니다 (편집이 약간 복잡합니다. 데이터베이스에 존재하지 않는 항목을 추가하고 데이터베이스에서 제거 된 항목을 삭제해야하기 때문입니다.)). SelectedCategories 속성 (List of ints)을 ProductEditViewModel에 추가하여 양식의 결과를 보관합니다.

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

Edit 메서드의 경우 현재 제품에 대한 데이터베이스를 쿼리 한 다음이를 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())); 
} 

그것은 내가 될 것이라고 기대했다 더 많은 코드이지만, 실제로 스텁을 사용하여 만 변경된 내용을 추가/삭제 꽤 효율적이다.

+0

아, 모델 유효성을 확인하기 전에 먼저 카테고리를 추가하는 것이 좋습니다. – Slauma

+0

나는 똑같은 일을하려하고있다. 스텁 생성으로 인해 오류가 발생합니다. 'id'가 SelectListItem이 아닌가? 질문을 게시 한 후 ViewModel을 변경 한 것 같습니다. –

+0

@Omkar 맞습니다. 이제 내 ViewModel에는 List로 정의 된 SelectedCategories가 있습니다. Austin

0

몇일 전에 비슷한 문제가있었습니다. "해킹"을 사용하여 종료 됨 - MVC 3 - Binding to a Complex Type with a List type property

대체 방법을 찾으면 메시지를 남겨주세요. 나는이 "표준 방식"인 경우 비록 확실하지 않다

[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 :

1

은 당신이 시도하면 다음과 같다.

편집

모델이 viewModel.Product.Categories 컬렉션 비어 있기 때문에 위의 내 예제에서 작동하지 않을 수 유효하므로 사용자가 한 항목을보기에 더 선택한 카테고리 항목을 얻을하지 것이다 return 경우 전에 선택했습니다.

콜렉션을 뷰 ("체크 박스 목록")에 얼마나 정확하게 바인딩했는지 모르겠지만 복수 선택을 허용하는 ListBox을 사용하면이 답변 라인을 따라 해결책이있는 것으로 보입니다. Challenges with selecting values in ListBoxFor. 선택한 항목 ID 목록이 게시 작업의 ViewModel에 바인딩되고 그 내용을 확인한 경우 Darin에게 의견에 요청했습니다.

관련 문제