前言
什么是对应之间的映射?
两个实体中只有一个实体指向另一个实体(知道该关系的存在),而另一实体没有指向这个实体(另一实体不知道该关系的存在)
举个很简单的例子:两个对象(类)具有相似或相同的属性,它们之间要互相映射,其实这项工作重复且无聊的工作
比如我们的Person对象 和 一个和它很相似的 TestPersonDto对象
[Table("Person")]
public class Person : FullAuditedEntity
{
public string Name { get; set; }
public int Sex { get; set; }
}
public class TestPersonDto
{
public string Name { get; set; }
public int Sex { get; set; }
}
如果我们想把Person的内容映射到TestPersonDto中传统是用以下方法 ,旦是如果数据多起来就是,很复杂的很无聊的操作
public void TestDto()
{
Person entity = _entityRepository.Get(1);
var personEditDto = new PersonEditDto
{
Name = entity.Name,
Sex = entity.Sex
};
}
那么今天我们就来看下,我们有什么办法偷懒,简化这些复杂的操作吧。
一.IObjectMapper 接口
我们可以利用IObjectMapper 接口,完成上面的映射操作
public List<TestPersonDto> TestDto()
{
var entity = _entityRepository.GetAllList();
var peopleList = ObjectMapper.Map<List<TestPersonDto>>(entity);
return peopleList;
}
二.AutoMapper的IMapper对象
首先,安装Abp.AutoMapper nuget包到你的项目里:
var peopleList = Mapper.Map<List<TestPersonDto>>(entity);
三.MapTo扩展方法
var peopleList = entity.MapTo<List<TestPersonDto>>();
四.自动映射的特性
我们还存在一个小问题
如果我们:peron------映射到--------》TestpersonDto 是没问题的
如果我们:TestpersonDto------映射到--------》person 是会出错的
public List<TestPersonDto> TestDto()
{
var entity = _entityRepository.GetAllList();
//peron------映射到--------》TestpersonDto
var peopleList = entity.MapTo<List<TestPersonDto>>();
//TestpersonDto------映射到--------》person 是会出错的
var personList = peopleList.MapTo<List<Person>>();
return peopleList;
}
为什么:TestpersonDto------映射到--------》person 是会出错的会出错呢?这是因为TestPersonDto实体和Person实体,的列是不匹配的
我们有2个解决方案:1.使得2个实体的列一一对应。2.声明映射,告诉系统TestPersonDto实体和Person实体是存在关系的
那么我们怎么告诉系统TestPersonDto实体和Person实体是存在关系呢?
首先,安装Abp.AutoMapper nuget包到你的项目里:
我们可以使用AutoMap、AutoMapFrom和AutoMapTo特性来实现实体的关系映射
AutoMap:双向映射
AutoMapFrom与AutoMapTo:单向映射
双向映射,和单向映射的解析:https://blog.csdn.net/lxy15329/article/details/6227168
我们直接在TestPersonDto上声明自动映射的特性([AutoMap(typeof(Person))]),就可以解决这个问题了
[AutoMap(typeof(Person))]
public class TestPersonDto
{
public int? Id { get; set; }
public string Name { get; set; }
public int Sex { get; set; }
}
五.自定义映射
也行有小伙伴说,我不想用[AutoMapTo(typeof(Person))],的方式声明映射。
那么我们可以用,我们的老办法进行映射,创建一个PersonMapper映射接口,然后一个一个声明实体之间的映射
internal static class PersonMapper
{
public static void CreateMappings(IMapperConfigurationExpression configuration)
{ //创建
configuration.CreateMap<TestPersonDto, Person>();
configuration.CreateMap<Person,TestPersonDto>();
}
}
最后,不要忘记把这个自定义映射在项目名ApplicationModule中的Initialize()方法声明一下
public override void Initialize()
{
var thisAssembly = typeof(AbpDemoApplicationModule).GetAssembly();
IocManager.RegisterAssemblyByConvention(thisAssembly);
Configuration.Modules.AbpAutoMapper().Configurators.Add(
// Scan the assembly for classes which inherit from AutoMapper.Profile
cfg => {
cfg.AddProfiles(thisAssembly);
PersonMapper.CreateMappings(cfg);
}
);
}
如果这样代码麻烦那么多,还不如使用自动映射的特性好了,别急这个方法并不是用于简单的映射的。是用于自定义映射的
比如我们改动TestPersonDto实体类,给Name改个名改为 MyName。
//[AutoMap(typeof(Person))]
public class TestPersonDto
{
public int? Id { get; set; }
public string MyName { get; set; }
public int Sex { get; set; }
}
使用自动映射是会出错的,因为系统不会知道,PersonEditDto的 MyName对应的Person的Name
那么,我们就可以用自定义映射苦口婆心得告诉它。
修改一下我们映射接口
internal static class PersonMapper
{
public static void CreateMappings(IMapperConfigurationExpression configuration)
{ //创建
configuration.CreateMap<TestPersonDto, Person>().ForMember(a=>a.Name,b=>b.MapFrom(a=>a.MyName));
configuration.CreateMap<Person, TestPersonDtoo>().ForMember(a=>a.MyName,b=>b.MapFrom(a=>a.Name));
}
}
我们可以看到是成功的
6.关于单元测试中的MapTo
我们想把每个单元测试独立开来,所以我们需要为我们的项目定义如下规则:
1.只使用IObjectMapper,不使用扩展方法MapTo.
2.配置Abp.AutoMapper 模块,使用局部的Mapper实例(用单例的方式注册到依赖注入)不用静态的(Abp.AutoMapper默认情况下,使用静态的Mapper.Instance,从而可以像上面那样使用MapTo扩展方法):
Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;