asp.net-mvc - AutoMapper.CreateMaps를 배치 할 위치는 어디입니까?




design-patterns configuration (9)

그것은 정적 클래스 인 한 중요하지 않습니다. 국제 대회 다 .

우리의 규약 은 각 "레이어"(웹, 서비스, 데이터)에는 Configure() 라는 단일 메서드가있는 AutoMapperXConfiguration.cs 라는 단일 파일이 있습니다. 여기서 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>();
    }
}

훨씬 더 깔끔하고 강력합니다.

ASP.NET MVC 응용 프로그램에서 AutoMapper 를 사용하고 있습니다. AutoMapper.CreateMap 을 오버 헤드가 많은 곳으로 옮겨야한다고 들었습니다. 나는이 애플리케이션을 단 한 곳에서만 사용하도록 애플리케이션을 설계하는 방법을 잘 모르겠습니다.

웹 레이어, 서비스 레이어 및 데이터 레이어가 있습니다. 각자의 프로젝트. 나는 모든 것을 DI하기 위해 Ninject 를 사용한다. 웹 및 서비스 레이어에서 AutoMapper 를 활용할 것입니다.

그렇다면 AutoMapper 의 CreateMap에 대한 설정은 무엇입니까? 어디에 넣었 니? 어떻게 부르죠?


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

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

AutoMapper를 " https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API "통합 관리하는 방법은 다음과 같습니다. 또한,이 대답 (및 질문)에 감사

1 - WebAPI 프로젝트에서 "ProfileMappers"라는 폴더를 만들었습니다. 이 폴더에서 내 매핑을 만드는 모든 프로필 클래스를 배치합니다.

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

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

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

프로필 :

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)

업데이트 : SelfProfiler 가 AutoMapper v2에서 제거 SelfProfiler 여기 게시 된 접근 방식은 더 이상 유효하지 않습니다.

나는 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()

이제는 하나의 모 놀리 식 매핑 클래스에 대해 걱정하지 않고 매핑 클래스를 배치 할 수 있습니다.


정적 메소드 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


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

  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 또는 기타 위치에서) 매핑 구성의 일부를 구성하는 다른 메소드를 호출해야한다고합니다. 그러나 모듈 형 응용 프로그램을 사용하는 경우 언제든지 해당 모듈을 응용 프로그램에 연결할 수 있습니다. 이러한 솔루션은 작동하지 않습니다. app_pre_startapp_post_start 에서 실행할 수있는 메소드를 등록 할 수있는 WebActivator 라이브러리를 사용하는 것이 좋습니다.

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

NuGet을 통해 WebActivator 를 설치할 수 있습니다.


가장 좋은 대답 외에도, Autofac IoC liberary를 사용하여 자동화를 추가하는 것이 좋습니다. 이를 통해 시작과 상관없이 프로파일을 정의 할 수 있습니다.

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

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


1) 원하는 위치에 파비콘을 넣고이 태그를 페이지 헤드에 추가 할 수 있습니다

<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />

일부 브라우저는 기본적으로 /favicon.ico에서 파비콘을 얻으려고 시도하지만 IgnoreRoute를 사용해야합니다.

2) 브라우저가 다른 디렉토리에서 파비콘 요청을하면 404 오류가 발생하고 마스터 페이지에서 대답 1의 링크 태그를 사용하면 브라우저에서 원하는 파비콘을 얻을 수 있습니다.





asp.net-mvc design-patterns configuration automapper