2009-11-18 8 views
1

J'ai le crochet suivant dans mon Global.aspxQuel est le problème avec mon crochet AutoMapper Customer ValueResolver?

protected void Application_Start() 
    { 
     AreaRegistration.RegisterAllAreas(); 
     RegisterRoutes(RouteTable.Routes); 
     AutoMapper.Mapper.CreateMap<FormCollection, Models.IAmACustomer>().ForAllMembers(form => form.ResolveUsing<Models.FormCollectionValueResolver<Models.IAmACustomer>>()); 
    } 

Mon contrôleur:

[HttpPost] 
    public ActionResult Create(FormCollection formCollection) 
    { 

     var customer = AutoMapper.Mapper.Map<FormCollection,Models.IAmACustomer> (formCollection,null); 
    } 

Cette ligne exécute mais mon résolveur personnalisé est jamais appelé.

Le résolveur ressemble à ceci:

public class FormCollectionValueResolver<TDestination>:ValueResolver<FormCollection,TDestination> 
{ 
//Code removed for brevity 
} 

L'application compile et fonctionne, mais sans le résolveur personnalisé, rien ne vient dans l'objet, il crée simplement un objet simulé avec jets d'exception se accesseurs.

Répondre

0

La raison pour laquelle la FormCollectionValueResolver<Customer> ne sera jamais appelé est que la méthode ForAllMembers() itère sur toutes vos correspondances de propriétés, telles que définies par la méthode ForMember(), appliquer les options membres spécifiées. Cependant, dans l'exemple de code fourni, aucun mappage de propriété n'a été défini, ainsi le résolveur n'est jamais appelé.

Voici un exemple d'utilisation de la méthode ForAllMembers().

[Test] 
public void AutoMapperForAllMembersTest() 
{ 
    Mapper.CreateMap<Source, Destination>() 
     .ForMember(dest => dest.Sum, 
      opt => opt.ResolveUsing<AdditionResolver>()) 
     .ForMember(dest => dest.Difference, 
      opt => opt.ResolveUsing<SubtractionResolver>()) 
     .ForAllMembers(opt => opt.AddFormatter<CustomerFormatter>()); 

    Source source = new Source(); 
    source.Expression = new Expression 
    { 
     LeftHandSide = 2, 
     RightHandSide = 1 
    }; 

    Destination destination = Mapper.Map<Source, Destination>(source); 
    Assert.That(destination.Sum, Is.EqualTo("*3*")); 
    Assert.That(destination.Difference, Is.EqualTo("*1*")); 
}  

public class Expression 
{ 
    public int LeftHandSide { get; set; } 

    public int RightHandSide { get; set; } 
} 

public class Source 
{ 
    public Expression Expression { get; set; } 
} 

public class Destination 
{ 
    public string Sum { get; set; } 

    public string Difference { get; set; } 
} 

public class AdditionResolver : ValueResolver<Source, int> 
{ 
    protected override int ResolveCore(Source source) 
    { 
     Expression expression = source.Expression; 
     return expression.LeftHandSide + expression.RightHandSide; 
    } 
} 

public class SubtractionResolver : ValueResolver<Source, int> 
{ 
    protected override int ResolveCore(Source source) 
    { 
     Expression expression = source.Expression; 
     return expression.LeftHandSide - expression.RightHandSide; 
    } 
} 

public class CustomerFormatter : IValueFormatter 
{ 
    public string FormatValue(ResolutionContext context) 
    { 
     return string.Format("*{0}*", context.SourceValue); 
    } 
} 
+0

Un convertisseur de type ne contourne pas le mappage manuel des noms pour chaque objet métier que j'utilise dans tous les projets que j'espère que l'automapper va exécuter si je peux comprendre comment l'utiliser pour cela. Je ne vois pas quel est l'avantage de faire la conversion dans le modelbinder plutôt que dans le contrôleur? Aussi, cette réponse ne semble pas résoudre les problèmes si la validation ou la conversion a des problèmes comme une entrée d'utilisateur invalide pour laquelle l'objet client devrait lancer des exceptions. – Maslow

+0

J'étais un peu hâtif dans ma réponse originale. J'ai fait un montage complet de celui-ci. J'espère que ma nouvelle réponse sera un peu plus utile. – mrydengren

+0

Donc, si vous comprenez cette réponse correctement, le 'ForAllMembers()' ne s'applique qu'aux propriétés '.ForMember' définies par l'utilisateur. Existe-t-il un moyen de définir un générique ici où les noms de propriété dont vous avez besoin sont sans définir explicitement chacun? – Maslow

0

Vous devriez considérer le creusement de fossés FormCollection tout à fait:

http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

Fondamentalement, vous penchez sur les vues fortement typées + types ViewModel créé sur mesure pour les formulaires. Ces formulaires ont des attributs comme les attributs de validation, ce qui vous permet de les exécuter via des cadres de validation. Si c'est valide, alors seulement mettez-vous à jour votre modèle de persistance à partir du formulaire posté. Nous évitons de créer des objets de domaine directement à partir du formulaire affiché.

+0

Si je peux obtenir l'automapper pour faire ce que je veux, je peux éviter de devoir concevoir des ViewModelTypes personnalisés pour les formulaires. – Maslow

+0

Ensuite, je suggère un IObjectMapper personnalisé - il offrira plus de flexibilité pour vous et un crochet plus propre. Si vous le faites fonctionner, je n'ai aucun problème à l'ajouter à AM. –

+0

Je ne pouvais pas trouver mon chemin à travers la conception d'un IObjectMapper personnalisé, des pointeurs ou des idées sur la façon dont je pourrais commencer cela? J'ai essayé plusieurs fois et je ne pouvais pas comprendre comment cette classe fonctionnait ou serait utilisée à cette fin. – Maslow