Attachment of the View Model instance constructor used as an action method parameter

When creating a view model, you can fill in the parameters (for example, used in the drop-down list) in the setter property of the view model. The problem is that when this view model is later passed as a parameter (via the frame!) To the action method, these property values ​​do not automatically re-enter, so if you need to re-display the form due to validation errors, you need to again fill in these parameters.

One potential solution that I specifically ask in this question is how to create an MVC environment to create a view model by embedding a constructor that would provide the view model constructor with an implementation of some data access object (for example, a repository) that can be used to retrieve parameters when they are requested by the view (for example, in the DropDownListFor helper method)?

I think the solution may have something to do with IModelBinderProvider or IModelBinder implementations, but after I experimented with these things from code snippets here and there on the network, I'm still looking for a fully working example with downloadable executable code without any Any missing parts, like all together.

If you are looking for an alternative discussion on how to populate a selection list, for example. using "Dependecy Lookup" instead of "Dependecy Injection" you can check the following discussion: The best way to populate SelectList for ViewModel in GET / POST The best way to populate SelectList for ViewModel in GET / POST

A few days ago, I wrote the following next question in this section about the "Dependecy Injection" that I am currently looking for in this thread: https://stackoverflow.com/a/166268/ (which provides an example code about the problem I'm looking for to solve)

But instead of hoping that someone would find this thread with a less specific name, I created this new question with a more specific topic about what I'm looking for. And I will also provide a link from this topic to this new question for those who want to follow up on this particular solution that I am looking for.

+6
source share
1 answer

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).

+5
source

All Articles