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.