Où placer AutoMapper.CreateMaps?

j'utilise AutoMapper dans une application ASP.NET MVC . On m'a dit que je devrais déplacer le AutoMapper.CreateMap ailleurs car ils ont beaucoup de frais généraux. Je ne suis pas trop sûr de la façon de concevoir mon application pour mettre ces appels dans un seul endroit.

j'ai une couche web, une couche service et une couche Données. Chaque projet qui lui est propre. J'utilise Ninject pour tout dire. Je vais utiliser AutoMapper dans les couches web et service.

alors quelle est votre configuration pour le CreateMap de AutoMapper ? Où avez-vous le mettre? Comment appelez-vous cela?

204
demandé sur Leniel Maccaferri 2011-07-26 08:25:02

10 réponses

n'a pas d'importance, tant que c'est une classe statique. Il s'agit de convention .

notre convention est que chaque" couche "(web, Services, données) a un seul fichier appelé AutoMapperXConfiguration.cs , avec une seule méthode appelée Configure() , où X est la couche.

la méthode Configure() appelle ensuite les méthodes private pour chaque zone.

Voici un exemple de notre config pour le niveau web:

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      ConfigureUserMapping();
      ConfigurePostMapping();
   }

   private static void ConfigureUserMapping()
   {
      Mapper.CreateMap<User,UserViewModel>();
   } 

   // ... etc
}

nous créons une méthode pour chaque" aggregate " (User, Post), de sorte que les choses sont bien séparées.

puis votre Global.asax :

AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc

c'est un peu comme une" interface de mots " - ne peut pas l'appliquer, mais vous vous y attendez, donc vous pouvez coder (et remanier) si nécessaire.

EDIT:

j'ai juste pensé que je mentionnerais que je maintenant utilisez AutoMapper profils , de sorte que l'exemple ci-dessus devient:

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      Mapper.Initialize(cfg =>
      {
        cfg.AddProfile(new UserProfile());
        cfg.AddProfile(new PostProfile());
      });
   }
}

public class UserProfile : Profile
{
    protected override void Configure()
    {
         Mapper.CreateMap<User,UserViewModel>();
    }
}

beaucoup plus propre / plus robuste.

210
répondu RPM1984 2016-10-18 08:19:08

vous pouvez vraiment le mettre n'importe où tant que votre projet web fait référence à l'assemblage dans lequel il se trouve. Dans votre situation, je le mettrais dans la couche service car elle sera accessible par la couche web et la couche service et plus tard si vous décidez de faire une application de console ou si vous faites un projet de test unitaire, la configuration de mapping sera également disponible dans ces projets.

dans votre Global.asax vous appellerez alors la méthode qui définit toutes vos cartes. Voir ci-dessous:

Fichier Automapperbootstrap.cs

public static class AutoMapperBootStrapper
{
     public static void BootStrap()
     {  
         AutoMapper.CreateMap<Object1, Object2>();
         // So on...


     }
}

Global.asax au début de l'application

appelez simplement

AutoMapperBootStrapper.BootStrap();

maintenant, certaines personnes vont argumenter contre cette méthode viole certains principes solides, qu'ils ont des arguments valables. Ici, ils sont pour la lecture.

configurer Automapper dans Bootstrapper viole-t-il le principe Open-Closed?

31
répondu Brett Allred 2017-05-23 11:54:55

mise à jour: l'approche affichée ici n'est plus valide puisque SelfProfiler a été supprimé à partir de L'AutoMapper v2.

j'adopte une approche similaire à celle de Thoai. Mais j'utiliserais la classe intégrée SelfProfiler<> pour gérer les cartes, puis j'utiliserais la fonction Mapper.SelfConfigure pour initialiser.

utilisant cet objet comme source:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string GetFullName()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}

et ceux-ci comme destination:

public class UserViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserWithAgeViewModel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Age { get; set; }
}

vous pouvez créer ces profils:

public class UserViewModelProfile : SelfProfiler<User,UserViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserViewModel> map)
    {
    //This maps by convention, so no configuration needed
    }
}

public class UserWithAgeViewModelProfile : SelfProfiler<User, UserWithAgeViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserWithAgeViewModel> map)
    {
    //This map needs a little configuration
        map.ForMember(d => d.Age, o => o.MapFrom(s => DateTime.Now.Year - s.BirthDate.Year));
    }
}

pour initialiser dans votre application, créer cette classe

 public class AutoMapperConfiguration
 {
      public static void Initialize()
      {
          Mapper.Initialize(x=>
          {
              x.SelfConfigure(typeof (UserViewModel).Assembly);
              // add assemblies as necessary
          });
      }
 }

Ajoutez cette ligne à votre global.asax.fichier cs: AutoMapperConfiguration.Initialize()

Maintenant, vous pouvez placer vos classes de cartographie où elles ont du sens pour vous et ne vous inquiétez pas d'une classe de cartographie monolithique.

15
répondu codeprogression 2015-08-24 17:00:19

pour ceux d'entre vous qui adhèrent à ce qui suit:

  1. utilisant un conteneur CIO
    151980920 "
  2. n'aime pas casser ouvert fermé pour ce

  3. n'aime pas les fichiers de configuration monolithiques

j'ai fait un combo entre les profils et l'optimisation de mon conteneur CIO:

configuration CIO:

public class Automapper : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Profile>().WithServiceBase());

        container.Register(Component.For<IMappingEngine>().UsingFactoryMethod(k =>
        {
            Profile[] profiles = k.ResolveAll<Profile>();

            Mapper.Initialize(cfg =>
            {
                foreach (var profile in profiles)
                {
                    cfg.AddProfile(profile);
                }
            });

            profiles.ForEach(k.ReleaseComponent);

            return Mapper.Engine;
        }));
    }
}

Exemple de Configuration:

public class TagStatusViewModelMappings : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
    }
}

exemple d'utilisation:

public class TagStatusController : ApiController
{
    private readonly IFooService _service;
    private readonly IMappingEngine _mapper;

    public TagStatusController(IFooService service, IMappingEngine mapper)
    {
        _service = service;
        _mapper = mapper;
    }

    [Route("")]
    public HttpResponseMessage Get()
    {
        var response = _service.GetTagStatus();

        return Request.CreateResponse(HttpStatusCode.Accepted, _mapper.Map<List<ViewModels.TagStatusViewModel>>(response)); 
    }
}

le compromis est que vous devez faire référence au Mapper par l'interface IMappingEngine au lieu du mapper statique, mais c'est une convention que je peux vivre avec.

15
répondu Marius 2015-08-24 17:01:23

toutes les solutions ci-dessus fournissent une méthode statique pour appeler (à partir d'app_start ou n'importe où) qu'elle devrait appeler d'autres méthodes pour configurer des parties de mapping-configuration. Mais, si vous avez une application modulaire, que les modules peuvent brancher et Hors d'application à tout moment, ces solutions ne fonctionne pas. Je suggère d'utiliser la bibliothèque WebActivator qui peut enregistrer certaines méthodes pour exécuter sur app_pre_start et app_post_start n'importe où:

// in MyModule1.dll
public class InitMapInModule1 {
    static void Init() {
        Mapper.CreateMap<User, UserViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule1), "Init")]

// in MyModule2.dll
public class InitMapInModule2 {
    static void Init() {
        Mapper.CreateMap<Blog, BlogViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// in MyModule3.dll
public class InitMapInModule3 {
    static void Init() {
        Mapper.CreateMap<Comment, CommentViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// and in other libraries...

vous pouvez installer WebActivator via NuGet.

14
répondu javad amiry 2015-12-14 16:50:59

en plus de la meilleure réponse, une bonne façon est d'utiliser Autofac IOC liberary pour ajouter un peu d'automatisation. Avec cela vous juste définissez vos profils indépendamment des initiations.

   public static class MapperConfig
    {
        internal static void Configure()
        {

            var myAssembly = Assembly.GetExecutingAssembly();

            var builder = new ContainerBuilder();

            builder.RegisterAssemblyTypes(myAssembly)
                .Where(t => t.IsSubclassOf(typeof(Profile))).As<Profile>();

            var container = builder.Build();

            using (var scope = container.BeginLifetimeScope())
            {
                var profiles = container.Resolve<IEnumerable<Profile>>();

                foreach (var profile in profiles)
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfile(profile);
                    });                    
                }

            }

        }
    }

et appelant cette ligne dans Application_Start méthode:

MapperConfig.Configure();

le code ci-dessus trouve toutes les sous-classes Profile et les lance automatiquement.

10
répondu Mahmoud Moravej 2014-07-27 13:39:25

mettre toute la logique de cartographie en un seul endroit n'est pas une bonne pratique pour moi. Parce que la classe de cartographie sera extrêmement grande et très difficile à maintenir.

je recommande de mettre la classe mapping avec la classe ViewModel dans le même fichier cs. Vous pouvez facilement naviguer vers la définition de mapping que vous voulez suivre cette convention. De plus, lors de la création de la classe mapping, vous pouvez faire référence aux propriétés ViewModel plus rapidement puisqu'elles sont dans le même fichier.

donc votre classe de modèle de vue ressemblera à:

public class UserViewModel
{
    public ObjectId Id { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public string Email { get; set; }

    public string Password { get; set; }
}

public class UserViewModelMapping : IBootStrapper // Whatever
{
    public void Start()
    {
        Mapper.CreateMap<User, UserViewModel>();
    }
}
7
répondu Van Thoai Nguyen 2011-07-27 04:23:58

De la nouvelle version de AutoMapper à l'aide de la méthode statique Mappeur.Map () est dépréciée. Vous pouvez donc ajouter MapperConfiguration comme propriété statique à MvcApplication (Global.asax.cs) et l'utiliser pour créer l'instance de Mapper.

App_Start

public class MapperConfig
{
    public static MapperConfiguration MapperConfiguration()
    {
        return new MapperConfiguration(_ =>
        {
            _.AddProfile(new FileProfile());
            _.AddProfile(new ChartProfile());
        });
    }
}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    internal static MapperConfiguration MapperConfiguration { get; private set; }

    protected void Application_Start()
    {
        MapperConfiguration = MapperConfig.MapperConfiguration();
        ...
    }
}

BaseController.cs

    public class BaseController : Controller
    {
        //
        // GET: /Base/
        private IMapper _mapper = null;
        protected IMapper Mapper
        {
            get
            {
                if (_mapper == null) _mapper = MvcApplication.MapperConfiguration.CreateMapper();
                return _mapper;
            }
        }
    }

https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API

5
répondu Andrey Burykin 2016-02-20 11:56:26

pour vb.net programmeurs utilisant la nouvelle Version (5.x) of AutoMapper.

Global.asax.vb:

Public Class MvcApplication
    Inherits System.Web.HttpApplication

    Protected Sub Application_Start()
        AutoMapperConfiguration.Configure()
    End Sub
End Class

AutoMapperConfiguration:

Imports AutoMapper

Module AutoMapperConfiguration
    Public MapperConfiguration As IMapper
    Public Sub Configure()
        Dim config = New MapperConfiguration(
            Sub(cfg)
                cfg.AddProfile(New UserProfile())
                cfg.AddProfile(New PostProfile())
            End Sub)
        MapperConfiguration = config.CreateMapper()
    End Sub
End Module

profils:

Public Class UserProfile
    Inherits AutoMapper.Profile
    Protected Overrides Sub Configure()
        Me.CreateMap(Of User, UserViewModel)()
    End Sub
End Class

Mapping:

Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)
3
répondu roland 2017-03-21 11:27:41

pour ceux qui sont (perdus) en utilisant:

  • WebAPI 2
  • SimpleInjector 3.1
  • AutoMapper 4.2.1 (Avec Profils)

Voici comment j'ai réussi à intégrer AutoMapper dans le " new way ". Également, un énorme merci à ce réponse (et question)

1 - Création d'un dossier dans le WebAPI projet appelé "ProfileMappers". Dans ce dossier, je place toutes mes classes de profils qui créent Mes Correspondances:

public class EntityToViewModelProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<User, UserViewModel>();
    }

    public override string ProfileName
    {
        get
        {
            return this.GetType().Name;
        }
    }
}

2-Dans Mon App_Start, j'ai un SimpleInjectorApiInitializer qui configure mon conteneur SimpleInjector:

public static Container Initialize(HttpConfiguration httpConfig)
{
    var container = new Container();

    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

    //Register Installers
    Register(container);

    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    //Verify container
    container.Verify();

    //Set SimpleInjector as the Dependency Resolver for the API
    GlobalConfiguration.Configuration.DependencyResolver =
       new SimpleInjectorWebApiDependencyResolver(container);

    httpConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

    return container;
}

private static void Register(Container container)
{
     container.Register<ISingleton, Singleton>(Lifestyle.Singleton);

    //Get all my Profiles from the assembly (in my case was the webapi)
    var profiles =  from t in typeof(SimpleInjectorApiInitializer).Assembly.GetTypes()
                    where typeof(Profile).IsAssignableFrom(t)
                    select (Profile)Activator.CreateInstance(t);

    //add all profiles found to the MapperConfiguration
    var config = new MapperConfiguration(cfg =>
    {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
    });

    //Register IMapper instance in the container.
    container.Register<IMapper>(() => config.CreateMapper(container.GetInstance));

    //If you need the config for LinqProjections, inject also the config
    //container.RegisterSingleton<MapperConfiguration>(config);
}

3 - Startup.cs

//Just call the Initialize method on the SimpleInjector class above
var container = SimpleInjectorApiInitializer.Initialize(configuration);

4 - puis, dans votre contrôleur, injectez simplement comme d'habitude une interface IMapper:

private readonly IMapper mapper;

public AccountController( IMapper mapper)
{
    this.mapper = mapper;
}

//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);
2
répondu jpgrassi 2017-05-23 12:10:35