2015-07-20 100 views
10

假設我有複雜的視圖模型,並且每次創建ViewModel時都需要從數據庫中獲取大量數據,例如國家/地區列表,產品/類別等。在視圖模型中使用存儲庫可以嗎?

我要解決的主要問題是,當我處理POST行爲和一些TestModel與不正確的值,這將導致ModelState.IsValid發佈是false,然後我不得不返回與目前發佈的模型同樣的觀點。這迫使我再次獲取我的類別列表,因爲我在GET操作中這樣做了。這在控制器中添加了很多重複的代碼,我想刪除它。目前,我做了以下內容:

我的模型和視圖模型:

模型,實體存儲在數據庫中:

public class Category 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public IEnumerable<Category> SubCategories { get; set; } 
} 

查看模型:

public class CategoryModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class TestModel 
{ 
    [Required] 
    [MaxLength(5)] 
    public string Text { get; set; } 

    public int SelectedCategory { get; set; } 
    public IEnumerable<CategoryModel> Categories { get; set; } 
    public SelectList CategoriesList 
    { 
     get 
     { 
      var items = Categories == null || !Categories.Any() 
       ? Enumerable.Empty<SelectListItem>() 
       : Categories.Select(c => new SelectListItem 
       { 
        Value = c.Id.ToString(), 
        Text = c.Name 
       }); 

      return new SelectList(items, "Value", "Text"); 
     } 
    } 
} 

我的控制器:

public class HomeController : Controller 
{ 
    private readonly Repository _repository = ObjectFactory.GetRepositoryInstance(); 

    public ActionResult Index() 
    { 
     var model = new TestModel 
     { 
      Categories = _repository.Categories.Select(c => new CategoryModel 
      { 
       Id = c.Id, 
       Name = c.Name 
      }) 
     }; 
     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(TestModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      return RedirectToAction("Succes"); 
     } 

     model.Categories = _repository.Categories.Select(c => new CategoryModel 
     { 
      Id = c.Id, 
      Name = c.Name 
     }); 
     return View(model); 
    } 

    public ActionResult Succes() 
    { 
     return View(); 
    } 
} 

我想刪除重複的關鍵字提取和映射,基本上是這樣的代碼:

.Categories = _repository.Categories.Select(c => new CategoryModel 
      { 
       Id = c.Id, 
       Name = c.Name 
      }) 

從控制器。另外我想刪除ModelState有效性檢查,我想只在ModelState.IsValid保持控制器代碼AS CLEAN AS POSSIBLE時才執行操作。到目前爲止,我有去除ModelState有效性檢查以下解決方案:

創建自定義ValidateModelAttribute

public class ValidateModelAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     var viewData = filterContext.Controller.ViewData; 

     if(viewData.ModelState.IsValid) return; 

     viewData.Model = filterContext.ActionParameters["model"]; 
     filterContext.Result = new ViewResult 
     { 
      ViewData = viewData, 
     }; 
    } 
} 

現在行動執行前驗證模型。如果出現驗證錯誤,我們使用與最近發佈的模型相同的視圖。因此,控制器POST操作是這樣的:

[HttpPost] 
[ValidateModelAttribute] 
public ActionResult Index(TestModel model) 
{ 
    // Do some important stuff with posted data 
    return RedirectToAction("Success"); 
} 

這是很好的,但現在我的我的TestModelCategories屬性爲空,因爲我必須從數據庫中獲取的類別,並相應地映射。 因此,它是確定修改我的視圖模型看起來像這樣

public class TestModel 
{ 
    private readonly Repository _repository = ObjectFactory.GetRepositoryInstance(); 

    ... 

    public int SelectedCategory { get; set; } 
    public IEnumerable<CategoryModel> Categories { 
     get 
     { 
      return _repository.Categories.Select(c => new CategoryModel 
      { 
       Id = c.Id, 
       Name = c.Name 
      }); 
     } 
    } 

    ... 
} 

這將使我們有很乾淨的控制器,但不會是引起某種性能或建築問題?它不會打破視圖模型的單一責任原則嗎? ViewModels應該負責提取它需要的數據嗎?

+2

理想情況下,您的視圖模型不會直接與您的存儲庫進行交互。如果您需要從存儲庫中填充模型,則會發生在您的控制器中。如果您不想在單獨的控制器路由中複製類別種羣,則可以嘗試將此邏輯重構爲單獨的方法。 – timothyclifford

回答

3

這並不好。視圖模型應該主要是由服務/查詢甚至控制器填充的DTO。以前的版本沒有問題,你的控制器只是幾行代碼。

但是您的存儲庫並非真正的存儲庫,而是一個ORM。一個合適的存儲庫(在這裏它只是一些查詢對象)將直接返回視圖模型的類別列表。

關於您的自動驗證屬性,請不要重新發明輪子,別人(在本例中爲我)做了before

0

有幾流,我可以用你的方式看,

  1. 使用存儲庫,並在做實際的查詢控制器,

    var model = new TestModel 
    { 
        Categories = _repository.Categories.Select(c => new CategoryModel 
        { 
         Id = c.Id, 
         Name = c.Name 
        }) 
    }; 
    

更好的方法是要麼莫將其存儲到存儲庫中,或者更好地將其放入更合乎邏輯的層面,如服務。

隨着您的新解決方案,當您在視圖模型中引用存儲庫時,情況會更糟糕。理想情況下,我會這樣做,

public class TestService : ITestService{ 
    private IReposotory repo; 

    public TestService(IReposotory repo){ 
    this.repo = repo; 
    } 

    public TestModel GetModel() 
    { 
     return new TestModel() 
{ 
    Categories = _repository.Categories.Select(c => new CategoryModel 
    { 
     Id = c.Id, 
     Name = c.Name 
    }) 
};  
    } 
} 

public class HomeController : Controller 
{ 
    private readonly ITestService _service; 

    public HomeController (ITestService service){ 
     _service = service; 
    } 

    public ActionResult Index() 
    {   
     return View(_service.GetModel()); 
    } 

    [HttpPost] 
    public ActionResult Index(TestModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      return RedirectToAction("Succes"); 
     } 
     return View(model); 
    } 

    public ActionResult Succes() 
    { 
     return View(); 
    } 
} 
相關問題