AutoMapper.CreateMaps를 어디에 배치합니까?


216

응용 프로그램 AutoMapper에서 사용 하고 ASP.NET MVC있습니다. 나는 움직여야한다고 들었다AutoMapper.CreateMap 그들이 많은 오버 헤드를 가지고 있기 때문에 다른 곳으로 . 이 전화를 한곳에 두도록 응용 프로그램을 디자인하는 방법을 잘 모르겠습니다.

웹 계층, 서비스 계층 및 데이터 계층이 있습니다. 각각 자체 프로젝트. 나는 Ninject모든 것을 DI로 사용 합니다. AutoMapper웹 및 서비스 계층 모두에서 활용하겠습니다 .

AutoMapper의 CreateMap 설정은 무엇 입니까? 어디에 두나요? 어떻게 부르세요?

답변:


219

정적 클래스 인 한 중요하지 않습니다. 컨벤션 에 관한 모든 것 입니다.

우리의 규약 은 각 "계층"(웹, 서비스, 데이터)에는이라는 AutoMapperXConfiguration.cs단일 메소드가있는 이라는 단일 파일 이 있으며 Configure(), 여기서는 X계층입니다.

Configure()방법은 다음 호출 private각 영역에 대한 방법을.

웹 티어 구성의 예는 다음과 같습니다.

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

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

   // ... etc
}

우리는 각각의 "집계"(User, Post)에 대한 메소드를 생성하므로 상황이 잘 분리됩니다.

그럼 당신의 Global.asax:

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

그것은 "단어의 인터페이스"와 비슷합니다-그것을 강요 할 수는 없지만, 그것을 예상 할 수 있습니다.

편집하다:

방금 AutoMapper profiles를 사용한다고 언급 했으므로 위의 예는 다음과 같습니다.

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

훨씬 더 깨끗하고 견고합니다.


2
@ AliRızaAdıyahşi 두 프로젝트 모두 매핑 파일이 있어야합니다. 코어에는 AutoMapperCoreConfiguration이 있어야하고 UI에는 AutoMapperWebConfiguration이 있어야합니다. 웹 구성은 핵심 구성에서 프로파일을 추가해야합니다.
— RPM1984

7
Mapper.Initialize각 구성 클래스에서 호출 하면 추가 된 이전 프로필을 덮어 씁니까? 그렇다면 초기화 대신 무엇을 사용해야합니까?
— 코디

4
이것이 웹 API 프로젝트가 서비스 및 도메인 계층에 대한 참조를 갖지 않습니까?
— Chazt3n

3
웹-> 서비스-> BLL-> DAL이있는 경우. 내 엔터티는 DAL에 있습니다. 웹 또는 서비스에서 DAL에 대한 참조를 제공하고 싶지 않습니다. 어떻게 초기화합니까?
— Vyache

19
AutoMapper 4.2부터는 Mapper.CreateMap()현재 지원되지 않습니다. '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.'. 새로운 요구 사항에 맞게 예제를 어떻게 업데이트 하시겠습니까?
— ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

34

웹 프로젝트가 어셈블리를 참조하는 한 실제로 어디에서나 배치 할 수 있습니다. 귀하의 상황에서는 웹 계층과 서비스 계층에서 액세스 할 수있는 서비스 계층에 배치하고 나중에 결정하면 콘솔 응용 프로그램을 수행하거나 단위 테스트 프로젝트를 수행하는 경우 해당 프로젝트에서도 매핑 구성을 사용할 수 있습니다.

Global.asax에서 모든 맵을 설정하는 메소드를 호출합니다. 아래를보십시오 :

파일 AutoMapperBootStrapper.cs

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


     }
}

응용 프로그램 시작시 Global.asax

그냥 전화

AutoMapperBootStrapper.BootStrap();

이제 일부 사람들은이 방법에 반대하여 SOLID 원칙을 어 기고 있다고 주장합니다. 여기 그들은 독서를위한 것입니다.

Bootstrapper에서 Automapper를 구성하면 공개 폐쇄 원칙을 위반합니까?


13
이. 적절한 "하드 코어"아키텍처를 향한 모든 단계는 기하 급수적으로 더 많은 코드를 필요로합니다. 이것은 쉬워요; 99.9 %의 코더 만 있으면 충분합니다. 동료들이 단순성을 높이 평가할 것입니다. 그렇습니다. 모든 사람은 공개 폐쇄 원칙에 관한 문제를 읽어야하지만 모든 사람들은 트레이드 오프에 대해서도 생각해야합니다.
— anon

AutoMapperBootStrapper 클래스를 어디에서 만들었습니까?
— user6395764

16

업데이트 : 여기에 게시 된 접근 방식은 SelfProfilerAutoMapper v2에서 제거되었으므로 더 이상 유효하지 않습니다 .

나는 Thoai와 비슷한 접근법을 취할 것입니다. 그러나 내장 SelfProfiler<>클래스를 사용하여 맵을 처리 한 다음 Mapper.SelfConfigure함수를 사용하여 초기화합니다.

이 객체를 소스로 사용 :

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

그리고 이것들은 목적지입니다 :

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

다음과 같은 프로파일을 작성할 수 있습니다.

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

애플리케이션에서 초기화하려면이 클래스를 작성하십시오.

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

global.asax.cs 파일에이 줄을 추가하십시오 : AutoMapperConfiguration.Initialize()

이제 매핑 클래스를 이해하기 쉬운 곳에 배치하고 하나의 모 놀리 식 매핑 클래스에 대해 걱정하지 않아도됩니다.


3
참고로, SelfProfiler 클래스는 Automapper v2 이후로 사라졌습니다.
— Matt Honeycutt

15

다음을 준수하는 사람들을 위해 :

  1. ioc 컨테이너 사용
  2. 이 폐쇄 폐쇄를 좋아하지 않아
  3. 모 놀리 식 설정 파일을 좋아하지 않습니다

나는 프로파일과 콤보를 활용하여 ioc 컨테이너를 활용했습니다.

IoC 구성 :

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

구성 예 :

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

사용 예 :

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

단점은 정적 매퍼 대신 IMappingEngine 인터페이스로 매퍼를 참조해야한다는 것입니다. 그러나 그것은 내가 살 수있는 관습입니다.


14

위의 모든 솔루션은 매핑 구성의 일부를 구성하기 위해 다른 방법을 호출해야하는 정적 메소드 (app_start 또는 어디서나 호출)를 제공합니다. 그러나 모듈 식 응용 프로그램을 사용하는 경우 해당 모듈이 언제든지 응용 프로그램에 연결되거나 연결 해제 될 수 있지만 이러한 솔루션은 작동하지 않습니다. 내가 사용하는 것이 좋습니다 WebActivator에 실행하는 몇 가지 방법을 등록 할 수 있습니다 라이브러리를 app_pre_start하고 app_post_start있는 경우 :

// 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을 통해 설치할 수 있습니다 .


2
나는 최근에 같은 결론을 내렸다. 지도 작성 코드를 사용하는 코드와 가깝게 유지합니다. 이 방법을 사용하면 MVC 컨트롤러의 유지 관리가 훨씬 쉬워집니다.
— mfras3r

어디에서나 시작하려면 어떻게해야합니까? 블로그 링크가 작동하지 않습니다 ...
— Vyache

1
@Vyache 그것은 분명하다! 의 MyModule1프로젝트 (또는 무엇이든 프로젝트의 이름입니다) 단지라는 이름의 클래스를 생성 InitMapInModule1하고 파일 안에 코드를 넣어; 다른 모듈의 경우에도 동일하게 수행하십시오.
— ravy amiry

잡았다, 나는 그것을 실제로 시도했다. Nuget의 WebActivator를 클래스 라이브러리 (DAL)에 추가하고 거기에 정적 AutoMapperDalConfiguration 클래스를 만들었습니다. 맵을 구성하고 초기화하기 위해 @ RPM1984 구현을 만들었습니다. 프로필을 사용하지 않습니다. 감사합니다.
— Vyache

10

가장 좋은 답변 외에도 Autofac IoC를 사용하여 자동화를 추가 하는 것이 좋습니다 . 이것으로 당신은 단지 에 관계없이 입문 당신의 프로파일을 정의합니다.

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

            }

        }
    }

Application_Start메소드 에서이 줄을 호출하십시오 .

MapperConfig.Configure();

위의 코드는 모든 프로파일 하위 클래스를 찾아서 자동으로 시작합니다.


7

모든 매핑 논리를 하나의 위치에 두는 것은 좋은 습관이 아닙니다. 매핑 클래스는 매우 크고 유지 관리가 매우 어렵 기 때문입니다.

동일한 CSS 파일에 ViewModel 클래스와 함께 매핑 항목을 넣는 것이 좋습니다. 이 규칙에 따라 원하는 매핑 정의로 쉽게 이동할 수 있습니다. 또한 매핑 클래스를 만드는 동안 동일한 파일에 있기 때문에 ViewModel 속성을 더 빠르게 참조 할 수 있습니다.

따라서 뷰 모델 클래스는 다음과 같습니다.

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
어떻게 부릅니까?
— Shawn Mclean

1
파일 규칙 당 하나의 클래스를 따릅니다. stackoverflow.com/q/2434990/1158845
— Umair

비슷한 영혼은 Velir의 블로그 MVC에서 AutoMapper의 맵 구성 구성
— xmedeko

5

정적 메소드 Mapper.Map ()을 사용하는 새 버전의 AutoMapper에서는 더 이상 사용되지 않습니다. 따라서 MapperConfiguration을 정적 속성으로 MvcApplication (Global.asax.cs)에 추가하고이를 사용하여 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


3

(잃어버린) 사람들을 위해 :

  • WebAPI 2
  • SimpleInjector 3.1
  • AutoMapper 4.2.1 (프로파일 포함)

다음은 " 새로운 방식 " 으로 AutoMapper 통합을 관리 한 방법 입니다. 또한 이 답변 (및 질문)에 큰 감사

1- "ProfileMappers"라는 WebAPI 프로젝트에 폴더를 작성했습니다. 이 폴더에는 매핑을 만드는 모든 프로파일 클래스를 배치합니다.

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

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

2-내 App_Start에는 SimpleInjector 컨테이너를 구성하는 SimpleInjectorApiInitializer가 있습니다.

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-그런 다음 컨트롤러에 일반적으로 IMapper 인터페이스와 같이 주입하십시오.

private readonly IMapper mapper;

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

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

약간의 세부 사항을 약간 조정하면이 접근법은 MVC에서도 훌륭하게 작동합니다. 감사합니다!
— Nick Coad

GitHub의에 데모 예제를 추가하십시오
— 모하마드 Daliri

3

AutoMapper의 새 버전 (5.x)을 사용하는 vb.net 프로그래머 용.

Global.asax.vb :

Public Class MvcApplication
    Inherits System.Web.HttpApplication

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

오토 매퍼 구성 :

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

프로필 :

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

매핑 :

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

귀하의 답변을 시도했지만 다음 줄에 오류가 표시됩니다. Dim config = New MapperConfiguration (// 다음 인수로 액세스 가능한 'New'를 호출 할 수 없으므로 과부하 해결에 실패했습니다. 'Public Overloads Sub New (configurationExpression As MapperConfigurationExpression) Can 당신은 저를 도와주세요?
— barsan

@barsan : 모든 프로파일 클래스를 올바르게 구성 했습니까 (UserProfile 및 PostProfile)? 나를 위해 그것은 Automapper 버전 5.2.0에서 작동합니다.
— roland

새로운 버전 6.0이 출시되었습니다. 따라서 Protected Overrides Sub Configure()더 이상 사용되지 않습니다. 모든 것이 동일하게 유지되지만이 라인은 다음과 같아야합니다.Public Sub New()
— roland
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.