2015-03-03 3 views
4

У меня есть простой 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?

Update:

Изменение char недвижимость string привязок, как ожидалось.

+0

является пробельные вернулся в контроллер? – user1666620

+0

Я не уверен, что вы имеете в виду. В действии контроллера свойство char на модели имеет нулевое значение символа (\ 0). –

+0

Вы можете показать соответствующий код с точки зрения? – user1666620

ответ

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

Причина проста, char определяется как тип значения (struct), а string определяется как ссылочный тип (class). Это означает, что char не имеет значения NULL и должен иметь значение.

Вот почему DefaultModelBinder (что вы, вероятно, используете) автоматически устанавливает метаданные проверки для этого свойства как required, даже если вы не добавить атрибут [Required].

Вот source для ModelMetaData.cs (строка 58):

_isRequired = !TypeHelpers.TypeAllowsNullValue(modelType); 

Таким образом, вы в конечном итоге с ModelMetaData.Required для вашего Character1 имущества установлен в true.

Однако, вы можете явно настроить DataAnnotationsModelValidatorProviderне автоматически установленное значение-типа, как required, используя следующие:

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; 

См MSDN

+0

Спасибо, но почему пустым символом пробела считается действительное значение? Я считаю, что «» отличается от пустой строки или нулевой. В настоящее время я проверяю источник, чтобы понять, почему с ним можно обращаться иначе. –

1

Ok Я нашел вредоносный код, в 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; 
     ... 
} 

Я не уверен, если это ошибка или нет.

1

Хит этот вопрос в .NET Ядра недавно, потому что 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()); })

Смежные вопросы