I assume that you want your ViewModels to automatically enter something through their constructor - for example, some kind of configuration object that the View will use to determine what to show. I also assume that this approach raises the “No parameterless constructor defined for this object” error when MVC tries to automatically create and associate a model instance with the arguments of your Controller action. Let us also suppose that we will use the DI framework to inject a SiteConfig object into our controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our controller in its ViewModels Actions when they are automatically connected.
So, let's define a base model for others inherited from.
Baseviewmodel
public class BaseViewModel { public ISiteConfig SiteConfig { get; set; } public BaseViewModel(ISiteConfig siteConfig) { this.SiteConfig = siteConfig; } }
Now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel { public string SomeIndexProperty { get; set; } public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {} }
And now let's define the base controller that our controllers inherit.
Basecontroller
public abstract class BaseController : Controller { protected BaseController(ISiteConfig siteConfig) { _siteConfig = siteConfig; } private readonly ISiteConfig _siteConfig; public ISiteConfig SiteConfig { get { return _siteConfig; } } }
Now we define our actual controller.
Homecontroller
public HomeController: BaseController { public HomeController(ISiteConfig siteConfig): base(siteConfig) {} }
Assuming we are using Ninject for DI, Ninject will be configured to automatically create a controller and pass a specific ISiteConfig object to its constructor at run time.
Now add our action to the controller.
Indicative action
public ActionResult Index(IndexViewModel model) { return View(model); }
So, this is the place where without any actions MVC will explode with the error "Parameterless Constructor" if you try to call the Index action because MVC cannot find the ViewModel constructor that takes no arguments.
So the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder { protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel))) { var baseControl = controllerContext.Controller as BaseController; if (baseControl == null) { throw new Exception("The Controller must derive from BaseController"); } var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig); bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType); return instance; } else { return base.CreateModel(controllerContext, bindingContext, modelType); } } }
And we need to set this as the default middleware in global.asax.cs :
protected void Application_Start() { ... ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder(); }
It's all. As you can see, when viewing the index action now MVC will use our custom mediator. He will understand that the IndexViewModel is obtained from the BaseViewModel and therefore will try to deploy an instance of the IndexViewModel using ISiteConfig, which he can find in the Action Controller (because the controller is derived from the BaseController).