在實體與DTO之間,我們一般都需要進行映射。如果手動的來進行轉換,實在是太麻煩。所以就產生了很多映射工具,比如AutoMapper,EmitMapper。而經過一些對比,EmitMapper比較快,接近於手工轉換的速度。
EmitMapper使用非常簡單,對於那種屬性名一樣的,直接使用默認映射即可。如:
ObjectsMapper<From,To> mapper = ObjectMapperManager.DefaultInstance.GetMapper<From,To>();
to = mapper.Map(from);
它就會自動的把From對象的值賦給To對象;
對於有一定規則的,可以使用一些擴展的方法,如:
通過使用ConvertUsing可以配置一些組合映射.比如這個字段可以是某兩個字段拼接,或者其他一些計算.具體可以參見一些使用例子:
http://www.cnblogs.com/aaa6818162/archive/2012/06/21/2557879.html
http://www.cnblogs.com/wuhong/archive/2011/09/21/2184313.html
但是在使用的過程中,發現了一個問題,就是本身默認是如果屬性名相同就直接映射,而使用ConvertUsing是可以自定義映射條件的.但是如果使用了ConvertUsing,那么你必需為里面每個字段都指定映射的條件.而其實真正想用的是,在實體與DTO之間,其實大部分字段的屬性名稱是相同的,對於這些字段,我們是想直接默認映射,而對於其他一些需要自定義映射的字段,來自定義映射規則.但實際上卻達不到這種效果,一般人也不想去把每個字段的映射規則配置一下,如果這樣,還不如手動寫呢.
后來在網上找到了一個例子,可以使用FlatteringConfig這個自定義配置,這個類在EmitMapper的源碼中是有的,但是不是在當前的解決方案下,而是在一個叫EMConfigurations的項目里.可以這樣使用這個自定義配置.
public class User
{
public Guid Id { get; set; }
public Company Company { get; set; }
}
public class Company
{
public Guid Id { get; set; }
}
public class UserDTO
{
public Guid Id { get; set; }
public Guid CompanyId{ get; set; }
}
ObjectMapperManager.DefaultInstance.GetMapper<User, UserDTO>(
new FlatteringConfig()
);
var dto = mapper.Map(new User());
這樣,它就會自動的給DTO中的CompanyId賦值,也會給Id賦值.不過有一個規則就是CompanyId的命名要有一定的規則,一定要是那個實體的名稱再加上這個實體里面屬性的名稱.這樣才能進行自動的映射.(不過這時,如果那個實體為null,那也有可能會報錯,沒有試過,后面有時間試一下).
在運行的過程中,發現了FlatteringConfig類的一個bug;這個類的原始代碼如下:
public class FlatteringConfig : DefaultMapConfig
{
protected Func<string, string, bool> nestedMembersMatcher;
public FlatteringConfig()
{
nestedMembersMatcher = (m1, m2) => m1.StartsWith(m2);
}
public override IMappingOperation[] GetMappingOperations(Type from, Type to)
{
var destinationMembers = GetDestinationMemebers(to);
var sourceMembers = GetSourceMemebers(from);
var result = new List<IMappingOperation>();
foreach (var dest in destinationMembers)
{
var matchedChain = GetMatchedChain(dest.Name, sourceMembers).ToArray();
if (matchedChain == null || matchedChain.Length == 0)
{
continue;
}
result.Add(
new ReadWriteSimple
{
Source = new MemberDescriptor(matchedChain),
Destination = new MemberDescriptor(new[] { dest })
}
);
}
return result.ToArray();
}
public DefaultMapConfig MatchNestedMembers(Func<string, string, bool> nestedMembersMatcher)
{
this.nestedMembersMatcher = nestedMembersMatcher;
return this;
}
private List<MemberInfo> GetMatchedChain(string destName, List<MemberInfo> sourceMembers)
{
var matches = sourceMembers.Where(s => MatchMembers(destName, s.Name) || nestedMembersMatcher(destName, s.Name));
int len = 0;
MemberInfo match = null;
foreach (var m in matches)
{
if (m.Name.Length > len)
{
len = m.Name.Length;
match = m;
}
}
if (match == null)
{
return null;
}
var result = new List<MemberInfo> { match };
if (!MatchMembers(destName, match.Name))
{
result.AddRange(
GetMatchedChain(destName.Substring(match.Name.Length), GetDestinationMemebers(match))
);
}
return result;
}
private static List<MemberInfo> GetSourceMemebers(Type t)
{
return GetMemebers(t)
.Where(
m =>
m.MemberType == MemberTypes.Field ||
m.MemberType == MemberTypes.Property ||
m.MemberType == MemberTypes.Method
)
.ToList();
}
private static List<MemberInfo> GetDestinationMemebers(MemberInfo mi)
{
Type t;
if (mi.MemberType == MemberTypes.Field)
{
t = mi.DeclaringType.GetField(mi.Name).FieldType;
}
else
{
t = mi.DeclaringType.GetProperty(mi.Name).PropertyType;
}
return GetDestinationMemebers(t);
}
private static List<MemberInfo> GetDestinationMemebers(Type t)
{
return GetMemebers(t).Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property).ToList();
}
private static List<MemberInfo> GetMemebers(Type t)
{
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
return t.GetMembers(bindingFlags).ToList();
}
}
在GetMappingOperations方法中,修改了以下代碼:(原代碼中是一個判斷在一起,改成分作兩個判斷)
var matchedChainSingle = GetMatchedChain(dest.Name, sourceMembers);
if (matchedChainSingle == null)
{
continue;
}
var matchedChain = matchedChainSingle.ToArray();
if (matchedChain.Length == 0)
{
continue;
}
參考資料:
http://emitmapper.codeplex.com/
http://stackoverflow.com/questions/9619265/emit-mapper-flattering-and-property-name-mismatch
http://emitmapper.codeplex.com/SourceControl/changeset/view/69894#1192663
http://emitmapper.codeplex.com/SourceControl/changeset/view/42128#691132
http://stackoverflow.com/questions/12542749/emitmapper-flattering-config-nullreferenceexception?rq=1

