Załóżmy, że mam złożony model widoku z dużą ilością danych, takich jak listy krajów, produkty, kategorie itp., Które muszę pobrać z bazy danych za każdym razem, gdy utworzę ViewModel.Czy można używać repozytorium w widoku modelu?
Głównym problemem chcę naprawić to, że kiedy uchwyt działania i po kilku TestModel
została wysłana z nieprawidłowymi wartościami, co powoduje ModelState.IsValid
być false
, potem muszę wrócić ten sam widok z aktualnie zamieszczonych modelu. To zmusza mnie do ponownego uzyskania listy kategorii, ponieważ robiłem to w akcji GET. Dodaje to dużo powielonego kodu do kontrolera i chcę go usunąć. Obecnie robię następujące:
mój model i widok modele:
modelu, jednostka przechowywane w bazie danych:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Category> SubCategories { get; set; }
}
Zobacz modele:
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");
}
}
}
Moje kontroler:
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();
}
}
Chcę usunąć zduplikowane kategorie pobierania i mapowanie, w zasadzie ten kod:
.Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
od kontrolera. Również chcę usunąć sprawdzanie poprawności ModelState
, chcę wykonać akcję tylko, jeśli ModelState.IsValid
, aby zachować kod kontrolera AS CLEAN AS MOŻLIWE. Do tej pory mam następujące rozwiązanie do usuwania czek ModelState
ważności:
tworzyć niestandardowe 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,
};
}
}
Teraz model jest sprawdzane przed wykonuje działanie. W przypadku błędów sprawdzania poprawności używamy tego samego widoku z tym samym ostatnio opublikowanym modelem. Dlatego akcja kontroler POST wygląda następująco:
[HttpPost]
[ValidateModelAttribute]
public ActionResult Index(TestModel model)
{
// Do some important stuff with posted data
return RedirectToAction("Success");
}
To jest ładne, ale teraz mój Categories
własność moich TestModel
jest pusta, bo muszę sprowadzić kategorie z bazy danych i map je odpowiednio. Więc jest to OK, aby zmodyfikować model moim zdaniem wyglądać mniej więcej tak:
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
});
}
}
...
}
Pozwoli to nam na bardzo czysty kontroler, ale nie będzie to powodować jakieś wydajności lub zagadnień architektonicznych? Czy nie złamałoby to zasady pojedynczej odpowiedzialności dla modeli widoku? Czy ViewModels powinien być odpowiedzialny za pobieranie potrzebnych danych?
Idealnie nie, Twoje modele widoków nie wchodzi bezpośrednio w interakcje z repozytorium. Jeśli chcesz zapełnić swój model z repozytorium, stanie się to w twoim kontrolerze. Jeśli nie chcesz powielać kategorii ludności w oddzielnych trasach kontrolera, możesz spróbować przekonwertować tę logikę na osobną metodę. – timothyclifford