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 reaso开发者_StackOverflow中文版n 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);
}
}
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.
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
}
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.
精彩评论