Скажем, у меня сложная модель просмотра с большим количеством данных, таких как списки стран, продуктов, категорий и т.д., которые мне нужно извлекать из базы данных каждый раз, когда я создаю 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");
}
Это хорошо, но теперь мое свойство Categories
моего TestModel
пуст, потому что мне нужно получить категории из базы данных и соответствующим образом сопоставить их. Итак, это нормально, чтобы изменить мою модель представления, чтобы посмотреть что-то вроде этого:
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 отвечать за выбор необходимых данных?