自动化程序-多对象源和一个目标

我使用自动映射器来映射多个对象(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);

Once I make call to the 'Map 2', the values that are populated using Map 1 are lost.. (i.e destination.one is becoming empty). How do I fix this?

66563 次浏览

Map has an overload that takes a source and destination object:

d = AutoMapper.Mapper.Map<sourceone, destination>(sourceone);


/* Pass the created destination to the second map call: */
AutoMapper.Mapper.Map<sourcetwo, destination>(sourcetwo, d);
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);
mapper.MergeInto<PersonCar>(person, car)

with the accepted answer as extension-methods, simple and general version:

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

after configuring mapping for each input-type:

IMapper mapper = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Person, PersonCar>();
cfg.CreateMap<Car, PersonCar>();
}).CreateMapper();

According to me you should avoid calling the overloaded Map method taking an instance of the destination object as explained in the accepted answer. This won't let you test/validate your mapping configuration (Mapper.Configuration.AssertConfigurationIsValid()) or to do so you will add a lot of 'Ignore' in your mappings.

A very simple solution is to create a composite type holding source references and define your mapping to the destination based on that composite type.

Something like:

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

Nowadays it looks like that:

DestinationDto = _mapper.Map(source2, _mapper.Map<source1type, destinationType>(source1));

Just wanted to add, that these days you can make use of tuple syntax to define mapping from multiple types.

// configuring
Mapper
.CreateMap<(SourceType1 Source1, SourceType2 Source2), DestinationType>()
.ForMember(sss => sss.one, m => m.MapFrom(source => source.Source1.abc))
.ForMember(sss => sss.two, m => m.MapFrom(source => source.Source2.xyz));


// using
var result = Mapper.Map<DestinationType>((source1, source2));

Pros:

  • don't have to remember to map from the second type after mapping from the first
  • all mapping is defined at once and in one place (as long as you don't need separate mappings as well as a combined one)
  • less of a clumber when mapping from more than 2 source types
  • essentially the @asidis's idea, but does not require a separate type

Cons:

  • well, as with Automapper in general, you have to know where to look, to find out what types to use for mapping
  • may look be a bit unwieldy when a number of types is involved