ASP.NET MVC 2 ModelBinder

Posts   
 
    
Posts: 134
Joined: 10-Jan-2007
# Posted on: 04-Feb-2010 16:10:55   

Since the MVC 2 RC is out, I have updated the ModelBinder, starting a new thread so it is seperate from the 1.0 version and easier to find.

(http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=15191).

Some notes:

  • No longer need the IndexModelBinder since it the MVC team added support back.
  • When you set an OverrideModelType now, it resets the internal ModelMetaData to that type. This way the correct DataAnnotationsModelValidator is found.
  • Refactored to take advantage of the some internal MVC changes

Updated: with code to fix nullable types/strings.

Brian

Attachments
Filename File size Added on Approval
LLBLGenModelBinder.cs 12,174 04-Feb-2010 21:54.13 Approved
MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 04-Feb-2010 16:54:38   

Thanks Brian.

Matt

arschr
User
Posts: 893
Joined: 14-Dec-2003
# Posted on: 04-Feb-2010 17:09:08   

Download is not yet approved.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 04-Feb-2010 18:15:40   

Thanks for the update. The one for MVC 1 worked great. I didn't have to do a thing to get it work.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 04-Feb-2010 19:22:41   

arschr wrote:

Download is not yet approved.

now it is simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 134
Joined: 10-Jan-2007
# Posted on: 04-Feb-2010 20:25:29   

Hey all, I found an interesting twist from MS. On the ModelMetaData, there is a property named ConvertEmptyStringToNull. As the name implies, it turns all "" to null when getting the value to set a property. The default is true (which makes it a breaking change to me cry , with no warning rage ).

This causes issues because the Entities are not loaded from the database, so the current value is null. This results in the field not being marked changed.

Basically, setting a string value to blank in the UI will not save to the database. I am looking into whether this affects nullable types too before releasing a final fix. To revert back to the original string handling, add this to your LLBLGenModelBinder.cs.

        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            return propertyBinder.BindModel(controllerContext, bindingContext);
        }

I will update this thread when as I find out more.

Sorry for any confusion.

Brian

Posts: 134
Joined: 10-Jan-2007
# Posted on: 04-Feb-2010 22:21:58   

The ConvertEmptyStringToNull setting brought to light the issue of setting any nullable type to null. This only affects setting a nullable property that has a value to actually be null (was 2.00 to null), the field is not marked changed so it is not updated.

The fix is to mark the property being set to null to changed and mark the entity as dirty.

I have updated the attachment in the top message to include the fix, this affects the 1.0 version too, I will be posting an update to that version also.

As for the ConvertEmptyStringToNull to setting. The attachment leaves it off, meaning all "" will be converted to null.

You can override the behaviour by adding an attribute to the property (not easy unless you use LLBLGen 3.0, not sure why this cannot be applied to classes?)

[System.ComponentModel.DataAnnotations.DisplayFormat(ConvertEmptyStringToNull=true)]

Add this to your LLBLGenModelBinder.cs, this will affect all objects bound:

protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
     return propertyBinder.BindModel(controllerContext, bindingContext);
}

I think you could create a new class that inherits from DataAnnotationsModelMetadataProvider or AssociatedMetadataProvider, set ConvertEmptyStringToNull = false in the constructor which would allow properties to override using the attribute. Then set the ModelMetadataProvider.Current to your new provider.

Let me know if you see any issues with this fix.

Brian

audunm
User
Posts: 2
Joined: 18-Aug-2011
# Posted on: 13-Apr-2012 10:06:14   

I'm using the LLBLGenModelBinder in an MVC 3 project, and it works fine so far, but I've run in to a problem with overriding the model type.

I have a database model with a MainProduct table and several other tables (e.g., AccomodationProduct) that inherit from this table. The MainProduct table has a field "Type" that specifies what kind of product it is.

I have a View called EditProduct that uses a MainProductEntity as Model, and this POST method in my controller:

[HttpPost]
public ActionResult EditProduct(MainProductEntity eProduct)
{
    var type = eProduct.OverrideModelType;
}       

I have extended the MainProductEntity with the OverrideModelType property like this:

public partial class MainProductEntity
    {
        public string OverrideModelType
        {
            get
            {
                switch (this.Type)
                {
                    case (int)MainProductType.Accomodation:
                        return typeof(AccomodationProductEntity).AssemblyQualifiedName;
                    //The rest of the implementation omitted
                }
                return null;
            }
        }
    }

The problem is that

ValueProviderResult overrideModelType = bindingContext.ValueProvider.GetValue(CreateSubPropertyName(bindingContext.ModelName, "OverrideModelType"));

in LLBLGenModelBinder.CreateModel() still returns null.

Any ideas of how I can make the modeltypeoverride work?

Audun

audunm
User
Posts: 2
Joined: 18-Aug-2011
# Posted on: 13-Apr-2012 10:39:11   

For the benefit of anyone else that might have the same problem as me:

I solved mine by adding

@Html.HiddenFor(m => m.OverrideModelType)

to my view.

Audun

TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 22-May-2013 23:52:05   

For LLBL v4 I had to change the CreateModel override:

    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
      ...

      return MarkAsFetched(base.CreateModel(controllerContext, bindingContext, modelType));
    }

    protected static object MarkAsFetched(object model)
    {
      var entity2 = model as IEntity2;
      if (entity2 != null )
        entity2.Fields.MarkAsFetched();
      return model;
    }

Due to issues raised in this post: ForceCurrentValueWrite not working as expected in 4.0.

Anyone else find the same?

Jeremy Thomas