AutoMapper.CreateMaps nereye yerleştirilir?


216

AutoMapperBir ASP.NET MVCuygulamada kullanıyorum . Çok AutoMapper.CreateMapfazla ek yükü olduğu için başka bir yere taşınmam gerektiği söylendi . Bu aramaları sadece 1 yere koymak için uygulamamın nasıl tasarlanacağından emin değilim.

Bir web katmanı, hizmet katmanı ve bir veri katmanı var. Her biri kendi projesi. Ben Ninjecther şeyi DI için kullanın . AutoMapperHem web hem de hizmet katmanlarında kullanacağım .

Peki AutoMapper'CreateMap' için kurulumunuz nedir? Nereye koydun? Nasıl diyorsun?

Yanıtlar:


219

Statik bir sınıf olduğu sürece önemli değil. Her şey kongre ile ilgili .

Bizim kongre her bir "tabaka" (web, hizmetleri, veri) olarak adlandırılan tek bir dosya olmasıdır AutoMapperXConfiguration.csadlandırılan tek yöntemle, Configure(), Xtabakasıdır.

Configure()Daha sonra, yöntem çağırır privateher bir alan için yöntemler.

Web katmanı yapılandırmamızın bir örneği:

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

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

   // ... etc
}

Her "toplama" (Kullanıcı, Posta) için bir yöntem oluşturuyoruz, böylece işler güzelce ayrılıyor.

Sonra Global.asax:

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

Bir nevi "kelimelerin arayüzü" gibi - bunu zorlayamazsınız, ama beklersiniz, böylece gerekirse kodlayabilirsiniz (ve yeniden düzenleyen).

DÜZENLE:

Şimdi AutoMapper profillerini kullandığımdan bahsettiğimi düşündüm , bu yüzden yukarıdaki örnek şöyle olur:

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

Çok daha temiz / daha sağlam.


2
@ AliRızaAdıyahşi Her iki projenin de bir eşleme dosyası olmalıdır. Çekirdeğin AutoMapperCoreConfiguration olması ve kullanıcı arayüzünde AutoMapperWebConfiguration olması gerekir. Web yapılandırması, Çekirdek yapılandırmasından profilleri eklemelidir.
RPM1984

7
Mapper.InitializeHer bir Configuration sınıfında çağrı yapmak , eklenen önceki profillerin üzerine yazılıyor mu? Eğer öyleyse, Initialize yerine ne kullanılmalıdır?
Cody

4
Bu, web API projenizin hizmetinize ve alan katmanlarınıza referans olmasını sağlamıyor mu?
Chazt3n

3
Web -> Hizmet -> BLL -> DAL varsa. Varlıklarım DAL'ımda. Web veya Hizmet'ten DAL'ımla ilgili bir referans vermek istemiyorum. Nasıl başlatırım?
Vyache

19
AutoMapper 4.2'den itibaren Mapper.CreateMap()artık imkansız. 'Mapper.Map<TSource, TDestination>(TSource, TDestination)' is obsolete: 'The static API will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed. Use CreateMapper to create a mapper instance.'. Örneğinizi yeni gereksinimlere uyacak şekilde nasıl güncellersiniz?
ʙᴀᴋᴇʀ ʙᴀᴋᴇʀ

34

Web projeniz içinde bulunduğu montajı referans aldığı sürece herhangi bir yere koyabilirsiniz. Durumunuzda, web katmanı ve hizmet katmanı tarafından erişilebilir olacak şekilde hizmet katmanına koyarım ve daha sonra karar verirseniz bir konsol uygulaması yapın veya bir birim test projesi yapıyorsanız, eşleme yapılandırması da bu projelerden edinilebilir.

Global.asax'ınızda, tüm haritalarınızı ayarlayan yöntemi çağırırsınız. Aşağıya bakınız:

AutoMapperBootStrapper.cs Dosyası

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


     }
}

Uygulama başlangıcında Global.asax

sadece ara

AutoMapperBootStrapper.BootStrap();

Şimdi bazı insanlar bu yönteme karşı tartışacaklar, geçerli argümanları olan bazı SOLID ilkelerini ihlal ediyorlar. İşte okuma için.

Automapper'ın Bootstrapper'da yapılandırılması Açık-Kapalı Prensibi'ni ihlal ediyor mu?


13
Bu. Uygun "hardcore" mimariye doğru atılan her adım katlanarak daha fazla kod içeriyor gibi görünüyor. Bu kolay; buradaki kodlayıcıların% 99,9'u için yeterli olacaktır; ve iş arkadaşlarınız basitliği takdir edeceklerdir. Evet, herkes Açık-Kapalı prensibi ile ilgili konuyu okumalı, ancak herkes aynı zamanda ödünleşmeyi de düşünmelidir.
anon

AutoMapperBootStrapper sınıfını nerede oluşturdunuz?
user6395764

16

Güncelleme: Burada yayınlanan yaklaşım SelfProfiler, AutoMapper v2'den kaldırıldığı için artık geçerli değil .

Thoai ile benzer bir yaklaşım benimseyecektim. Ancak SelfProfiler<>, haritaları kullanmak için yerleşik sınıfı, ardından Mapper.SelfConfigurebaşlatmak için işlevi kullanırdım.

Bu nesneyi kaynak olarak kullanma:

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

Ve bunlar hedef olarak:

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

Bu profilleri oluşturabilirsiniz:

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

Uygulamanızda başlatmak için bu sınıfı oluşturun

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

Bu satırı global.asax.cs dosyanıza ekleyin: AutoMapperConfiguration.Initialize()

Artık haritalama sınıflarınızı sizin için anlamlı olan yerlere yerleştirebilir ve tek bir monolitik haritalama sınıfı için endişelenmeyebilirsiniz.


3
Sadece FYI, SelfProfiler sınıfı Automapper v2'den beri yok.
Matt Honeycutt

15

Aşağıdakilere bağlı olanlarınız için:

  1. ioc kap kullanarak
  2. bunun için açık kapalı kırmayı sevmiyorum
  3. yekpare bir yapılandırma dosyasını beğenme

Ben profiller ve benim ioc konteyner kaldıran arasında bir açılan yaptım:

IoC yapılandırması:

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

Yapılandırma örneği:

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

Kullanım örneği:

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

Bunun karşılığı, Mapper'a statik Mapper yerine IMappingEngine arabirimiyle başvurmanız gerektiğidir, ancak bu yaşayabileceğim bir kongre.


14

Yukarıdaki tüm çözümler, eşleme-yapılandırmanın bölümlerini yapılandırmak için diğer yöntemleri çağırması gerektiğini (app_start veya herhangi bir yerden) çağırmak için statik bir yöntem sağlar. Ancak, modüler bir uygulamanız varsa, bu modüller herhangi bir zamanda uygulamaya girip çıkabilir, bu çözümler işe yaramaz. WebActivatorÇalıştırmak için bazı yöntemler app_pre_startve app_post_startherhangi bir yerde kaydedebilirsiniz kütüphane kullanmanızı öneririz :

// 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...

WebActivatorNuGet ile kurabilirsiniz .


2
Son zamanlarda aynı sonuca vardım. Harita oluşturma kodunuzu tüketen koda yakın tutar. Bu yöntem, bir MVC denetleyicisini çok daha sürdürülebilir hale getirir.
mfras3r

Her yerden nasıl başlatabilirim, bir örnek verebilir misiniz? Blog bağlantılarınız çalışmıyor ...
Vyache

1
@Vyache çok açık! içinde MyModule1projenin (ya da her türlü projenizin adıdır) sadece adında bir sınıf oluşturmak InitMapInModule1ve dosya içinde kod koymak; diğer modüller için de aynısını yapın.
ravy amiry

Anladım, aslında denedim. Nuget'ten Sınıf kütüphaneme (DAL) WebActivator ekledim ve orada statik bir AutoMapperDalConfiguration sınıfı oluşturdum. Haritaları yapılandırmak ve başlatmak için @ RPM1984 uygulaması oluşturdum. Ben üzerinden profil kullanmıyorum. Teşekkür ederim.
Vyache

10

En iyi yanıta ek olarak, Autofac IoC liberary'yi bazı otomasyon eklemek için kullanmak iyi bir yoldur . Bu ile sadece bağımsız inisiyasyon Profillerinizi tanımlar.

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

            }

        }
    }

ve bu satırı Application_Startyöntemde çağırmak :

MapperConfig.Configure();

Yukarıdaki kod tüm Profil alt sınıflarını bulur ve otomatik olarak başlatır.


7

Tüm haritalama mantığını 1 yere koymak benim için iyi bir uygulama değil. Çünkü haritalandırma sınıfı son derece büyük ve bakımı çok zor olacak.

Eşleme öğelerini ViewModel sınıfıyla birlikte aynı cs dosyasına koymanızı öneririm. Bu kuralı izleyerek istediğiniz eşleme tanımına kolayca gidebilirsiniz. Ayrıca, eşleme sınıfı oluştururken, aynı dosyada oldukları için ViewModel özelliklerine daha hızlı başvurabilirsiniz.

Böylece görünüm modeli sınıfınız şöyle görünecektir:

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

9
Nasıl diyorsun?
Shawn Mclean

1
Dosya kuralı başına bir sınıfı
izlerdim


5

AutoMapper'ın yeni sürümünden Mapper.Map () statik yöntemi kullanılarak kullanımdan kaldırıldı. Böylece MapperConfiguration'ı MvcApplication'ye (Global.asax.cs) statik özellik olarak ekleyebilir ve Mapper örneği oluşturmak için kullanabilirsiniz.

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


3

Kullanan (kaybeden) olanlar için:

  • WebAPI 2
  • SimpleInjector 3.1
  • AutoMapper 4.2.1 (Profilli)

AutoMapper'ı " yeni şekilde " entegre etmeyi başardım . Ayrıca, bu cevap (ve soru) için büyük bir teşekkürler

1 - WebAPI projesinde "ProfileMappers" adlı bir klasör oluşturuldu. Bu klasöre eşlemelerimi oluşturan tüm profillerim sınıflarını yerleştiriyorum:

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

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

2 - App_Start'ımda, SimpleInjector kabımı yapılandıran bir SimpleInjectorApiInitializer'ım var:

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 - Ardından, kontrol cihazınıza genellikle bir IMapper arayüzü olarak enjekte edin:

private readonly IMapper mapper;

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

//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);

Bazı özelliklerde biraz değişiklik yaparak, bu yaklaşım MVC ile de mükemmel çalışır - teşekkürler adam!
Nick Coad

Lütfen
github'da

3

AutoMapper'in yeni Sürümünü (5.x) kullanan vb.net programcıları için.

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

Profilleri:

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

haritalama:

Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)

Cevabınızı denedim ama bu satırda hata gösteriyor: Dim config = Yeni MapperConfiguration (// Aşırı yük çözünürlüğü bu argümanlarla erişilebilir 'Yeni' çağrılamadığı için başarısız oldu: 'Genel Aşırı Yükler Alt Yeni (MapperConfigurationExpression Olarak ConfigExionion) Lütfen bana bu konuda yardım eder misin?
Barsan

@barsan: Tüm profil sınıflarını doğru şekilde yapılandırdınız mı (UserProfile ve PostProfile)? Benim için Automapper sürüm 5.2.0 ile çalışır.
roland

Yeni sürüm 6.0 yayınlandı. Yani Protected Overrides Sub Configure()kullanımdan kaldırıldı. Her şey aynı kalır, ancak bu hat şöyle olmalıdır:Public Sub New()
roland
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.