2017-09-11 11 views
1

モデルプロパティに割り当てる前に、いくつかの前処理を生データに適用したいとします。つまり、この文字列 "324.32"と "324,32"の両方を二重に変換できるようにするには、カンマをドットで置き換えます。だから私は、その後、適切なプロバイダ[FromBody]属性を持つモデルプロパティバインディング

public class MoneyModelBinderProvider : IModelBinderProvider 
    { 
     public IModelBinder GetBinder(ModelBinderProviderContext context) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException(nameof(context)); 
      } 

      if(context.Metadata?.ModelType == null) 
      { 
       return null; 
      } 


      if (context.Metadata.ModelType.In(typeof(double), typeof(decimal), typeof(float))) 
      { 
       return new MoneyModelBinder(context.Metadata.ModelType); 
      } 
      return null; 
     } 
    } 

とStartup.cs

services.AddMvc(options => 
     { 
      options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider()); 

     }); 

内側に登録このモデルバインダーに

public class MoneyModelBinder: IModelBinder 
    { 
     private readonly Type _modelType; 
     public MoneyModelBinder(Type modelType) 
     { 
      _modelType = modelType; 
     } 

     public Task BindModelAsync(ModelBindingContext bindingContext) 
     { 
      if (bindingContext == null) 
      { 
       throw new ArgumentNullException(nameof(bindingContext)); 
      } 

      string modelName = bindingContext.ModelName; 


      ValueProviderResult providerResult = bindingContext.ValueProvider.GetValue(modelName); 

      if (providerResult == ValueProviderResult.None) 
      { 
       return TaskCache.CompletedTask; 
      } 

      bindingContext.ModelState.SetModelValue(modelName, providerResult); 

      string value = providerResult.FirstValue; 

      if (string.IsNullOrEmpty(value)) 
      { 
       return TaskCache.CompletedTask; 
      } 

      value = value.Replace(",", "."); 

      object result; 
      if(_modelType == typeof(double)) 
      { 
       result = Convert.ToDouble(value, CultureInfo.InvariantCulture); 
      } 
      else if(_modelType == typeof(decimal)) 
      { 
       result = Convert.ToDecimal(value, CultureInfo.InvariantCulture); 
      } 
      else if(_modelType == typeof(float)) 
      { 
       result = Convert.ToSingle(value, CultureInfo.InvariantCulture); 
      } 
      else 
      { 
       throw new NotSupportedException($"binder doesn't implement this type {_modelType}"); 
      } 


      bindingContext.Result = ModelBindingResult.Success(result); 
      return TaskCache.CompletedTask; 
     } 

    } 

を書いたが、私はいくつかの奇妙な行動に気づいたか、多分私が何かを逃しました。この種のアクションを使用すると、最初のプロバイダはモデル自体のために呼び出され、次にモデルのすべてのプロパティに対して呼び出されます。 [FromBody]属性を使用し、JSONでパラメータを指定すると、プロバイダはモデルに対して呼び出されますが、このモデルのプロパティは呼び出されません。しかし、なぜ? FromBodyでバインダーを使用するにはどうすればいいですか?

+0

カントのコメントを使用して、あなたの周りの簡単な作業は、ビューモデルを受け入れることができるよう(それらのプロパティを文字列として)AutoMapperを使用して、コントローラアクション内のエンティティモデル/ dtoにマップします。 – Steveland83

+0

投稿時にコンテンツタイプヘッダーを明示的に設定していますか? JSONのように見えるだけで、JSONとして解釈されるわけではありません。 –

答えて

0

解決策が見つかりました。これは、here [FromBody]が他の値プロバイダと比較して動作が異なることを示しているため、JsonFormattersを介して複雑なオブジェクトを一度にすべて変換します。だから、モデルバインダーでは、FromBodyのためだけに別のロジックを書くべきです。そしてもちろん、私たちは、JSONの処理中にいくつかのポイントをキャッチすることができます:

public class MoneyJsonConverter : JsonConverter 
{ 
    public override bool CanWrite => false; 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(double); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     string value = (reader.Value ?? "").Replace(" ", "").Replace(",", "."); 

     TypeConverter converter = TypeDescriptor.GetConverter(modelType); 
     object result = converter.ConvertFromInvariantString(value); 

     return result;; 
    } 
} 

、あなたが記述している問題について

 services.AddMvc(options => 
     { 
      options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider()); 

     }).AddJsonOptions(options => 
     {    
      options.SerializerSettings.Converters.Add(new MoneyJsonConverter()); 
     }); 
関連する問題