2009-11-23 3 views
2

MISE À JOUR: ce genre de choses a évolué dans un beau projet, voir à http://valueinjecter.codeplex.com Convention Simple Automapper pour la cartographie à deux voies (entités à/de ViewModels)


vérifier, je viens d'écrire un automapper simple, il prend la valeur de la propriété avec le même nom et type d'un objet et le met dans un autre, et vous pouvez ajouter des exceptions (ifs, commutateur) pour chaque type que vous pourriez avoir besoin

alors dites-moi ce que vous en pensez il ?

je l'ai fait pour que je puisse faire quelque chose comme ceci:

Product –> ProductDTO 

ProductDTO –> Product 

qui est la façon dont il a commencé:

J'utilise le type « objet » dans mes entrées/Dto/ViewModels pour Dropdowns parce que je envoyer au html IEnumerable <SelectListItem> et je reçois un tableau de chaînes de clés sélectionnées Retour

public void Map(object a, object b) 
    { 
     var pp = a.GetType().GetProperties(); 
     foreach (var pa in pp) 
     { 
      var value = pa.GetValue(a, null); 

      // property with the same name in b 
      var pb = b.GetType().GetProperty(pa.Name); 
      if (pb == null) 
      { 
       //no such property in b 
       continue; 
      } 

      if (pa.PropertyType == pb.PropertyType) 
      { 
       pb.SetValue(b, value, null); 
      } 

     } 
    } 

UPDATE: le véritable usage:
les méthodes de génération (entrée = Dto):

 public static TI BuildInput<TI, T>(this T entity) where TI: class, new() 
     { 
      var input = new TI(); 
      input = Map(entity, input) as TI; 
      return input; 
     } 

     public static T BuildEntity<T, TI, TR>(this TI input) 
      where T : class, new() 
      where TR : IBaseAdvanceService<T> 
     {    
      var id = (long)input.GetType().GetProperty("Id").GetValue(input, null); 
      var entity = LocatorConfigurator.Resolve<TR>().Get(id) ?? new T(); 
      entity = Map(input, entity) as T; 
      return entity; 
     } 

     public static TI RebuildInput<T, TI, TR>(this TI input) 
      where T: class, new() 
      where TR : IBaseAdvanceService<T> 
      where TI : class, new() 
     { 

       return input.BuildEntity<T, TI, TR>().BuildInput<TI, T>(); 
      } 

dans le contrôleur:

public ActionResult Create() 
    { 
     return View(new Organisation().BuildInput<OrganisationInput, Organisation>()); 
    } 

    [AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult Create(OrganisationInput o) 
    { 
     if (!ModelState.IsValid) 
     { 
      return View(o.RebuildInput<Organisation,OrganisationInput, IOrganisationService>());     
     } 
     organisationService.SaveOrUpdate(o.BuildEntity<Organisation, OrganisationInput, IOrganisationService>()); 
     return RedirectToAction("Index"); 
    } 

La vraie méthode Carte

public static object Map(object a, object b) 
     { 
      var lookups = GetLookups(); 

      var propertyInfos = a.GetType().GetProperties(); 
      foreach (var pa in propertyInfos) 
      { 
       var value = pa.GetValue(a, null); 

       // property with the same name in b 
       var pb = b.GetType().GetProperty(pa.Name); 
       if (pb == null) 
       { 
        continue; 
       } 

       if (pa.PropertyType == pb.PropertyType) 
       { 
        pb.SetValue(b, value, null); 
       } 
       else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(LookupItem)) 
       { 
        pb.SetValue(b, (pa.GetValue(a, null) as LookupItem).GetSelectList(pa.Name), null); 
       } 
       else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(object)) 
       { 
        pb.SetValue(b, pa.GetValue(a, null).ReadSelectItemValue(), null); 
       } 
       else if (pa.PropertyType == typeof(long) && pb.PropertyType == typeof(Organisation)) 
       { 
        pb.SetValue(b, pa.GetValue<long>(a).ReadOrganisationId(), null); 
       } 
       else if (pa.PropertyType == typeof(Organisation) && pb.PropertyType == typeof(long)) 
       { 
        pb.SetValue(b, pa.GetValue<Organisation>(a).Id, null); 
       } 
      } 

      return b; 
     } 
+1

hors sujet - j'adore votre première ligne à la 'question': "check this out ..."! haha. J'ai pensé à faire cela mais pas eu les boules –

+0

@cottsak oui c'est vraiment bonne ouverture: D, jamais vu un comme ça – Omu

+0

@ChuckNorris - Pouvez-vous s'il vous plaît préciser pourquoi Automapper (initialement) n'était pas recommandé pour ViewModel <-> mappage de modèle ? Je me rends compte que c'est un commentaire très en retard dans cette discussion et beaucoup de choses pourraient avoir avancé dans Automapper et ValueInjector, mais néanmoins, si vous avez un point remarquable sur pourquoi éviter Automapper, s'il vous plaît laissez-nous savoir. – Faredoon

Répondre

6

One chose que vous pouvez ajouter est de mettre en cache les bits de réflexion. Si vous mappez deux fois un objet, vous ne voudrez probablement plus rechercher tous les objets de réflexion. En outre, des choses comme GetValue et SetValue sont assez lentes, je suis passé aux délégués en retard + Reflection.Emit pour accélérer les choses.

+0

:) eh bien, je ne mappe pas un type spécifique à un autre, c'est plus comme la cartographie par propriété pour tous les types – Omu

+0

Je vais étudier comment pour accélérer les choses cependant, merci d'avoir suggéré la réflexion.Emission et délégués en retard, pas sûr de la mise en cache – Omu

+2

La mise en cache a définitivement aidé AutoMapper - les appels GetFields et GetProperties peuvent être un goulot d'étranglement - exécutez ceci dans quelque chose comme dotTrace et vous pouvez voir où le temps est passé. Ce type de codage est plutôt cool, amusez-vous avec! –

6

Il suffit d'utiliser AutoMapper. C'est bien, mais ça va devenir un mini-projet.

Juste quelques choses AM (le vrai) est fait:

  • rapports si vous avez des propriétés qui ne peuvent pas être mis en correspondance avec
  • objets aplanissement
  • fournissant des crochets pour vous de personnaliser certains aspects , pas dans un communiqué grand commutateur
  • utilisant Expression.Compile pour des raisons plutôt que de perf réflexion directement

Mais c'est certainement un espace intéressant à explorer, et l'idée de la cartographie automatique est certainement utile.

Un peu comme DI en 15 ou 33 lignes vs NInject ou ses amis - cool, mais pourquoi ?. Je suppose que vous avez lu article and comments regarding 2 way mapping on Jimmy's blog?

+0

J'ai l'intention de l'utiliser pour éditer des entités (pour mapper de mes modèles dans mes viewmodels et viceversa), je voulais utiliser automapper pour cela, mais Jimmy m'a dit de ne pas :) – Omu

+1

@Ou, assez juste. Le point pour moi cependant est que votre «solution» superficiellement simple cache un iceberg de cas spéciaux, cool et tout ce qu'il est, et en le complétant, vous vous retrouverez avec une quantité de travail non triviale. Le point de cottsak demandant qu'est-ce que vous faites cette cartographie est à considérer. Mais vous ne nous avez pas donné beaucoup de choses à propos de ce que votre scénario * réel * est. Pour tout ce que nous savons, vous pourriez être l'utilisateur idéal .NET RIA Services: P Jimmy a-t-il dit de ne pas ajouter de correspondance bidirectionnelle à AM ou de ne pas y aller parce que ce n'est pas un cas d'utilisation? Forking pourrait être mieux que de réinventer –

+0

J'ai mis à jour le texte de la question, vous pouvez voir maintenant comment je l'utilise – Omu

3

Juste une pensée:

On peut se demander ce que le point d'une abstraction est si l'abstraction est si facilement mis en correspondance qui s'Abstraite.

3

vous pouvez ajouter des exceptions (ifs, switch) pour chaque type, vous devrez peut-être

Tu ne vas pas le faire. Sérieusement. Les instructions de cas agissant sur les types d'objet sont de mauvais style POO. Je veux dire, vraiment mauvais. Comme conduire avec une boisson de vodka dans votre estomac. Cela peut fonctionner pendant un certain temps, mais vous finirez par avoir des ennuis. OK Jimmy vous a dit de ne pas utiliser AutoMapper ... mais je parie qu'il voulait dire autre chose. Maintenant, avez-vous inventé quelque chose de différent - quelque chose qui rend Jimmy heureux? ;-) Non, vous venez de créer votre propre AutoMapper à moitié roulé. Et Jimmy vous a dit de ne pas l'utiliser! ;-)

Alors, voici ma suggestion: ignorer ce que Jimmy dit, il suffit de vous penser .. et utiliser AutoMapper de

+0

bien oui, je pourrais, mais avec ce genre de choses, je peux très facilement faire mes propres conventions; Je l'utilise pour le moment dans 2 méthodes d'une classe "Builder": BuildEntity BuildInput , donc je mets automatiquement toutes mes entités à toutes mes entrées (ViewModels), sans avoir besoin de spécifier un mapping (CreateMap) pour chaque paire de types – Omu

+0

Vous pouvez avoir vos méthodes BuildEntity <> pour appeler AutoMapper CreateMap/Map en interne. De plus, vous pouvez l'avoir pour vérifier si CreateMap est déjà enregistré, donc vous avez la possibilité de remplacer le mapping par défaut - au lieu de hard-coding dans les switchs qui est le style BAD BAD. Pourtant, je ne vois pas de raison de réinventer AutoMapper. – queen3

+0

J'ai mis à jour le texte de la question, vous pouvez voir maintenant comment je l'utilise – Omu

Questions connexes