Automapper - источник нескольких объектов и один пункт назначения
Я использую auto mapper для сопоставления нескольких объектов (класс db в объекты ui).
Карта 1:
Mapper.CreateMap<sourceone, destination>().ForMember(sss => sss.one, m => m.MapFrom(source => source.abc));
Карта 2:
Mapper.CreateMap<sourcetwo, destination>().ForMember(sss => sss.two, m => m.MapFrom(source => source.xyz));
destination d = new destination();
//Карта 1
d = AutoMapper.Mapper.Map<sourceone, destination>(sourceone);
//Карта 2
d = AutoMapper.Mapper.Map<sourcetwo, destination>(sourcetwo);
Как только я делаю вызов на "Карта 2", значения, которые заполняются с использованием карты 1, теряются.. (то есть destination.one становится пустым). Как это исправить?
Ответы
Ответ 1
Map
имеет перегрузку, которая берет исходный и целевой объект:
d = AutoMapper.Mapper.Map<sourceone, destination>(sourceone);
/* Pass the created destination to the second map call: */
AutoMapper.Mapper.Map<sourcetwo, destination>(sourcetwo, d);
Ответ 2
mapper.MergeInto<PersonCar>(person, car)
с принятым ответом в качестве методов расширения, простой и общей версии:
public static TResult MergeInto<TResult>(this IMapper mapper, object item1, object item2)
{
return mapper.Map(item2, mapper.Map<TResult>(item1));
}
public static TResult MergeInto<TResult>(this IMapper mapper, params object[] objects)
{
var res = mapper.Map<TResult>(objects.First());
return objects.Skip(1).Aggregate(res, (r, obj) => mapper.Map(obj, r));
}
после настройки отображения для каждого типа ввода:
IMapper mapper = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, PersonCar>();
cfg.CreateMap<Car, PersonCar>();
}).CreateMapper();
Ответ 3
public class Person
{
public string Name { get; set; }
public string PhNo { get; set; }
}
public class Company
{
public int EmpNo { get; set; }
public string Title { get; set; }
}
public class PersonCompany
{
public string Name { get; set; }
public string PhNo { get; set; }
public int EmpNo { get; set; }
public string Title { get; set; }
}
//you can test as below
var pMap = Mapper.CreateMap<Person,PersonCompany>();
pMap.ForAllMembers(d => d.Ignore());
pMap.ForMember(d => d.Name, opt => opt.MapFrom(s => s.Name))
.ForMember(d => d.PhNo, opt => opt.MapFrom(s => s.PhNo));
var cMap = Mapper.CreateMap<Company, PersonCompany>();
cMap.ForAllMembers(d => d.Ignore());
cMap.ForMember(d => d.EmpNo, opt => opt.MapFrom(s => s.EmpNo))
.ForMember(d => d.Title, opt => opt.MapFrom(s => s.Title));
var person = new Person { Name = "PersonName", PhNo = "212-000-0000" };
var personCompany = Mapper.Map<Person,PersonCompany>(person);
var company = new Company { Title = "Associate Director", EmpNo = 10001 };
personCompany = Mapper.Map(company, personCompany);
Console.WriteLine("personCompany.Name={0}", personCompany.Name);
Console.WriteLine("personCompany.PhNo={0}", personCompany.PhNo);
Console.WriteLine("personCompany.EmpNo={0}", personCompany.EmpNo);
Console.WriteLine("personCompany.Title={0}", personCompany.Title);
Ответ 4
По моему мнению, вам следует избегать вызова перегруженного метода Map, получающего экземпляр целевого объекта, как объяснено в принятом ответе. Это не позволит вам проверить/проверить вашу конфигурацию сопоставления (Mapper.Configuration.AssertConfigurationIsValid()
) или сделать так, что вы добавите много "Игнорировать" в свои сопоставления.
Очень простое решение состоит в том, чтобы создать составной тип, содержащий ссылки на источник, и определить ваше сопоставление с назначением на основе этого составного типа.
Что-то вроде:
public class SourceOneTwo
{
public SourceOne SourceOne { get; set; }
public SourceTwo SourceTwo { get; set; }
}
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<SourceOneTwo, Destination>()
.ForMember(dest => dest.one, m => m.MapFrom(source => source.SourceOne.abc))
.ForMember(dest => dest.two, m => m.MapFrom(source => source.SourceTwo.xyz)));
config.AssertConfigurationIsValid();
}