5

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);

    }
}
Fabiano
  • 5,124
  • 6
  • 42
  • 69
  • Is there code that checks whether the Guid is `DefaultValue`? It makes more sense to check for null/value than to check `DefaultValue`. And storing `DefaultValue` instead of a null is just confusing - it's the equivalent of storing "empty string" instead of "". – Kirk Broadhurst Jan 14 '11 at 01:55
  • Yes I have services and DAOs that are checking for Guid.Empty. The Guids I'm using here are all for retrieving data or setting references. I never store the default value of Guid – Fabiano Jan 14 '11 at 11:09

3 Answers3

12

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.

Timwi
  • 65,159
  • 33
  • 165
  • 230
  • Thanks for your answer. My code already handles the default Guid correctly (if it has to handle it at all). Changing it to an other type requires a lot of changes. And I have more than one non-required Guid in various ViewModels. Isn't There a way to tell the Model-binder to ignore errors when trying to bind "" to Guid? – Fabiano Jan 13 '11 at 23:55
  • 3
    @Fabiano: If your code handles the all-zeros GUID as a special “null” value, while admittedly this might work by and large, I would consider it rather hacky and a code smell. Future maintainers of your code have no way of telling from the type of the property that it might contain this magic value with a special meaning. If, on the other hand, you use `Guid?`, then it is immediately obvious that it could be `null`, and what that means. Therefore, I would argue that the changes necessary to your code to accommodate the Nullable type would be well worth it. – Timwi Jan 14 '11 at 00:20
  • Ok, I made some test with changing the Guids to Guid? It seems to work with some changes in the code to viewModel.MyGuid.GetValueOrDefault() – Fabiano Jan 14 '11 at 14:12
  • Using Guid? means you now have two empty values: null and Guid.Empty. This means all code should check for both. That makes using Guid better than Guid? because it makes a lot of code easier: if (myGuid != Guid.Empty) is better than if (myGuid != null && myGuid != Guid.Empty) – Vladimir Aug 19 '15 at 12:35
1

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
    }
Community
  • 1
  • 1
Max
  • 531
  • 6
  • 12
0

In your application start event set: DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

Keeping in mind this will make all value types not required unless explicitly set so.

I agree with Fabiano though. Guid is a bit of a freak with it's Guid.Empty.

Sam
  • 1,725
  • 1
  • 17
  • 28