MVC 6 - концепция ViewModel Builder
Я начал рефакторинг веб-приложения ASP.Net 5, которое использует MVC 6 и Entity Framework 7, когда мне стало интересно о некоторых моментах. Мои контроллеры в настоящее время используют DbContext
реализация через внедрение зависимостей, чтобы заполнить модели представления и вернуть их для рендеринга с представлением. Подобно:
public class UserController : Controller
{
[FromServices]
public MyContext myContext { get; set; }
//route which renders all users
public IActionResult Index()
{
var users = myContext.User.OrderBy(u => u.Name);
List<UserIndexViewModel> v = new List<UserIndexViewModel>();
foreach (var item in users)
{
v.Add(new UserIndexViewModel { Name = item.Name, City = item.City, DateOfBirth = item.DateOfBirth });
}
return View(v);
}
//route to edit user
public async Task<ActionResult> Edit(int id)
{
User user = await FindUserAsync(id);
UserEditViewModel v = new UserEditViewModel { Name = user.Name, City = user.City };
return View(v);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Update(int id, UserEditViewModel userModel)
{
User user = await FindUserAsync(id);
try
{
user.Name = userModel.Name;
user.City = userModel.City;
myContext.User.Attach(user);
myContext.Entry(user).State = EntityState.Modified;
await myContext.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (Exception)
{
ModelState.AddModelError(string.Empty, "Unable to save changes.");
}
return View(userModel);
}
private Task<User> FindUserAsync(int id)
{
return myContext.User.SingleOrDefaultAsync(u => u.UserId == id);
}
}
Я провел некоторое исследование и нашел несколько постов в блоге ( вроде этого), в которых содержится просьба содержать контроллеры в чистоте. Хорошо Почему бы и нет? Я начал создавать своего рода конструктор моделей представлений, чтобы передать логику методов контроллера конструктору моделей представлений. В приведенной выше статье есть подсказка, чтобы создать для каждой модели представления собственный класс конструктора моделей представлений, что имеет смысл в моих глазах. Для модели User
существуют два вида и, следовательно, две модели (UserIndexViewModel
а также UserEditViewModel
). Когда я создаю связанные классы модели представления-модели, они должны быть производными от одного и того же (абстрактного) класса, потому что может быть так, что оба дочерних класса нуждаются в вспомогательном методе (например, FindUserAsync()
- это не так в моем примере, но только представьте). Таким образом, я хотел бы иметь такую конструкцию:
public interface IViewModelBuilder<TController, TViewModel>
{
TViewModel Build(TController controller, TViewModel viewModel);
Task<TViewModel > BuildAsync(TController controller, TViewModel viewModel);
}
public abstract class UserViewModelBuilder<TViewModel> : IViewModelBuilder<UserController, TViewModel> { ... }
public class UserIndexViewModelBuilder : SiteViewModelBuilder<UserIndexViewModel> { ... }
public class UserEditViewModelBuilder : SiteViewModelBuilder<UserEditViewModel> { ... }
Эта упомянутая функция, которая необходима для создателей моделей нескольких представлений одной модели, должна быть реализована в абстрактном классе (UserViewModelBuilder
в моем случае), верно?
Я сделал это так:
public abstract class UserViewModelBuilder<TViewModel> : IViewModelBuilder<UserController, TViewModel>
{
[FromServices]
public MyContext myContext { get; set; }
public abstract TViewModel Build(UserController controller, TViewModel viewModel);
public abstract Task<TViewModel> BuildAsync(UserController controller, TViewModel viewModel);
public Task<User> FindUserAsync(int id)
{
return myContext.User.SingleOrDefaultAsync(u => u.UserId == id);
}
}
Таким образом, с помощью этого абстрактного класса я могу создать реализацию UserIndexViewModelBuilder
а также UserEditViewModelBuilder
классы, а также, например, UserDeleteViewModelBuilder
а также UserCreateViewModelBuilder
занятия в будущем...
Теперь вопрос:
- Это правильный подход, чтобы отделить логику от контроллера? Если да, нужна ли мне какая-то фабрика для всех сборщиков моделей представлений, к которой можно получить доступ через DI на всех моих контроллерах? Если это способ, который является своего рода лучшей практикой для приложений MVC. Есть ли что-то отличное от упомянутого руководства, которое можно / нужно использовать в приложениях MVC 6?
- Является ли абстрактный класс правильным местом для вызова
DbContext
реализация через DI? Это нехорошо для меня. - Некоторые другие моменты, которые я пропустил? Некоторые if-операторы для проверки ответа были удалены из UserController для лучшей читаемости.
Спасибо!:-)