locked
MVC 2 RC + ModelMetaData.ConvertEmptyStringToNull + ModelBinder RRS feed

  • Question

  • User-2069143896 posted

    Any way we can make ConvertEmptyStringToNull = false by default? Or at least globally override?

    Having all "" convert to null is a breaking change in behavior from 1.0 and is causing havoc with my upgrade.

    Brian

    Thursday, February 4, 2010 2:52 PM

Answers

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 5, 2010 6:44 AM
  • User-2115483147 posted

    I agree that you can set the value to 'false'. However the set to true is done in the MetaData constructor, and the MetaData objects are created internally by the ModelBinder for each property. This means implementing your own MetaData+MetaDataProvider to set to true or implementing your own ModelBinder to ignore the check. A simple static property to intialize would be better and clearer.

    Brian

     

    Hi Brian,

    I know what is your thinking now.

    But we can not change this behaviour, the Qualifier of class of Parameter is not 'virtual',

    http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.parameter.aspx

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 10, 2010 9:36 PM
  • User-1032240251 posted

    We will not be changing the default value of this, because it lines up with the expectations of not only previous frameworks (WebForms treating empty strings as null by default), but also other platforms (f.e., RIA Services).

    Use the [DisplayFormat] attribute to override this on a case-by-case basis.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 15, 2010 12:35 PM

All replies

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 5, 2010 6:44 AM
  • User-2069143896 posted

    Unfortunately RC2 does not fix the issue.  

    The DefaultModelBinder.cs BindProperty Method calls GetPropertyValue (line 200) method which returns null for string.Empty if ConvertEmptyStringToNull = true (line 400), and ConvertEmptyStringToNull is true by default in ModelMetadata.cs (line 18) for everything not overridden with an attribute.

    In MVC 1.0, the BindProperty just used propertyBinder.BindModel(controllerContext, innerBindingContext); (line 217) which did not coerce.

    I think the new behaviour is correct, but there should be a global way to turn it off. Maybe have the ModelMetadata expose a static property that all instances are initialized to.

    Brian

    Friday, February 5, 2010 9:42 AM
  • User-2115483147 posted

    Any way we can make ConvertEmptyStringToNull = false by default?
     

     

    So far as we know that ConvertEmptyStringToNull's default value is true, check this,

    http://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata.convertemptystringtonull(VS.100).aspx

    so, I think we can set 'false' to ConvertEmptyStringToNull manual.

     

    Tuesday, February 9, 2010 10:48 PM
  • User-2069143896 posted

    I agree that you can set the value to 'false'. However the set to true is done in the MetaData constructor, and the MetaData objects are created internally by the ModelBinder for each property. This means implementing your own MetaData+MetaDataProvider to set to true or implementing your own ModelBinder to ignore the check. A simple static property to intialize would be better and clearer.

    Brian

    Wednesday, February 10, 2010 9:05 AM
  • User-2115483147 posted

    I agree that you can set the value to 'false'. However the set to true is done in the MetaData constructor, and the MetaData objects are created internally by the ModelBinder for each property. This means implementing your own MetaData+MetaDataProvider to set to true or implementing your own ModelBinder to ignore the check. A simple static property to intialize would be better and clearer.

    Brian

     

    Hi Brian,

    I know what is your thinking now.

    But we can not change this behaviour, the Qualifier of class of Parameter is not 'virtual',

    http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.parameter.aspx

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, February 10, 2010 9:36 PM
  • User-2069143896 posted

    I am talking about ASP.NET MVC 2 RC, not ASP.NET server form controls.

    Brian

    Thursday, February 11, 2010 9:51 AM
  • User-2115483147 posted

    I am talking about ASP.NET MVC 2 RC, not ASP.NET server form controls.

    Brian

     

    Yes, I know that.

    I mean that we can not override the constructor for the 'Parameter' class, so it mean that we can not make

    ConvertEmptyStringToNull = false by default.

     

    Monday, February 15, 2010 1:52 AM
  • User-2069143896 posted

    I know it is set to true in the constructor. I am suggesting the constructor be changed to pull the value from a static variable or an attribute.

     

    If you are not a member of the ASP.NET MVC 2 team that can change the source code, then please do not reply on this thread.

     

    Brian 

    Monday, February 15, 2010 9:46 AM
  • User-1032240251 posted

    We will not be changing the default value of this, because it lines up with the expectations of not only previous frameworks (WebForms treating empty strings as null by default), but also other platforms (f.e., RIA Services).

    Use the [DisplayFormat] attribute to override this on a case-by-case basis.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, February 15, 2010 12:35 PM
  • User-2069143896 posted

    I agree that the value should be true by default, but give us a way to override it globally. The DisplayFormat only applies to individual properties, not the whole framework.

    Anyone with a 1.0 site that is used to having "" save to the database will now get null. 

     

    Brian

    Monday, February 15, 2010 1:03 PM
  • User-1122014483 posted

    In our app we always assume non nulls, so null lists get turned into empty lists, null arrays to empty arrays, null strings to empty strings.  Maybe<>'s are used when parameters are optional.  This change initially posed a problem for us as well.  The way we overrided this setting with strings was to create a model binder like this:


    public class SimpleArrayModelBinder : DefaultModelBinder {
            public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
    
    if (bindingContext.ModelType == typeof(string)) {
                    var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                    bindingContext.ModelMetadata.ConvertEmptyStringToNull = false;
                    if (value == null || value.AttemptedValue.IsNullOrEmpty())
                        return "";
                    else
                        return value.AttemptedValue;
                }
    
    }
    
    }


    Thursday, February 18, 2010 9:09 AM
  • User-2069143896 posted

    Matt,

    That is 1 approach to work through the problem. 2 other ways are to override the GetPropertyValue on an inherited ModelBinder, or create your own MetaDataProvider.

    The goal of this post was to try and get the ASP.NET MVC 2 team to flex the new default behaviour so 1.0 apps are not broken out of the box.

     

    Brian

    Thursday, February 18, 2010 9:55 AM
  • User-1510398259 posted

    One more opinion to take into account.    From a UI binding perspective this does not seem logical.   If I have a text box that is binding to a string property on my form I would expect that to bind to an empty string.   If the same Model is used on another form and that same property is not used on the form I would expect it to be null (so the behavior of 1.0).   The new behavior provides us no way of knowing whether the property is null because it was not on the form and not bound to or that it was on the form and cleared.     There are a lot of applications that benefit from this differentiation.     This is how WCF binds its data objects so on the service side we can tell if a property was simply not passed or if it was passed and is empty.  

     

    This seems like a pretty change to be pushing out between 1.0 and 2.0, pretty much I would imagine anyone using 1.0 will be required to rewrite some if not all of there code depending on what size of shop and how they are using the framework.

    If this can not be revereted then there needs to be the ability to control this globally through a configuration setting.   We have over 100 objects with anywhere from 10-50 properties on each object.  Going in and changing each property to have the DisplayFormat attribute to set the ConvertEmptyStringToNull to false is not a feasible option for many of us.

    Wednesday, March 3, 2010 12:41 PM
  • User-331009151 posted

    Man, you are singing my song!!! This has got to be the most frustrating change ever that the ASP.NET MVC team have come out with yet! Regardless of whatever reasons are given for making this breaking change, it should at least have been optional  and backwards compataible! I have hundreds of lines of code that will need to be changed to accomodate this! I for one am really pi**ed!

    Sunday, April 18, 2010 2:35 PM
  • User843884940 posted

    Not overly pleased here either.  This is the kind of stuff we dealt with on a regular basis.  Tell me MS isn't going back to old habits!

    2.0 might be the (insert opinion) way but putting people over the barrel on this just to jump from 1 to 2 isn't right.

    Wednesday, April 21, 2010 5:04 PM
  • User843884940 posted

    I ended up using the fix on this page by seting up EmptyStringModelBinder and default it in the global.asax...

    http://stackoverflow.com/questions/2625042/mvc-2-conversion-disrupts-the-parameters-passed-to-my-stored-procedure 

    Thursday, April 22, 2010 2:19 PM
  • User1909789845 posted

    Yea this is causing me a real headache too.

    I don't see how or why this isn't listed as a breaking change in the official documentation?

    http://download.microsoft.com/download/F/1/6/F16F9AF9-8EF4-4845-BC97-639791D5699C/WhatIsNewInMVC_2.pdf

    http://www.hanselman.com/blog/ASPNET4BreakingChangesAndStuffToBeAwareOf.aspx

    And the responses saying "We aren't going to change this as it falls in line with other frameworks" is a joke... how about falling in line with MVC 1 which I, along with several other developers have invested man-months of time in? The least you could do is implement some kind of configuration option for this behaviour.

    Shocking...

     

    Wednesday, April 28, 2010 6:13 AM
  • User-2069143896 posted

    I think the easiest way to workaround this is to create a new model binder:

    namespace System.Web.Mvc
    {
    	public class KeepEmptyStringsEmptyModelBinder : DefaultModelBinder
    	{
    		protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    		{
    			return propertyBinder.BindModel(controllerContext, bindingContext);
    		}
    	}
    }

    Then in the Application_Start of the global.asax:

    ModelBinders.Binders.DefaultBinder = new KeepEmptyStringsEmptyModelBinder();

    Brian

     

    Wednesday, April 28, 2010 9:05 AM
  • User1968914314 posted

    This is old but I'm going to offer the solution for others. I too was frustrated by this, and after examining the MVC 5 code I was able to determine the following fix which also works for MVC2 to MVC4.

    public class CustomModelBinder : DefaultModelBinder
    {
    	public bool ConvertEmptyStringToNull { get; set; }
    
    	public CustomModelBinder ()
    	{
    	}
    
    	public CustomModelBinder (bool convertEmptyStringToNull)
    	{
    		this.ConvertEmptyStringToNull = convertEmptyStringToNull;
    	}
    
    	protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
    	{
    		// this little bit is required to override the ConvertEmptyStringToNull functionality that we do not want!
    	
    		foreach (string propertyKey in bindingContext.PropertyMetadata.Keys)
    		{
    			if(bindingContext.PropertyMetadata[propertyKey] != null)
    					bindingContext.PropertyMetadata[propertyKey].ConvertEmptyStringToNull = this.ConvertEmptyStringToNull;
    		}
    		return base.OnModelUpdating(controllerContext, bindingContext);
    	}
    
    
    }

    This will fix the issue. It would seem that bindingContext.ModelMetadata.ConvertEmptyStringToNull is completely ignored after MVC2, and this is because the setting exists in the PropertyMetadata object for each property being bound. PropertyMetadata is recreated in BindProperty() so if you set it before that method call it will get overwritten unless it exists as an attribute on the property of your object being bound (such as [DisplayFormat(ConvertEmptyStringToNull=false)]). No one wants to do this on every property as that's silly.

    Thursday, October 10, 2013 2:26 PM