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 TestModel
Categories
è 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?
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