Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind empty string to Guid.Empty or avoid model state error

When I post a form with an empty string "" for a Guid field I get the error "The MyGuid field is required." although I haven't set the "Required" attribute.

//NOT Required   
public Guid MyGuid { get; set; }

after model binding the Guid is 00000000-0000-0000-0000-000000000000 (because it's the default value) and that's correct. But the ModelState has the mentioned error.

How can I avoid this error?

Additional Info:

[Required(AllowEmptyStrings = true)] does not help

I don't want to make the Guid nullable (Guid?) because this would lead to a lot additional code (checking if it has a value, mapping and so on)

Update:

OK, I figured out that a change to Guid? in my view models doesn't result in that many changes than I expected (some calls to MyGuid.GetValueOrDefault() or some checks for MyGuid.HasValue and calls to MyGuid.Value).

However, the reason that a model error is added if no valid Guid is provided with the post request, is that the DefaultModelBinder tries to bind null to Guid. The solution would be to override the DefaultModelBinder. And no errors will be added to the model state

public class MyModelBinder : DefaultModelBinder
{
    protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    {
        if (propertyDescriptor.PropertyType == typeof(Guid) && value == null)
        {
            value = Guid.Empty;
        }
        base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);

    }
}
like image 664
Fabiano Avatar asked Jan 13 '11 23:01

Fabiano


2 Answers

If the type of the field is Guid (which is a value type), then it must contain a value (even if it’s all zeros). The correct solution to have a non-required GUID is to use Guid? (Nullable Guid).

Your reasons for not wanting to use Nullable don’t make sense; no matter which way you are going to encode “emptiness”, your code will have to check for it. I’d argue that Nullable actually makes this easier, generally.

like image 99
Timwi Avatar answered Sep 30 '22 05:09

Timwi


in the sense of my answer https://stackoverflow.com/a/31268941/4985705 on ASP.NET MVC: types cast in model binding the following will return the Guid.Empty values or the Guid parameter, if it is desired.

public class NullableGuidBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(Guid?))
        {
            var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            string input = valueResult.AttemptedValue;
            if (string.IsNullOrEmpty(input) || input == "0")
            {
                // return null, even if input = 0
                // however, that is dropdowns' "guid.empty")
                // base.BindModel(...) would fail converting string to guid, 
                // Guid.Parse and Guid.TryParse would fail since they expect 000-... format

                // add the property to modelstate dictionary
                var modelState = new ModelState { Value = valueResult };
                bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
                return Guid.Empty;
            }
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

binding as follows in your controller

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> SelectAction(
        [ModelBinder(typeof(NullableGuidBinder))] Guid? id)
    {
        // your stuff
    }
like image 21
Max Avatar answered Sep 30 '22 05:09

Max