I am trying to move some logic outside my controller for creating my view models for the view.
I have a lot of queries to fire to make sure the complete view model is ready, so to keep my controllers thin I came up with this design - any feedback, comments or suggestions are welcome!
The interfaces looks as following:
public interface IViewModel
{
// Marker interface
}
public interface IHandleViewModel<TViewModel> where TViewModel : IViewModel
{
Task<TViewModel> Handle();
}
public interface IProcessViewModels
{
Task<TViewModel> Create<TViewModel>() where TViewModel : IViewModel, new();
}
The implementation of the IProcessViewModels
is using dependency injection (Simple Injector) to find the correct handler for the view model:
internal sealed class ViewModelProcessor : IProcessViewModels
{
private readonly Container _container;
public ViewModelProcessor(Container container)
{
_container = container;
}
public Task<TViewModel> Create<TViewModel>() where TViewModel : IViewModel, new()
{
var handlerType = typeof(IHandleViewModel<>).MakeGenericType(typeof(TViewModel));
dynamic handler = _container.GetInstance(handlerType);
return handler.Handle();
}
}
The setup is done in the Composition Root using this extension:
public static void RegisterViewModels(this Container container, Assembly[] viewModelAssemblies)
{
if (container == null)
throw new ArgumentNullException("container");
if (viewModelAssemblies == null)
throw new ArgumentNullException("viewModelAssemblies");
container.RegisterSingle<IProcessViewModels, ViewModelProcessor>();
container.RegisterManyForOpenGeneric(typeof(IHandleViewModel<>), viewModelAssemblies);
container.RegisterSingleDecorator(
typeof(IHandleViewModel<>),
typeof(ViewModelLifetimeScopeDecorator<>)
);
}
The usage
public HomeViewModel : IViewModel {
public string PageTitle { get; set; }
public IList<TodoItem> TodoItems { get; set; }
}
public HandleHomeViewModel : IHandleViewModel<HomeViewModel> {
private readonly IProcessQueries _queries; // CQRS
public HandleHomeViewModel(IProcessQueries queries) {
_queries = queries;
}
public async Task<HomeViewModel> Handle() {
var model = new HomeViewModel {
PageTitle = "This is the page title";
TodoItems = await _queries.Execute(new GetTodoItemsFromDatabase()); // for demo purpose
}
return model;
}
}
public class HomeController : Controller
{
private readonly IProcessViewModels _models;
public HomeController(IProcessViewModels models)
{
_models = models;
}
public async Task<ActionResult> Index()
{
var model = await _models.Create<HomeViewModel>();
return model;
}
}