I am using AutoMapper in a WCF service to return User
objects. User
has properties such as AccountTeams
which itself has child objects. All of the classes have AutoMapper maps.
Depending on the WCF OperationContract
that is called, I want to return different amounts of data. I want one OperationContract
to return the User
object without its AccountTeams
property (and their children) populated and another OperationContract
to return the User
with the whole chain of properties filled out.
Is there a way to have two different maps between the same two objects or do I need to perform the full mapping and null
out the properties I don't want to return from the service?
Kevin Kalitowski raised a good point about wal's answer: If we need two configurations to deal with a mapping that needs to be different, then don't we have to duplicate all the other mappings that are common?
I think I've found a way around this using profiles: Have one profile for each of the unique mappings, and a third profile for the common mappings. Then create two configurations, one for each unique profile but also add the common profile to each configuration as well.
Example, in AutoMapper 4.2:
The classes to be mapped: User and Vehicle:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Vehicle
{
public int FleetNumber { get; set; }
public string Registration { get; set; }
}
The profiles:
public class Profile1 : Profile
{
protected override void Configure()
{
base.CreateMap<User, User>();
}
}
public class Profile2 : Profile
{
protected override void Configure()
{
base.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
}
}
public class CommonProfile : Profile
{
protected override void Configure()
{
base.CreateMap<Vehicle, Vehicle>();
}
}
Then create the configurations and map the objects:
[TestMethod]
public void TestMethod()
{
var user = new User() { Name = "John", Age = 42 };
var vehicle = new Vehicle {FleetNumber = 36, Registration = "XYZ123"};
var configuration1 = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CommonProfile>();
cfg.AddProfile<Profile1>();
});
var mapper1 = configuration1.CreateMapper();
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var mappedVehicle1 = mapper1.Map<Vehicle, Vehicle>(vehicle);//Maps both FleetNumber
//and Registration.
var configuration2 = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CommonProfile>();
cfg.AddProfile<Profile2>();
});
var mapper2 = configuration2.CreateMapper();
var mappedUser2 = mapper2.Map<User, User>(user);//maps only Name
var mappedVehicle2 = mapper2.Map<Vehicle, Vehicle>(vehicle);//Same as mappedVehicle1.
}
I tried this out and it works.
I am assuming you are mapping from User
to User
(if not then just change the destination type)
Assume this class for the following example:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
You can then use separate AutoMapper.Configuration
to define 2 maps:
[TestMethod]
public void TestMethod()
{
var configuration1 = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());
var mapper1 = new MappingEngine(configuration1);
configuration1.CreateMap<User, User>();
var user = new User() { Name = "John", Age = 42 };
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var configuration2 = new Configuration(new TypeMapFactory(), MapperRegistry.AllMappers());
configuration2.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
var mapper2 = new MappingEngine(configuration2);
var mappedUser2 = mapper2.Map<User, User>(user);
Assert.AreEqual(0, mappedUser2.Age);//maps only Name
}
To avoid mapping every other Type twice you could add a common method that takes a Configuration
which maps everything that can be reached from User
and call this on both configuration1
and configuration2
after the calls to CreateMap
.
Update
For Automapper 4.x use the following:
var configuration1 = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, User>();
});
var mapper1 = configuration1.CreateMapper();
var user = new User() { Name = "John", Age = 42 };
var mappedUser1 = mapper1.Map<User, User>(user);//maps both Name and Age
var configuration2 = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, User>().ForMember(dest => dest.Age, opt => opt.Ignore());
});
var mapper2 = configuration2.CreateMapper();
var mappedUser2 = mapper2.Map<User, User>(user); //maps only Name
I think you can solve this problem with different Configuration objects as described here, you can find an example of this here