我在 WCF 服务中使用 AutoMapper 返回
User
对象。 User
具有诸如 AccountTeams
之类的属性,它本身也有子对象。所有类都有 AutoMapper 地图。
根据调用的WCF
OperationContract
,我想返回不同数量的数据。我希望一个 OperationContract
返回未填充其 User
属性(及其子项)的 AccountTeams
对象,另一个 OperationContract
返回填充了整个属性链的 User
。
有没有办法在相同的两个对象之间有两个不同的映射,或者我是否需要执行完整映射并
null
出我不想从服务返回的属性?
Kevin Kalitowski 对 wal 的回答提出了一个很好的观点:如果我们需要两种配置来处理需要不同的映射,那么我们是否必须复制所有其他常见的映射?
我想我已经找到了一种使用配置文件解决此问题的方法:为每个唯一映射提供一个配置文件,为通用映射提供第三个配置文件。然后创建两个配置,一个用于每个唯一的配置文件,但也将通用配置文件添加到每个配置中。
例如,在 AutoMapper 4.2 中:
要映射的类:用户和车辆:
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; }
}
简介:
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>();
}
}
然后创建配置并映射对象:
[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.
}
我尝试了一下,它有效。
我假设您正在从
User
映射到 User
(如果没有,则只需更改目标类型)
假设该类用于以下示例:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
然后您可以使用单独的
AutoMapper.Configuration
来定义 2 个映射:
[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
}
为了避免将其他类型映射两次,您可以添加一个通用方法,该方法采用
Configuration
来映射从 User
可以到达的所有内容,并在调用 configuration1
后在 configuration2
和 CreateMap
上调用此方法.
对于 Automapper 4.x 使用以下命令:
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
我找到了一种不同的方法来解决这种情况,使用“命名包装类”或“类型别名”。
型号:
// db model
public User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OperationContract> OperationContracts { get; set; }
}
// view model
public UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public OperationContract[] OperationContracts { get; set; }
}
// type alias for UserDto
public UserOperationsDto : UserDto
{
}
映射:
public class UserProfile : Profile
{
// register this in global mappings
public UserProfile()
{
// ignore OperationContracts
CreateMap<User, UserDto>()
.ForMember(dest => dest.OperationContracts, opt => opt.Ignore());
// include OperationContracts
CreateMap<User, UserOperationsDto>()
.ForMember(dest => dest.OperationContracts, opt => opt.MapFrom(u => u.OperationContracts));
// TODO: add `OperationContract` mapping as well
}
}
及用法:
var usersWithoutOperations = await _dbContext.Users
.Where(u => u.Id == 1)
.ProjectTo<UserDto>(_mapper.ConfigurationProvider)
.ToArrayAsync(cancellationToken);
var usersWithOperations = await _dbContext.Users
.Where(u => u.Id == 1)
.ProjectTo<UserOperationsDto>(_mapper.ConfigurationProvider)
.ToArrayAsync(cancellationToken);
// you can also do this since parent class is `UserDto`
UserDto[] parentClassList = usersWithOperations;
希望这有帮助!谢谢!