我有一个简单的viewmodel,其中包含char
属性...
public char Character1 { get; set; }
默认模型绑定似乎没有将空格字符("")转换为此属性,从而导致以下ModelState错误...
The Character1 field is required.
html输入元素是在javascript中创建的:
var input = $('<input type="password" name="Character' + i + '" id="input-' + i + '" data-val="true" data-val-custom maxlength="1"></input>');
[Required]
属性。AttemptedValue
属性。ModelState.IsValid
返回false。\0
。为什么空格字符没有绑定到char
属性?
更新
将char
属性更改为string
会按预期进行绑定。
答案 0 :(得分:2)
我认为它是DefaultModelBinder的失败。如果在动作中使用FormCollection,则该字符串将作为空格返回。
此IModelBinder实现显示了默认模型绑定器的行为方式,并提供了可能的解决方案:
public class CharModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var dmb = new DefaultModelBinder();
var result = dmb.BindModel(controllerContext, bindingContext);
// ^^ result == null
var rawValueAsChar = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(char));
// ^^ rawValueAsChar == null
var rawValueAsString = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
if(!string.IsNullOrEmpty(rawValueAsString))
return rawValueAsString.ToCharArray()[0];
return null;
}
}
使用以下命令在Global.asax中注册:
ModelBinders.Binders.Add(typeof(char), new CharModelBinder());
答案 1 :(得分:2)
原因很简单,char
被定义为值类型(struct
),而string
被定义为引用类型(class
)。这意味着char
不可为空,必须具有值。
这就是为什么DefaultModelBinder
(您可能正在使用)会自动将此属性的验证元数据设置为required
,即使您没有添加[Required]
属性。
此处为ModelMetaData.cs
source(第58行):
_isRequired = !TypeHelpers.TypeAllowsNullValue(modelType);
因此,对于ModelMetaData.Required
属性设置为Character1
,您最终会得到true
。
但是,您可以使用以下内容明确配置DataAnnotationsModelValidatorProvider
而不是以自动将值类型设置为required
:
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
请参阅MSDN
答案 2 :(得分:1)
好的,我在System.Web.Mvc.ValueProviderResult
找到了有问题的代码:
private static object ConvertSimpleType(CultureInfo culture, object value, Type destinationType)
{
if (value == null || destinationType.IsInstanceOfType(value))
return value;
string str = value as string;
if (str != null && string.IsNullOrWhiteSpace(str))
return (object) null;
...
}
我不确定这是不是一个错误。
答案 3 :(得分:1)
最近在.NET Core中遇到此问题,因为SimpleTypeModelBinder具有相同的检查,因此添加了以下内容:
using System; using Microsoft.AspNetCore.Mvc.ModelBinding; public class CharModelBinderProvider : IModelBinderProvider { /// <inheritdoc /> public IModelBinder GetBinder( ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.ModelType == typeof(char)) { return new CharModelBinder(); } return null; } } using System; using System.ComponentModel; using System.Runtime.ExceptionServices; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; /// <inheritdoc /> /// <summary> /// An <see cref="T:Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder" /> for char. /// </summary> /// <remarks> /// Difference here is that we allow for a space as a character which the <see cref="T:Microsoft.AspNetCore.Mvc.ModelBinding.SimpleTypeModelBinder" /> does not. /// </remarks> public class CharModelBinder : IModelBinder { private readonly TypeConverter _charConverter; public CharModelBinder() { this._charConverter = new CharConverter(); } /// <inheritdoc /> public Task BindModelAsync( ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == ValueProviderResult.None) { // no entry return Task.CompletedTask; } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { var value = valueProviderResult.FirstValue; var model = this._charConverter.ConvertFrom(null, valueProviderResult.Culture, value); this.CheckModel(bindingContext, valueProviderResult, model); return Task.CompletedTask; } catch (Exception exception) { var isFormatException = exception is FormatException; if (!isFormatException && exception.InnerException != null) { // TypeConverter throws System.Exception wrapping the FormatException, so we capture the inner exception. exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; } bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, exception, bindingContext.ModelMetadata); // Were able to find a converter for the type but conversion failed. return Task.CompletedTask; } } protected virtual void CheckModel( ModelBindingContext bindingContext, ValueProviderResult valueProviderResult, object model) { // When converting newModel a null value may indicate a failed conversion for an otherwise required model (can't set a ValueType to null). // This detects if a null model value is acceptable given the current bindingContext. If not, an error is logged. if (model == null && !bindingContext.ModelMetadata.IsReferenceOrNullableType) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueMustNotBeNullAccessor(valueProviderResult.ToString())); } else { bindingContext.Result = ModelBindingResult.Success(model); } } }
在初创公司内:
serviceCollection.AddMvc(options => { options.ModelBinderProviders.Insert(0, new CharModelBinderProvider()); })