두 개의 모델 클래스가 있다고 가정 해 보겠습니다.
public class People {
public string FirstName {get;set;}
public string LastName {get;set;}
}
또한 수업 전화 :
public class Phone {
public string Number {get;set;}
}
그리고 다음과 같이 PeoplePhoneDto로 변환하고 싶습니다.
public class PeoplePhoneDto {
public string FirstName {get;set;}
public string LastName {get;set;}
public string PhoneNumber {get;set;}
}
내 컨트롤러에서 내가 가지고 있다고 가정 해 봅시다.
var people = repository.GetPeople(1);
var phone = repository.GetPhone(4);
// normally, without automapper I would made
return new PeoplePhoneDto(people, phone) ;
이 시나리오에 대한 예를 찾을 수없는 것 같습니다. 이것이 가능한가 ?
참고 :이 질문에 대한 예제는 실제가 아닙니다.
답변
여러 소스를 단일 대상에 직접 매핑 할 수 없습니다 . Andrew Whitaker 답변에 설명 된대로지도를 하나씩 적용해야합니다 . 따라서 모든 매핑을 정의해야합니다.
Mapper.CreateMap<People, PeoplePhoneDto>();
Mapper.CreateMap<Phone, PeoplePhoneDto>()
.ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));
그런 다음 이러한 매핑 중 하나를 사용하여 대상 개체를 만들고 생성 된 개체에 다른 매핑을 적용합니다. 이 단계는 매우 간단한 확장 방법으로 단순화 할 수 있습니다.
public static TDestination Map<TSource, TDestination>(
this TDestination destination, TSource source)
{
return Mapper.Map(source, destination);
}
사용법은 매우 간단합니다.
var dto = Mapper.Map<PeoplePhoneDto>(people)
.Map(phone);
답변
Tuple
이것을 위해 사용할 수 있습니다 .
Mapper.CreateMap<Tuple<People, Phone>, PeoplePhoneDto>()
.ForMember(d => d.FirstName, opt => opt.MapFrom(s => s.Item1.FirstName))
.ForMember(d => d.LastName, opt => opt.MapFrom(s => s.Item1.LastName))
.ForMember(d => d.Number, opt => opt.MapFrom(s => s.Item2.Number ));
더 많은 소스 모델이있는 경우 다른 표현 (목록, 사전 또는 기타)을 사용하여 이러한 모든 모델을 소스로 모을 수 있습니다.
위의 코드는 일부 AutoMapperConfiguration 파일에 배치하고 한 번 전체적으로 설정 한 다음 적용 가능한 경우 사용하는 것이 좋습니다.
AutoMapper는 기본적으로 단일 데이터 소스 만 지원합니다. 따라서 예를 들어 두 개의 소스 모델에 동일한 이름의 속성이있는 경우 어떻게 알 수 있습니까?
이를 달성하기위한 몇 가지 해결 방법이 있습니다.
public static class EntityMapper
{
public static T Map<T>(params object[] sources) where T : class
{
if (!sources.Any())
{
return default(T);
}
var initialSource = sources[0];
var mappingResult = Map<T>(initialSource);
// Now map the remaining source objects
if (sources.Count() > 1)
{
Map(mappingResult, sources.Skip(1).ToArray());
}
return mappingResult;
}
private static void Map(object destination, params object[] sources)
{
if (!sources.Any())
{
return;
}
var destinationType = destination.GetType();
foreach (var source in sources)
{
var sourceType = source.GetType();
Mapper.Map(source, destination, sourceType, destinationType);
}
}
private static T Map<T>(object source) where T : class
{
var destinationType = typeof(T);
var sourceType = source.GetType();
var mappingResult = Mapper.Map(source, sourceType, destinationType);
return mappingResult as T;
}
}
그리고:
var peoplePhoneDto = EntityMapper.Map<PeoplePhoneDto>(people, phone);
그러나 솔직히 말해서 이미 몇 년 동안 AutoMapper를 사용하고 있지만 여러 소스의 매핑을 사용할 필요가 없었습니다. 예를 들어 단일 뷰 모델에 여러 비즈니스 모델이 필요한 경우 이러한 모델을 뷰 모델 클래스에 포함했습니다.
따라서 귀하의 경우에는 다음과 같이 보일 것입니다.
public class PeoplePhoneDto {
public People People { get; set; }
public Phone Phone { get; set; }
}
답변
다음과 같이 확장 메서드를 작성합니다.
public static TDestination Map<TSource1, TSource2, TDestination>(
this IMapper mapper, TSource1 source1, TSource2 source2)
{
var destination = mapper.Map<TSource1, TDestination>(source1);
return mapper.Map(source2, destination);
}
그러면 사용법은 다음과 같습니다.
mapper.Map<People, Phone, PeoplePhoneDto>(people, phone);
답변
아마도 오래된 게시물 처럼 들리지만 AutoMapper IMapper Map 함수 문서를 참조하여 동일한 문제로 여전히 어려움을 겪고있는 일부 사람들이있을 수 있습니다. 이미 만든 경우 새 소스에서 매핑을 위해 동일한 기존 대상 객체를 재사용 할 수 있습니다. 각 소스를 프로필의 대상에 매핑하면 다음과 같은 간단한 확장 방법을 사용할 수 있습니다.
public static class MappingExtentions
{
public static TDestination Map<TDestination>(this IMapper mapper, params object[] sources) where TDestination : new()
{
return Map(mapper, new TDestination(), sources);
}
public static TDestination Map<TDestination>(this IMapper mapper, TDestination destination, params object[] sources) where TDestination : new()
{
if (!sources.Any())
return destination;
foreach (var src in sources)
destination = mapper.Map(src, destination);
return destination;
}
}
인스턴스화 가능한 유형이어야한다는 대상 유형에 대한 제약 조건을 만들었습니다. 당신의 유형은 사용처럼이 아닌 경우 default(TDestination)
대신 new TDestination()
.
경고 : 이러한 유형의 매핑은 대상 매핑 속성이 여러 소스에 의해 덮어 쓰여지고 더 큰 앱에서 문제를 추적하는 것이 골칫거리가 될 수 있기 때문에 때때로 약간 위험합니다. 적용 할 수있는 느슨한 해결 방법이 있습니다. 아래와 같이 수행 할 수 있지만 다시 말하지만 그것은 전혀 고체 솔루션이 아닙니다.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
}
public class Contact
{
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string Other{ get; set; }
}
public class PersonContact
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address{ get; set; }
public string PhoneNumber { get; set; }
}
public class PersonMappingProfile : MappingProfile
{
public PersonMappingProfile()
{
this.CreateMap<Person, PersonContact>();
this.CreateMap<Phone, PersonContact>()
.ForMember(dest => dest.PhoneNumber, opt => opt.MapFrom((src, dest) => dest.PhoneNumber ?? src.PhoneNumber)) // apply mapping from source only if the phone number is not already there, this way you prevent overwriting already initialized props
.ForAllOtherMembers(o => o.Ignore());
}
}
답변
소스 중 하나에서 대상 유형을 매핑해야하는 시나리오가 있고 linq 프로젝션을 사용하려는 경우 다음을 수행 할 수 있습니다.
Mapper.CreateMap<People, PeoplePhoneDto>(MemberList.Source);
Mapper.CreateMap<Phone, PeoplePhoneDto>(MemberList.Source)
.ForMember(d => d.PhoneNumber, a => a.MapFrom(s => s.Number));
CreateMap<PeoplePhoneDto,(People,Phone)>(MemberList.Destination)
.ForMember(x => x.Item1, opts => opts.MapFrom(x => x))
.ForMember(x => x.Item2, opts => opts.MapFrom(x => x.PhoneNumber))
.ReverseMap();
나는 주로 이와 같은 교차 적용 쿼리에 필요했습니다.
var dbQuery =
from p in _context.People
from ph in _context.Phones
.Where(x => ...).Take(1)
select ValueTuple.Create(p, ph);
var list = await dbQuery
.ProjectTo<PeoplePhoneDto>(_mapper.ConfigurationProvider)
.ToListAsync();
답변
이미 많은 옵션이 제공되었지만 내가 원하는 것에 맞는 옵션은 없습니다. 나는 어제 밤에 잠들었 고 생각했습니다.
당신이 두 개의 클래스를 매핑 할, 말할 수 People
및 Phone
에PeoplePhoneDto
public class People {
public string FirstName {get;set;}
public string LastName {get;set;}
}
+
public class Phone {
public string Number {get;set;}
}
=
public class PeoplePhoneDto {
public string FirstName {get;set;}
public string LastName {get;set;}
public string PhoneNumber {get;set;}
}
정말 필요한 것은 Automapper 목적을위한 또 다른 래퍼 클래스입니다.
public class PeoplePhone {
public People People {get;set;}
public Phone Phone {get;set;}
}
그런 다음 매핑을 정의합니다.
CreateMap<PeoplePhone, PeoplePhoneDto>()
그리고 그것을 사용하십시오
var dto = Map<PeoplePhoneDto>(new PeoplePhone
{
People = people,
Phone = phone,
});