2015-07-20 8 views
10

Diciamo che ho un modello di vista complesso con molti dati come elenchi di paesi, prodotti, categorie ecc. Per i quali ho bisogno di recuperare dal database ogni volta che creo il ViewModel.È corretto utilizzare il repository nel modello di visualizzazione?

Il problema principale che voglio risolvere è che quando mi occupo di azioni POST e alcuni TestModel stato inviato con valori non corretti, che provoca ModelState.IsValid essere false, allora devo restituire la stessa vista con il modello attualmente pubblicati. Questo mi costringe a ottenere di nuovo il mio elenco di categorie, poiché l'ho fatto nell'azione GET. Questo aggiunge un sacco di codice duplicato nel controller e voglio rimuoverlo. Attualmente sto facendo le seguenti:

il mio modello e visualizzare i modelli:

modello, entità memorizzata nel database:

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

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

Visualizza i modelli:

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

Il mio regolatore:

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

Voglio rimuovere duplicati Categorie recupero e la mappatura, in fondo questo codice:

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

dal regolatore. Voglio anche rimuovere il controllo di validità ModelState, Voglio eseguire l'azione solo se ModelState.IsValid per mantenere il codice del controller AS CLEAN AS POSSIBLE. Finora ho la seguente soluzione per la rimozione di controllo ModelState validità:

Creare personalizzato 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, 
     }; 
    } 
} 

Ora modello viene convalidato prima che l'azione viene eseguita. In caso di errori di validazione, usiamo la stessa vista con lo stesso modello pubblicato di recente. Pertanto, l'azione di controllo POST assomiglia a questo:

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

Questo è bello, ma ora la mia proprietà della mia TestModelCategories è vuota, perché devo prendere le categorie dal database, e la mappa di conseguenza. Quindi è OK per modificare la mia vista del modello di simile a questa:

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

    ... 
} 

Questo ci permetterà di avere controllo molto pulito, ma non sarebbe causare qualche tipo di prestazioni o problemi di architettura? Non romperebbe il principio di responsabilità unica per i modelli di vista? ViewModels dovrebbe essere responsabile per il recupero dei dati di cui ha bisogno?

+2

Idealmente no, i tuoi modelli di vista non interagirebbero direttamente con il tuo repository. Se è necessario popolare il modello dal repository, ciò avverrebbe nel controller. Se non si desidera duplicare la popolazione delle categorie in percorsi di controller separati, è possibile provare a ritrasferire questa logica in un metodo separato. – timothyclifford

risposta

3

Non va bene. il modello di visualizzazione dovrebbe essere principalmente un DTO popolato da un servizio/query o persino dal controller. Non ci sono stati problemi con la versione precedente, il tuo controller è solo un paio di linee di codice.

Ma il tuo repository non è realmente un repository, è un ORM.Un repository appropriato (beh qui sarebbe solo un oggetto di query) restituirebbe direttamente l'elenco di Categorie per il modello di visualizzazione.

Informazioni sull'attributo di convalida automatica, non reinventare la ruota, qualcun altro (in questo caso io) lo ha fatto before.

3

No, non è necessario inserire il riferimento e la logica del repository nei modelli di visualizzazione. Suppongo che l'unica cosa di cui hai bisogno sia di poter ricostruire il modello se la validazione fallisce. Si può provare una delle convalida ModelState automatizzata, ad esempio:

http://benfoster.io/blog/automatic-modelstate-validation-in-aspnet-mvc

0

Ci sono alcuni flussi che posso vedere con il vostro approccio,

  1. Usare il repository e fare interrogazione reale nel controllore,

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

migliore approccio è o a mo questo al repository o meglio metterlo in un livello più logico come i servizi.

Con la tua nuova soluzione è ancora peggio quando fai riferimento al repository all'interno del modello di visualizzazione. Idealmente lo farei in questo modo,

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