具有相同属性名称的AutoMapper双向映射
本文关键字:AutoMapper 映射 属性 | 更新日期: 2023-09-27 18:19:56
给定这两个对象
public class UserModel
{
public string Name {get;set;}
public IList<RoleModel> Roles {get;set;}
}
public class UserViewModel
{
public string Name {get;set;}
public IList<RoleViewModel> Roles {get;set;} // notice the ViewModel
}
这是进行映射的最佳方式,还是AutoMapper能够单独将Roles
映射到Roles
?
应用程序配置
Mapper.CreateMap<UserModel, UserViewModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Mapper.CreateMap<UserViewModel, UserModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
实施
_userRepository.Create(Mapper.Map<UserModel>(someUserViewModelWithRolesAttached);
这是进行映射的最佳方式,还是AutoMapper能够单独将角色映射到角色?
如果属性名称相同,则不必手动提供映射:
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
只需确保内部类型也已映射(RoleViewModel
↔RoleModel
)
然而,这意味着,如果更改源或目标属性名称,AutoMapper映射可能会静默失败,并导致难以跟踪的问题(例如,如果在不更改UserViewModels.Roles
的情况下将UserModel.Roles
更改为UserModel.RolesCollection
)。
AutoMapper提供了一个Mapper.AssertConfigurationIsValid()
方法,该方法将检查所有映射是否存在错误,并捕获配置错误的映射。有一个与构建一起运行的单元测试非常有用,它可以验证您的映射是否存在此类问题。
您不需要映射属性。只需确保属性名称匹配,并且在它们之间定义了映射即可。
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Mapper.CreateMap<RoleModel, RoleViewModel>();
Mapper.CreateMap<RoleViewModel, RoleModel>();
或者用我刚刚发现的更酷的方式:
Mapper.CreateMap<UserModel, UserViewModel>().ReverseMap();
Mapper.CreateMap<RoleModel, RoleViewModel>().ReverseMap();
所有其他答案都要好得多(我对每个答案都投了赞成票)。
但我想在这里发布的是一个快速的游乐场,你可以在C#程序模式下复制并通过它进入LinqPad,在不干扰实际代码的情况下播放你的想法。
将所有转换移到TyperConverter类中的另一件很棒的事情是,您的转换现在是可单元测试的。:)
在这里,您会注意到,除了一个属性外,模型和视图模型几乎完全相同。但是通过这个过程,正确的属性会转换为目标对象中正确的属性。
将此代码复制到LinqPad中,切换到C#程序模式后,您可以使用播放按钮运行它。
void Main()
{
AutoMapper.Mapper.CreateMap<UserModel, UserViewModel>().ConvertUsing(new UserModelToUserViewModelConverter());
AutoMapper.Mapper.AssertConfigurationIsValid();
var userModel = new UserModel
{
DifferentPropertyName = "Batman",
Name = "RockStar",
Roles = new[] {new RoleModel(), new RoleModel() }
};
var userViewModel = AutoMapper.Mapper.Map<UserViewModel>(userModel);
Console.WriteLine(userViewModel.ToString());
}
// Define other methods and classes here
public class UserModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; }
public string DifferentPropertyName { get; set; }
}
public class UserViewModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; } // notice the ViewModel
public string Thingy { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("Name: {0}", Name));
sb.AppendLine(string.Format("Thingy: {0}", Thingy));
sb.AppendLine(string.Format("Contains #{0} of roles", Roles.Count()));
return sb.ToString();
}
}
public class UserModelToUserViewModelConverter : TypeConverter<UserModel, UserViewModel>
{
protected override UserViewModel ConvertCore(UserModel source)
{
if(source == null)
{
return null;
}
//You can add logic here to deal with nulls, empty strings, empty objects etc
var userViewModel = new UserViewModel
{
Name = source.Name,
Roles = source.Roles,
Thingy = source.DifferentPropertyName
};
return userViewModel;
}
}
public class RoleModel
{
//no content for ease, plus this has it's own mapper in real life
}
Console.WriteLine(userViewModel.ToString());
:的结果
Name: RockStar
Thingy: Batman
Contains #2 of roles
Mapper.Initialize(config => {
config.CreateMap<UserModel, UserViewModel>().ReverseMap();
// other maps you want to do.
});