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