3

Je crée quelques classeurs personnalisés pour les types complexes dans mon modèle. Mon modèle est composé d'objets qui ont leurs propres liants séparés. Je veux que l'objet de base fasse son sale boulot, puis remplisse l'objet complexe qu'il encapsule en passant au routage ModelBinder standard. Comment puis-je faire cela?Comment puis-je appeler UpdateModel à partir d'un ModelBinder personnalisé? (MVC)

Pour l'illustration j'ai créé un exemple très simple.

Dites que mon modèle contient ces objets.

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;} 
} 

Et j'ai les classeurs suivants pour chacun de ces modèles. Non que PersonBinder doit remplir le PhoneNumber mais ne souhaite pas dupliquer le code dans le classeur PhoneNumber. Comment délègue-t-il au routage Binder standard?

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; 
    } 
} 

Et bien sûr, j'ai enregistré mes ModelBinders dans le fichier Global.asax.cs.

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

    RegisterRoutes(RouteTable.Routes); 

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

Merci,

Justin

Répondre

2

Eh bien, j'ai réussi à trouver une solution. N'hésitez pas à commenter sa validité.

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;       
} 

Voici un résumé de ce que j'ai fait.

  1. Lookup l'ModelBinder en utilisant la collection ModelBinders.Binders globalement accessible (REPLI par défaut si l'on est pas enregistré)
  2. Créer le ModelMetadataProvider pour le modèle que je suis liant à.
  3. Définissez la propriété ModelName du bindingContext sur la propriété de modèle que j'essaie de remplir ("PhoneNumber").
0

Plutôt que d'écrire un classeur que vous pourriez utilisateur AutoMapper et gérer la construction de modèle complexe dans l'action.

+0

Merci pour la suggestion, mais je ne pense pas que l'AutoMapper est l'outil pour moi. On dirait une solution de contournement. Je suis sûr que les liants devraient supporter cette fonctionnalité nativement. – Justin