응용 프로그램 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 원칙을 어 기고 있다고 주장합니다. 여기 그들은 독서를위한 것입니다.
답변
업데이트 : 여기에 게시 된 접근 방식은 SelfProfiler
AutoMapper 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()
이제 매핑 클래스를 이해하기 쉬운 곳에 배치하고 하나의 모 놀리 식 매핑 클래스에 대해 걱정하지 않아도됩니다.
답변
다음을 준수하는 사람들을 위해 :
- ioc 컨테이너 사용
- 이 폐쇄 폐쇄를 좋아하지 않아
- 모 놀리 식 설정 파일을 좋아하지 않습니다
나는 프로파일과 콤보를 활용하여 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...
WebActivator
NuGet을 통해 설치할 수 있습니다 .
답변
가장 좋은 답변 외에도 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>();
}
}