ASP.NET MVC UpdateModel with interface

I am trying to get an UpdateModel to populate a model that is installed as soon as the interface is at compile time. For example, I have:

// View Model public class AccountViewModel { public string Email { get; set; } public IProfile Profile { get; set; } } // Interface public interface IProfile { // Empty } // Actual profile instance used public class StandardProfile : IProfile { public string FavoriteFood { get; set; } public string FavoriteMusic { get; set; } } // Controller action public ActionResult AddAccount(AccountViewModel viewModel) { // viewModel is populated already UpdateModel(viewModel.Profile, "Profile"); // This isn't working. } // Form <form ... > <input name='Email' /> <input name='Profile.FavoriteFood' /> <input name='Profile.FavoriteMusic' /> <button type='submit'></button> </form> 

Also note that I have a custom mediator that inherits the DefaultModelBinder used, which populates the IProfile with an instance of StandardProfile in an overridden CreateModel method.

The problem is that FavoriteFood and FavoriteMusic are never populated. Any ideas? Ideally, this will all be done in conjunction with the model, but I'm not sure if this is possible without writing a fully custom implementation.

Thanks Brian

+6
asp.net-mvc defaultmodelbinder
source share
2 answers

I will need to check the ASP.NET MVC code (DefaultModelBinder), but I assume that it reflects the IProfile type, not the StandardProfile instance.

Thus, he is looking for all the members of the IProfile that he can try to link, but his interface is empty, so he believes that this is done.

You can try something like updating the BindingContext and change ModelType to StandardProfile and then call

 bindingContext.ModelType = typeof(StandardProfile); IProfile profile = base.BindModel(controllerContext, bindingContext); 

Anyway, having an empty interface is weird ~


Edit: I just want to add that the code above is just a pseudo code, you will need to check DefaultModelBinder to see what exactly you want to write.


Edit # 2:

You can:

 public class ProfileModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { { bindingContext.ModelType = typeof(StandardProfile); return base.BindModel(controllerContext, bindingContext); } } 

No need to create model bindings for AccountView, this works great.


Edit # 3

Tested, the binder described above works, just add:

 ModelBinders.Binders[typeof(IProfile)] = new ProfileModelBinder(); 

Your action looks like this:

 public ActionResult AddAccount(AccountViewModel viewModel) { // viewModel is fully populated, including profile, don't call UpdateModel } 

You can use IOC when setting up a model binding object (for example, an injected type constructor).

+2
source share

The actual interface type was not discussed here: http://forums.asp.net/t/1348233.aspx

However, I found a hacker way around the problem. Since I already had a custom mediator for this type, I was able to add some code to it in order to bind for me. Here's what my connecting device looks like:

 public class AccountViewModelModelBinder : DefaultModelBinder { private readonly IProfileViewModel profileViewModel; private bool profileBound = false; public AccountViewModelModelBinder(IProfileViewModel profileViewModel) { this.profileViewModel = profileViewModel; } protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { // Bind the profile if (profileBound) return; profileBound = true; bindingContext.ModelType = profileViewModel.GetType(); bindingContext.Model = profileViewModel; bindingContext.ModelName = "Profile"; BindModel(controllerContext, bindingContext); } protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType) { var model = new AccountViewModel(); model.Profile = profileViewModel; return model; } } 

Basically, when the "binding" of the model is "completed" binding the main element of the AccountViewModel, I then change the binding context (as suggested by the eyston) and call BindModel again. Then it connects my profile. Note that I called GetType in profileViewModel (which comes with the IOC container in the constructor). Also note that I turn on a flag indicating whether the profile model has already been linked. Otherwise, there will be an infinite OnModelUpdated loop that gets called.

I am not saying that this is enough, but it works well enough for my needs. I would still like to hear about other offers.

0
source share

All Articles