4632

ASP.NET MVC Issue with Using Reflection Created Objects with the Default Model Binder

Question:

I am having a weird issue in ASP.NET MVC with objects not being updated with <strong>UpdateModel</strong> when passed a <strong>formCollection</strong>. <strong>UpdateModel</strong> does not appear to be working properly when the object being updated is created through reflection.

Scenario: I have an application which has approximately 50 lookup tables--each of which includes exactly the same schema including typical fields like id, title, description, isactive, and createdon. Rather than build 50 views, I wanted to have a single view which could display the data from all of the lookup tables. I created an Interface called IReferenceEntity and implemented it in each of the POCOs representing my lookup tables.

Using this interface, I am able to easily populate a view with a record from the lookup table. (I pass the items to the view via the following.)

System.Web.Mvc.ViewPage<MyNamespece.IReferenceEntity>

From the database to the view, every thing works perfectly.

However, when I attempt to update the model on post, I am running into some problems.

If I explicitly declare an object reference like the following, every thing works perfectly and the values of my object are updated with the values from my form. Hence, I can then update the database.

AccountStatus a = new AccountStatus(); UpdateModel(a, formCollection.ToValueProvider());

Unfortunately, hard coding the object type would completely defeat the reason for using an interface.

(A primary objective of the application is to be able to dynamically add new tables such as lookup tables without having to do anything "special". This is accomplished by reflecting on the loaded assemblies and locating any classes which implement a specific interface or base class)

My strategy is to determine the concrete type of the object at postback and then create an instance of the type through reflection. (The mechanism I use to determine type is somewhat primitive. I include it as a hidden field within the form. Better ideas are welcome.)

When I create an instance of the object using reflection through any of the following methods, none of the objects are being updated by UpdateModel.

Type t = {Magically Determined Type} object b = Activator.CreatorInstance(t); UpdateModel(b, formCollection.ToValueProvider()); Type t = {Magically Determined Type} var c = Activator.CreatorInstance(t); UpdateModel(c, formCollection.ToValueProvider()); Type t = {Magically Determined Type} IReferenceEntity d = Activator.CreatorInstance(t); UpdateModel(d, formCollection.ToValueProvider());

Note: I have verified that the objects which are being created through relection are all of the proper type.

Does anyone have any idea why this might be happening? I am somewhat stumped.

If I was really "hard up", I could create factory object which would many instantiate any one of these reference entity/lookup objects. However, this would break the application's ability to allow for new lookup tables to be added and discovered transparently and is just not quite as clean.

Also, I could try deriving from an actual ReferenceEntity base class as opposed to an interface, but I am doubtful whether this would make any difference. The issue appears to be with using reflection created objects in the modelbinder.

Any help is appreciated.

Anthony

Answer1:

Augi answered this on ASP.NET forums. It worked with only a couple of minor modifications. Thank you Augi.

<hr />

The problem is that [Try]UpdateModel methods allow to specify model type using generic parameter only so they don't allow dynamic model type specification. I have created issue ticket for this.

You can see TryModelUpdate method implementation here. So it's not difficult to write own overload:

public virtual bool TryUpdateModelDynamic<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class { if (model == null) { throw new ArgumentNullException("model"); } if (valueProvider == null) { throw new ArgumentNullException("valueProvider"); } //Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties); IModelBinder binder = Binders.GetBinder( /*typeof(TModel)*/model.GetType()); ModelBindingContext bindingContext = new ModelBindingContext() { Model = model, ModelName = prefix, ModelState = ModelState, //ModelType = typeof(TModel), // old ModelType = model.GetType(), // new //PropertyFilter = propertyFilter, ValueProvider = valueProvider }; binder.BindModel(ControllerContext, bindingContext); return ModelState.IsValid; }

Answer2:

Does your IReferenceEntity contain setters on the properties as well as getters? I would think that the last sample would work if the interface had property setters, though you'd have to cast it to get it to compile.

Type t = {Magically Determined Type} IReferenceEntity d = Activator.CreatorInstance(t) as IReferenceEntity; UpdateModel(d, formCollection.ToValueProvider());

Normally the reason that it won't set a property on a class is because it can't find a public setter method available to use via reflection.

Answer3:

Just a quick "another thing to try":

UpdateModel(d as IReferenceEntity, formCollection.ToValueProvider());

Not sure if that will work, and I haven't tried it myself, but it's the first thing that came to mind.

If I get a chance later I'll peek at the Default Model Binder code and see if there's anything in there that is obvious...

Recommend

  • MySQL PDO NOW() as assigned value - is it possible?
  • WSO2 Identity Server Deactivating user account
  • Reading from a QFile in different thread
  • Xcode 6 Testing Target Troubles
  • When is it necessary to escape double quotes and other characters when inserting them into MySQL dat
  • Should “DbContext” and “DbSet” be exposed for a pure POCO application
  • Does Context/Scoping of a SQLAlchemy Session Require Non-Automatic Object/Attribute Expiration?
  • Android application not restoring state when installed from .apk, works fine from eclipse
  • mapping between two ontologies
  • Does the MySQL IN clause execute the subquery multiple times?
  • ConfigurationBuilder not working in azure function
  • Losing my session variables
  • IE6 changes DOCTYPE to a bad one
  • Need code translation from VB to C#
  • Better Indy for Dephi 2007
  • Best practice to eliminate magic numbers within a member function
  • Cannot save model when using ember render helper
  • How to access meteor package name inside package?
  • VBA Excel, loop through variables
  • using System.Speech.Synthesis with Windows10 universal app (XAML-C#)
  • Clear fused location provider's location for testing
  • How to get latest version of a artifact on Bintray using JSONP
  • Tell Git to stop prompting me for conflicts when none really exist?
  • Trying to get the char code of ENTER key
  • Using Sax parsing to edit and write XML in VB6
  • GAE: Way to get reference to an HttpSession from its ID?
  • Spring boot 2.0.0.M4 required a bean named 'entityManagerFactory' that could not be found
  • Eliminate partial duplicate rows from result set
  • What is the purpose of TaskExecutor in spring?
  • Debug.DrawLine not showing in the GameView
  • Yii2: Config params vs. const/define
  • Convert array of 8 bytes to signed long in C++
  • jQuery tmpl and DataLink beta
  • Run Powershell script from inside other Powershell script with dynamic redirection to file
  • SQL merge duplicate rows and join values that are different
  • Proper way to use connect-multiparty with express.js?
  • How to set the response of a form post action to a iframe source?
  • Understanding cpu registers
  • Recursive/Hierarchical Query Using Postgres
  • Running Map reduces the dimensions of the matrices