AutoMapper之ABP项目中的使用
最近在研究ABP项目,昨天写了Castle Windsor常用介绍以及其在ABP项目的应用介绍 欢迎各位拍砖,有关ABP的介绍请看阳光铭睿 博客
AutoMapper只要用来数据转换,在园里已经有很多这方面文章了,本文主要介绍其在实际项目常用总结,以及在ABP项目中的应用介绍。AutoMapper应用非常简单,大家稍微看下文档就可以上手,但是性能不好啊,所以一般用在后台项目,对外的项目千万不要用。就那NOP来说吧,它也是把AutoMapper放在后台项目使用,商城前台的项目是不敢用的。
有关性能的问题本文没有涉及到,想了解的请参考EmitMapper,AutoMapper,NLiteMapper和手工映射性能大比拼 和 NLiteMapper与EmitMapper性能简单比较。
下面主要讲下项目的入门和项目中的使用。
AutoMapper使用只要两步,配置和Mapper,一般的在项目中我们会在Global中进行配置
配置映射关系
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
Source
{
public
int
SomeValue {
get
;
set
; }
}
public
class
Destination
{
public
int
SomeValue {
get
;
set
; }
}
//这个就是配置映射关系
Mapper.CreateMap<Source, Destination>();
|
然后就是Mapper
1
2
3
4
5
6
7
|
Source source =
new
Source()
{
SomeValue = 1
};
var
destination = Mapper.Map<Source, Destination>(source);
Console.WriteLine(destination.SomeValue);
//1
|
是不是很简单,这是最简单的使用了,当然AutoMapper是个“机关枪”,这个只是它的最简单使用。下面在介绍几点常用的功能。还是上面那个例子,只是字段变了
1
2
3
4
5
6
7
8
9
10
11
|
public
class
Source
{
public
int
SomeValue {
get
;
set
; }
}
public
class
Destination
{
public
int
SomeValuefff {
get
;
set
; }
}
Mapper.CreateMap<AddressDto, Address>();
|
这样子字段都不一样明细是不能映射的嘛,所有呢我们可以用Mapper.AssertConfigurationIsValid()来验证,就会AutoMapperConfigurationException异常,
选择忽略相关字段
1
2
|
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());
|
类型转换,自定义类型转换
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
Source
{
public
string
Value1 {
get
;
set
; }
public
string
Value2 {
get
;
set
; }
public
string
Value3 {
get
;
set
; }
}
public
class
Destination
{
public
int
Value1 {
get
;
set
; }
public
DateTime Value2 {
get
;
set
; }
public
Type Value3 {
get
;
set
; }
}
|
Source要转Destination,但是第二和第三的字段类型都不一致,所以我们可以自定义类型转换,下面看下转换函数ConvertUsing一般最常用第二种,接受一个ITypeConverter
1
2
3
|
void
ConvertUsing(Func<TSource, TDestination> mappingFunction);
void
ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void
ConvertUsing<TTypeConverter>()
where
TTypeConverter : ITypeConverter<TSource, TDestination>;
|
下面看下ITypeConverter接口
1
2
3
4
|
public
interface
ITypeConverter<TSource, TDestination>
{
TDestination Convert(ResolutionContext context);
}
|
我们可以继承这个接口队Convert进行重写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
class
DateTimeTypeConverter : ITypeConverter<
string
, DateTime>
{
public
DateTime Convert(ResolutionContext context)
{
return
System.Convert.ToDateTime(context.SourceValue);
}
}
public
class
TypeTypeConverter : ITypeConverter<
string
, Type>
{
public
Type Convert(ResolutionContext context)
{
return
context.SourceType;
}
}
|
这样我们就可以映射了,下面看下完整代码
好了,上面把 AutoMapper在项目中常用的方法都介绍完了,再介绍ABP之前我们先看下NOP是怎么使用的吧,由于代码较长省略部分
好了,终于可以到ABP的了,ABP对AutoMapper的使用总结出来两点,1、在模块中初始化配置,2、遍历bin目录下所有的Types判断哪些类是否被定义为需要转换的Attribute
在模块中初始化配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
class
AbpAutoMapperModule : AbpModule
{
private
readonly
ITypeFinder _typeFinder;
private
static
bool
_createdMappingsBefore;
private
static
readonly
object
_syncObj =
new
object
();
public
AbpAutoMapperModule(ITypeFinder typeFinder)
{
_typeFinder = typeFinder;
}
private
void
FindAndAutoMapTypes()
{
var
types = _typeFinder.Find(type =>
type.IsDefined(
typeof
(AutoMapAttribute)) ||
type.IsDefined(
typeof
(AutoMapFromAttribute)) ||
type.IsDefined(
typeof
(AutoMapToAttribute))
);
foreach
(
var
type
in
types)
{
AutoMapperHelper.CreateMap(type);
}
}
}
|
AbpAutoMapperModule 模块会在Global的时候被初始化,然后在PreInitialize的时候回调用到FindAndAutoMapTypes,有关模块是怎么初始化的我想再写一篇介绍。下面我们看下_typeFinder吧
上面_typeFinder.Find调用的是TypeFinder的Find方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
public
Type[] Find(Func<Type,
bool
> predicate)
{
return
GetAllTypes().Where(predicate).ToArray();
}
public
Type[] FindAll()
{
return
GetAllTypes().ToArray();
}
private
List<Type> GetAllTypes()
{
var
allTypes =
new
List<Type>();
foreach
(
var
assembly
in
AssemblyFinder.GetAllAssemblies().Distinct())
{
try
{
Type[] typesInThisAssembly;
try
{
typesInThisAssembly = assembly.GetTypes();
}
catch
(ReflectionTypeLoadException ex)
{
typesInThisAssembly = ex.Types;
}
if
(typesInThisAssembly.IsNullOrEmpty())
{
continue
;
}
allTypes.AddRange(typesInThisAssembly.Where(type => type !=
null
));
}
catch
(Exception ex)
{
Logger.Warn(ex.ToString(), ex);
}
}
return
allTypes;
}
|
好吧,上面代码有点多,但是很简单,就是获取所有的Types,我们看下关键代码AssemblyFinder.GetAllAssemblies()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public
class
WebAssemblyFinder : IAssemblyFinder
{
/// <summary>
/// This return all assemblies in bin folder of the web application.
/// </summary>
/// <returns>List of assemblies</returns>
public
List<Assembly> GetAllAssemblies()
{
var
assembliesInBinFolder =
new
List<Assembly>();
var
allReferencedAssemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList();
var
dllFiles = Directory.GetFiles(HttpRuntime.AppDomainAppPath +
"bin\\"
,
"*.dll"
, SearchOption.TopDirectoryOnly).ToList();
foreach
(
string
dllFile
in
dllFiles)
{
var
locatedAssembly = allReferencedAssemblies.FirstOrDefault(asm => AssemblyName.ReferenceMatchesDefinition(asm.GetName(), AssemblyName.GetAssemblyName(dllFile)));
if
(locatedAssembly !=
null
)
{
assembliesInBinFolder.Add(locatedAssembly);
}
}
return
assembliesInBinFolder;
}
}
|
看看吧,这代码是或bin目录下面的dll,好丧心病狂啊,回到刚刚AbpAutoMapperModule 的获取FindAndAutoMapTypes方法。在获取所有的Types之后我们就要判断这个类是否是被标识了
AutoMapAttribute、AutoMapFromAttribute和AutoMapToAttribute
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private
void
FindAndAutoMapTypes()
{
var
types = _typeFinder.Find(type =>
type.IsDefined(
typeof
(AutoMapAttribute)) ||
type.IsDefined(
typeof
(AutoMapFromAttribute)) ||
type.IsDefined(
typeof
(AutoMapToAttribute))
);
foreach
(
var
type
in
types)
{
AutoMapperHelper.CreateMap(type);
}
}
|
获取之后再我下的Demo中就只有一个UserDto类被标识了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
namespace
AbpDemo.Application.Users.Dto
{
[AutoMapFrom(
typeof
(User))]
public
class
UserDto : EntityDto<
long
>
{
public
string
UserName {
get
;
set
; }
public
string
Name {
get
;
set
; }
public
string
Surname {
get
;
set
; }
public
string
EmailAddress {
get
;
set
; }
}
}
|
接下来就是遍历所有的types进行配置了AutoMapperHelper.CreateMap(type);配置也很简单 看下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public
static
void
CreateMap<TAttribute>(Type type)
where
TAttribute : AutoMapAttribute
{
if
(!type.IsDefined(
typeof
(TAttribute)))
{
return
;
}
foreach
(
var
autoMapToAttribute
in
type.GetCustomAttributes<TAttribute>())
{
if
(autoMapToAttribute.TargetTypes.IsNullOrEmpty())
{
continue
;
}
foreach
(
var
targetType
in
autoMapToAttribute.TargetTypes)
{
if
(autoMapToAttribute.Direction.HasFlag(AutoMapDirection.To))
{
Mapper.CreateMap(type, targetType);
}
if
(autoMapToAttribute.Direction.HasFlag(AutoMapDirection.From))
{
Mapper.CreateMap(targetType, type);
}
}
}
}
|
好了,表达能力不是很好,各位就勉强看下吧。终于写完了。发现作者很喜欢用Attribute来过滤。这边AutoMapper是这样,UnitOfWork也是这样,其实这样也是挺方便的,有点AOP的切面的感觉,值得学习。
参考文章:
http://www.cnblogs.com/netcasewqs/archive/2011/04/13/2014684.html
http://www.cnblogs.com/repository/archive/2011/04/08/2009713.html
https://github.com/AutoMapper/AutoMapper/wiki/Configuration-validation
http://www.cnblogs.com/youring2/p/automapper.html
http://www.cnblogs.com/1-2-3/p/AutoMapper-Best-Practice.html