2013-05-25 13 views
16

DefaultModelBinderから継承するMVC 4のカスタムモデルバインダーを作成しようとしています。 のインターフェイスを傍受してのバインディングレベルを取得し、AssemblyQualifiedNameという非表示のフィールドから目的のタイプをロードしようとします。DefaultModelBinderから継承したカスタムモデルバインダー

ここで私は今のところ(簡体字)しているものです:

public class MyWebApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     ModelBinders.Binders.DefaultBinder = new InterfaceModelBinder(); 
    } 
} 

public class InterfaceModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, 
     ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType.IsInterface 
      && controllerContext.RequestContext.HttpContext.Request.Form.AllKeys.Contains("AssemblyQualifiedName")) 
     { 
      ModelBindingContext context = new ModelBindingContext(bindingContext); 

      var item = Activator.CreateInstance(
       Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"])); 

      Func<object> modelAccessor =() => item; 
      context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(), 
       bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName); 

      return base.BindModel(controllerContext, context); 
     } 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

例Create.cshtml(簡体)ファイル:

@model Models.ScheduledJob 

@* Begin Form *@ 
@Html.Hidden("AssemblyQualifiedName", Model.Job.GetType().AssemblyQualifiedName) 

@Html.Partial("_JobParameters") 
@* End Form *@ 

_JobParameters.cshtml上記部分がModel.Jobのプロパティを見て、ビルドします編集コントロールは@Html.EditorFor()に似ていますが、余分なマークアップがあります。 ScheduledJob.JobプロパティのタイプはIJob(インターフェイス)です。

例ScheduledJobsController.cs(簡体字):私は、フォームを保存するとき

[HttpPost] 
public ActionResult Create(ScheduledJob scheduledJob) 
{ 
    //scheduledJob.Job here is not null, but has only default values 
} 

、それは正しくオブジェクト型を解釈し、新しいインスタンスを取得しますが、オブジェクトのプロパティは、その適切に設定されていません値。

これ以外に、指定したタイプのプロパティーバインドを引き継ぐためにデフォルトのバインダーに指示する必要がありますか?

答えて

21

This articleは、私がモデルのバインダーを過度に複雑にしていることを私に示しました。次のコードは動作します:

public class InterfaceModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType.IsInterface) 
     { 
      Type desiredType = Type.GetType(
       EncryptionService.Decrypt(
        (string)bindingContext.ValueProvider.GetValue("AssemblyQualifiedName").ConvertTo(typeof(string)))); 
      bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, desiredType); 
     } 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 
+0

上記のバインダーはどこに追加しましたか?私はバインダーModelBinders.Binders.Add(typeof(Organization)、新しいOrganizationModelBinder(DependencyResolver.Current.GetService ()))を持っています。 app_start()メソッドのglobal.asax.csページにあります。私はルートとバンドルの仕方のようなモデルバインダーを登録するためのより良い方法を探していますか? – mmssaann

+0

@mmssaann私はglobal.asax.csの 'Application_Start()'に 'ModelBinders.Binders.DefaultBinder = new MyCustomModelBinder();'を使用しました。次に、私はModelBinderを抽象クラスやインタフェースを扱えるように十分に汎用化し、そうでない場合はバインディングロジックをDefaultModelBinderに渡しました。 –

1

MVC 4でそれはあなたがカスタムモデルバインダーに必要になる場合がありますすべてのであれば、メッセージを上書きすることは簡単です:

protected void Application_Start(object sender, EventArgs e) 
    { 
     //set mvc default messages, or language specifc 
     ClientDataTypeModelValidatorProvider.ResourceClassKey = "ValidationMessages"; 
     DefaultModelBinder.ResourceClassKey = "ValidationMessages"; 
    } 

はその後でValidationMessagesという名前のリソースファイルを作成します次のようなエントリ:

NAME: FieldMustBeDate 
VALUE: The field {0} must be a date. 
NAME: FieldMustBeNumeric 
VALUE: The field {0} must be a number 

これはコンプライアンスに失敗したためです。私たちのセキュリティスキャンは、javascript注射が戻ってきてバリデーションメッセージに現れて実行されることを好まなかった。この実装を使用することによって、ユーザーが指定した値を返すデフォルトのメッセージをオーバーライドします。

関連する問題