C# AutoMapper 使用方法总结
本文基于AutoMapper9.0.0
AutoMapper是一个对象-对象映射器,可以将一个对象映射到另一个对象。
官网地址:http://automapper.org/
官方文档:https://docs.automapper.org/en/latest/
1入门例子
publicclassFoo
{
publicintID{get;set;}
publicstringName{get;set;}
}
publicclassFooDto
{
publicintID{get;set;}
publicstringName{get;set;}
}
publicvoidMap()
{
varconfig=newMapperConfiguration(cfg=>cfg.CreateMap());
varmapper=config.CreateMapper();
Foofoo=newFoo{ID=1,Name="Tom"};
FooDtodto=mapper.Map(foo);
}
2注册
在使用Map方法之前,首先要告诉AutoMapper什么类可以映射到什么类。
varconfig=newMapperConfiguration(cfg=>cfg.CreateMap());
每个AppDomain只能进行一次配置。这意味着放置配置代码的最佳位置是在应用程序启动中,例如ASP.NET应用程序的Global.asax文件。
从9.0开始Mapper.Initialize方法就不可用了。
2.1Profile
Profile是组织映射的另一种方式。新建一个类,继承Profile,并在构造函数中配置映射。
publicclassEmployeeProfile:Profile
{
publicEmployeeProfile()
{
CreateMap();
}
}
varconfig=newMapperConfiguration(cfg=>
{
cfg.AddProfile();
});
Profile内部的配置仅适用于Profile内部的映射。应用于根配置的配置适用于所有创建的映射。
AutoMapper也可以在指定的程序集中扫描从Profile继承的类,并将其添加到配置中。
varconfig=newMapperConfiguration(cfg=>
{
//扫描当前程序集
cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
//也可以传程序集名称(dll名称)
cfg.AddMaps("LibCoreTest");
});
3配置
3.1命名约定
默认情况下,AutoMapper基于相同的字段名映射,并且是不区分大小写的。
但有时,我们需要处理一些特殊的情况。
- SourceMemberNamingConvention表示源类型命名规则
- DestinationMemberNamingConvention表示目标类型命名规则
LowerUnderscoreNamingConvention和PascalCaseNamingConvention是AutoMapper提供的两个命名规则。前者命名是小写并包含下划线,后者就是帕斯卡命名规则(每个单词的首字母大写)。
我的理解,如果源类型和目标类型分别采用了蛇形命名法和驼峰命名法,那么就需要指定命名规则,使其能正确映射。
publicclassFoo
{
publicintId{get;set;}
publicstringMyName{get;set;}
}
publicclassFooDto
{
publicintID{get;set;}
publicstringMy_Name{get;set;}
}
publicvoidMap()
{
varconfig=newMapperConfiguration(cfg=>
{
cfg.CreateMap();
cfg.SourceMemberNamingConvention=newPascalCaseNamingConvention();
cfg.DestinationMemberNamingConvention=newLowerUnderscoreNamingConvention();
});
varmapper=config.CreateMapper();
Foofoo=newFoo{Id=2,MyName="Tom"};
FooDtodto=mapper.Map(foo);
}
3.2配置可见性
默认情况下,AutoMapper仅映射public成员,但其实它是可以映射到private属性的。
varconfig=newMapperConfiguration(cfg=>
{
cfg.ShouldMapProperty=p=>p.GetMethod.IsPublic||p.SetMethod.IsPrivate;
cfg.CreateMap();
});
需要注意的是,这里属性必须添加privateset,省略set是不行的。
3.3全局属性/字段过滤
默认情况下,AutoMapper尝试映射每个公共属性/字段。以下配置将忽略字段映射。
varconfig=newMapperConfiguration(cfg=>
{
cfg.ShouldMapField=fi=>false;
});
3.4识别前缀和后缀
varconfig=newMapperConfiguration(cfg=>
{
cfg.RecognizePrefixes("My");
cfg.RecognizePostfixes("My");
}
3.5替换字符
varconfig=newMapperConfiguration(cfg=>
{
cfg.ReplaceMemberName("Ä","A");
});
这功能我们基本上用不上。
4调用构造函数
有些类,属性的set方法是私有的。
publicclassCommodity
{
publicstringName{get;set;}
publicintPrice{get;set;}
}
publicclassCommodityDto
{
publicstringName{get;}
publicintPrice{get;}
publicCommodityDto(stringname,intprice)
{
Name=name;
Price=price*2;
}
}
AutoMapper会自动找到相应的构造函数调用。如果在构造函数中对参数做一些改变的话,其改变会反应在映射结果中。如上例,映射后Price会乘2。
禁用构造函数映射:
publicclassCommodity
{
publicstringName{get;set;}
publicintPrice{get;set;}
}
publicclassCommodityDto
{
publicstringName{get;}
publicintPrice{get;}
publicCommodityDto(stringname,intprice)
{
Name=name;
Price=price*2;
}
}
AutoMapper会自动找到相应的构造函数调用。如果在构造函数中对参数做一些改变的话,其改变会反应在映射结果中。如上例,映射后Price会乘2。
禁用构造函数映射:
varconfig=newMapperConfiguration(cfg=>cfg.DisableConstructorMapping());
禁用构造函数映射的话,目标类要有一个无参构造函数。
5数组和列表映射
数组和列表的映射比较简单,仅需配置元素类型,定义简单类型如下:
publicclassSource
{
publicintValue{get;set;}
}
publicclassDestination
{
publicintValue{get;set;}
}
映射:
varconfig=newMapperConfiguration(cfg=>
{
cfg.CreateMap();
});
IMappermapper=config.CreateMapper();
varsources=new[]
{
newSource{Value=5},
newSource{Value=6},
newSource{Value=7}
};
IEnumerableienumerableDest=mapper.Map>(sources);
ICollectionicollectionDest=mapper.Map>(sources);
IListilistDest=mapper.Map>(sources);
ListlistDest=mapper.Map>(sources);
Destination[]arrayDest=mapper.Map(sources);
具体来说,支持的源集合类型包括:
- IEnumerable
- IEnumerable
- ICollection
- ICollection
- IList
- IList
- List
- Arrays
映射到现有集合时,将首先清除目标集合。如果这不是你想要的,请查看AutoMapper.Collection。
5.1处理空集合
映射集合属性时,如果源值为null,则AutoMapper会将目标字段映射为空集合,而不是null。这与EntityFramework和FrameworkDesignGuidelines的行为一致,认为C#引用,数组,List,Collection,Dictionary和IEnumerables永远不应该为null。
5.2集合中的多态
这个官方的文档不是很好理解。我重新举个例子。实体类如下:
publicclassEmployee
{
publicintID{get;set;}
publicstringName{get;set;}
}
publicclassEmployee2:Employee
{
publicstringDeptName{get;set;}
}
publicclassEmployeeDto
{
publicintID{get;set;}
publicstringName{get;set;}
}
publicclassEmployeeDto2:EmployeeDto
{
publicstringDeptName{get;set;}
}
数组映射代码如下:
varconfig=newMapperConfiguration(cfg=>
{
cfg.CreateMap().Include();
cfg.CreateMap();
});
IMappermapper=config.CreateMapper();
varemployees=new[]
{
newEmployee{ID=1,Name="Tom"},
newEmployee2{ID=2,Name="Jerry",DeptName="R&D"}
};
vardto=mapper.Map(employees);
可以看到,映射后,dto中两个元素的类型,一个是EmployeeDto,一个是EmployeeDto2,即实现了父类映射到父类,子类映射到子类。
如果去掉Include方法,则映射后dto中两个元素的类型均为EmployeeDto。
6方法到属性映射
AutoMapper不仅能实现属性到属性映射,还可以实现方法到属性的映射,并且不需要任何配置,方法名可以和属性名一致,也可以带有Get前缀。
例如下例的Employee.GetFullName()方法,可以映射到EmployeeDto.FullName属性。
publicclassEmployee
{
publicintID{get;set;}
publicstringFirstName{get;set;}
publicstringLastName{get;set;}
publicstringGetFullName()
{
return$"{FirstName}{LastName}";
}
}
publicclassEmployeeDto
{
publicintID{get;set;}
publicstringFirstName{get;set;}
publicstringLastName{get;set;}
publicstringFullName{get;set;}
}
7自定义映射
当源类型与目标类型名称不一致时,或者需要对源数据做一些转换时,可以用自定义映射。
publicclassEmployee
{
publicintID{get;set;}
publicstringName{get;set;}
publicDateTimeJoinTime{get;set;}
}
publicclassEmployeeDto
{
publicintEmployeeID{get;set;}
publicstringEmployeeName{get;set;}
publicintJoinYear{get;set;}
}
如上例,ID和EmployeeID属性名不同,JoinTime和JoinYear不仅属性名不同,属性类型也不同。
varconfig=newMapperConfiguration(cfg=>
{
cfg.CreateMap()
.ForMember("EmployeeID",opt=>opt.MapFrom(src=>src.ID))
.ForMember(dest=>dest.EmployeeName,opt=>opt.MapFrom(src=>src.Name))
.ForMember(dest=>dest.JoinYear,opt=>opt.MapFrom(src=>src.JoinTime.Year));
});
8扁平化映射
对象-对象映射的常见用法之一是将复杂的对象模型并将其展平为更简单的模型。
publicclassEmployee
{
publicintID{get;set;}
publicstringName{get;set;}
publicDepartmentDepartment{get;set;}
}
publicclassDepartment
{
publicintID{get;set;}
publicstringName{get;set;}
}
publicclassEmployeeDto
{
publicintID{get;set;}
publicstringName{get;set;}
publicintDepartmentID{get;set;}
publicstringDepartmentName{get;set;}
}
如果目标类型上的属性,与源类型的属性、方法都对应不上,则AutoMapper会将目标成员名按驼峰法拆解成单个单词,再进行匹配。例如上例中,EmployeeDto.DepartmentID就对应到了Employee.Department.ID。
8.1IncludeMembers
如果属性命名不符合上述的规则,而是像下面这样:
publicclassEmployee
{
publicintID{get;set;}
publicstringName{get;set;}
publicDepartmentDepartment{get;set;}
}
publicclassDepartment
{
publicintDepartmentID{get;set;}
publicstringDepartmentName{get;set;}
}
publicclassEmployeeDto
{
publicintID{get;set;}
publicstringName{get;set;}
publicintDepartmentID{get;set;}
publicstringDepartmentName{get;set;}
}
Department类中的属性名,直接跟EmployeeDto类中的属性名一致,则可以使用IncludeMembers方法指定。
9嵌套映射
有时,我们可能不需要展平。看如下例子:
publicclassEmployee
{
publicintID{get;set;}
publicstringName{get;set;}
publicintAge{get;set;}
publicDepartmentDepartment{get;set;}
}
publicclassDepartment
{
publicintID{get;set;}
publicstringName{get;set;}
publicstringHeads{get;set;}
}
publicclassEmployeeDto
{
publicintID{get;set;}
publicstringName{get;set;}
publicDepartmentDtoDepartment{get;set;}
}
publicclassDepartmentDto
{
publicintID{get;set;}
publicstringName{get;set;}
}
我们要将Employee映射到EmployeeDto,并且将Department映射到DepartmentDto。
varconfig=newMapperConfiguration(cfg=>
{
cfg.CreateMap();
cfg.CreateMap();
});
以上就是C#AutoMapper使用方法总结的详细内容,更多关于C#AutoMapper用法的资料请关注毛票票其它相关文章!