在實體與DTO之間,咱們通常都須要進行映射。若是手動的來進行轉換,實在是太麻煩。因此就產生了不少映射工具,好比AutoMapper,EmitMapper。而通過一些對比,EmitMapper比較快,接近於手工轉換的速度。html
EmitMapper使用很是簡單,對於那種屬性名同樣的,直接使用默認映射便可。如:app
ObjectsMapper<From,To> mapper = ObjectMapperManager.DefaultInstance.GetMapper<From,To>();
to = mapper.Map(from);
它就會自動的把From對象的值賦給To對象;ide
對於有必定規則的,可使用一些擴展的方法,如:工具
經過使用ConvertUsing能夠配置一些組合映射.好比這個字段能夠是某兩個字段拼接,或者其餘一些計算.具體能夠參見一些使用例子:ui
http://www.cnblogs.com/aaa6818162/archive/2012/06/21/2557879.htmlthis
http://www.cnblogs.com/wuhong/archive/2011/09/21/2184313.htmlspa
可是在使用的過程當中,發現了一個問題,就是自己默認是若是屬性名相同就直接映射,而使用ConvertUsing是能夠自定義映射條件的.可是若是使用了ConvertUsing,那麼你必需爲裏面每一個字段都指定映射的條件.而其實真正想用的是,在實體與DTO之間,其實大部分字段的屬性名稱是相同的,對於這些字段,咱們是想直接默認映射,而對於其餘一些須要自定義映射的字段,來自定義映射規則.但實際上卻達不到這種效果,通常人也不想去把每一個字段的映射規則配置一下,若是這樣,還不如手動寫呢.code
後來在網上找到了一個例子,可使用FlatteringConfig這個自定義配置,這個類在EmitMapper的源碼中是有的,可是不是在當前的解決方案下,而是在一個叫EMConfigurations的項目裏.能夠這樣使用這個自定義配置.htm
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