[asp.net-mvc] AutoMapper.CreateMaps를 어디에 배치합니까?

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

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

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



답변

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

우리의 규약 은 각 “계층”(웹, 서비스, 데이터)에는이라는 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>();
    }
}

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


답변

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

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를 구성하면 공개 폐쇄 원칙을 위반합니까?


답변

업데이트 : 여기에 게시 된 접근 방식은 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()

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


답변

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

  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 인터페이스로 매퍼를 참조해야한다는 것입니다. 그러나 그것은 내가 살 수있는 관습입니다.


답변

위의 모든 솔루션은 매핑 구성의 일부를 구성하기 위해 다른 방법을 호출해야하는 정적 메소드 (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을 통해 설치할 수 있습니다 .


답변

가장 좋은 답변 외에도 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();

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


답변

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

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