3

モデルの複合型用にいくつかのカスタム バインダーを作成しています。私のモデルは、独自の個別のバインダーを持つオブジェクトで構成されています。基本オブジェクトに汚い仕事をさせてから、標準の ModelBinder ルーティングに渡すことによって、カプセル化された複雑なオブジェクトを設定します。どうすればいいですか?

説明のために、非常に単純な例を作成しました。

モデルにこれらのオブジェクトが含まれているとします。

public class Person
{
    public string Name {get; set;}
    public PhoneNumber PhoneNumber {get; set;}
}

public class PhoneNumber
{
    public string AreaCode {get; set;}
    public string LocalNumber {get; set;}
}

そして、これらのモデルごとに次のバインダーがあります。PersonBinder が PhoneNumber を設定する必要があるわけではありませんが、PhoneNumber バインダーでコードを複製したくありません。標準の Binder ルーティングに戻すには、どのように委任しますか?

public class PersonBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext     bindingContext)
    {
        Person person = new Person();
        person.Name = bindingContext.ValueProvider.GetValue(String.Format("{0}.{1}", bindingContext.ModelName, "Name")).AttemptedValue

        // This is where I'd like to have the PhoneNumber object use binding from another customer ModelBinder.
        // Of course the bindingContext.ModelName should be updated to its current value + "PhoneNumber"
        person.PhoneNumber = ???;  // I don't want to explicitly call the PhoneNumberBinder it should go through standard Binding routing.  (ie.  ModelBinders.Binders[typeof(PhoneNumber)] = new PhoneNumberBinder();)

        return person;       
    }
}

public class PhoneNumberBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext     bindingContext)
    {
        PhoneNumber phoneNumber = new PhoneNumber();
        phoneNumber.AreaCode = bindingContext.ValueProvider.GetValue(String.Format("{0}.{1}", bindingContext.ModelName, "AreaCode")).AttemptedValue
        phoneNumber.LocalNumber = bindingContext.ValueProvider.GetValue(String.Format("{0}.{1}", bindingContext.ModelName, "LocalNumber")).AttemptedValue

        return phoneNumber;
    }
}

そしてもちろん、ModelBinders を Global.asax.cs ファイルに登録しました。

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ModelBinders.Binders[typeof(Person)] = new PersonBinder();
    ModelBinders.Binders[typeof(PhoneNumber)] = new PhoneNumberBinder();
}

ありがとう、

ジャスティン

4

2 に答える 2

2

さて、私はなんとか解決策を思い付くことができました。その有効性についてお気軽にコメントしてください。

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
     Person person = new Person();
     person.Name = bindingContext.ValueProvider.GetValue("Name").AttemptedValue

     if (bindingContext.ModelName == String.Empty)
     {
         bindingContext.ModelName = "PhoneNumber";
     }
     else
     {
         bindingContext.ModelName = bindingContext.ModelName + ".PhoneNumber";
     }

     PhoneNumber phoneNumber = new PhoneNumber();
     bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => phoneNumber, phoneNumber.GetType());

     IModelBinder binder = ModelBinders.Binders[typeof(PhoneNumber)];
     if (binder == null)
     {
          binder = ModelBinders.Binders.DefaultBinder;
     }

     person.PhoneNumber = binder.BindModel(controllerContext, bindingContext) as PhoneNumber;

     return person;                         
}

これが私がしたことの要約です。

  1. グローバルにアクセス可能なModelBinders.Bindersコレクションを使用してModelBinderを検索します(登録されていない場合はデフォルトにフォールバックします)
  2. バインドするモデルのModelMetadataProviderを作成します。
  3. bindingContextのModelNameプロパティを、入力しようとしているモデルプロパティ( "PhoneNumber")に設定します。
于 2010-07-06T20:37:04.347 に答える
0

バインダーを作成する代わりに、AutoMapper を使用してアクションで複雑なモデル構築を処理できます。

于 2010-07-06T07:45:16.370 に答える