场景
在业务开发中,从仓储至应用,中间一般还有一层模型映射服务,其中的核心主键俺管他叫映射器(Mapper)。现在业界已经有很多Mapper
的实现方案了,多为自实现如反射同名映射,或者使用大名鼎鼎的AutoMapper
。
AutoMapper
底层是有一些优化的,比很多自实现的反射同名映射要快,反射会占用大量时间。但是不管AutoMapper再快,肯定是没有直接赋值快( 直接new Data(){ Name = dataDto.Name }
)
一般来说,除非动态动编译层,否则除了Lambda之外,很难有比原生赋值还快的方法了。
Lambda Mapper
我的LambdaMapper
的实现是基于我领域模型与视图模型的基本定义来编写的,读者可能需要按照各自的业务进行修改。
如我的领域模型:
public class ApplicationMenuItem : EntityBase<Guid>
{
/// <summary>
/// 访问时的默认参数min
/// </summary>
public string Parameter { get; set; }
/// <summary>
/// 导航地址
/// </summary>
[StringLength(150)]
public string Path { get; set; }
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; }
/// <summary>
/// 上级菜单
/// </summary>
public ApplicationMenuItem Parent { get; set; }
/// <summary>
/// 是否不显示在导航栏
/// </summary>
public bool IsHidden { get; set; }
/// <summary>
/// 归属的客户端
/// </summary>
public CddApplication CddApplication { get; set; }
}
Dto:
public class ApplicationMenuItemDto : DtoBase<Guid>
{
/// <summary>
/// 访问时的默认参数
/// </summary>
public string Parameter { get; set; }
/// <summary>
/// 导航地址
/// </summary>
public string Path { get; set; }
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; }
/// <summary>
/// 上级菜单
/// </summary>
public string ParentId { get; set; }
public string ParentName { get; set; }
/// <summary>
/// 归属的客户端
/// </summary>
public string CddApplicationId { get; set; }
public string CddApplicationName { get; set; }
两种模型相互的映射的规则
-
领域模型 => 视图模型
1.1 同名同类型属性映射,同时提供不同类型但是同名类型的属性类型转换后赋值
1.2 导航属性映射,如上面ApplicationMenuItem
的Parent.Id
和Parent.Name
会映射到ApplicationMenuItemDto
的ParentId
和ParentName
(当然这里面还有一些空值判断,如果Parent为空则不映射)
1.3 层级映射,类似于使用AutoMapper的时候,会将Parent.Parent.Name映射至ParentParentName -
视图模型 => 领域模型
1.1 同名同类型属性映射,同时提供不同类型但是同名类型的属性类型转换后赋值
1.2 从视图模型的属性中,拼接回导航属性,如上面ApplicationMenuItemDto
的ParentId
和ParentName
会变成语句Parent = new ApplicationMenuItem(){ Id = applicationMenuItemDto.ParentId, Name = applicationMenuItemDto.ParentName}
代码实现
public class Mapper<TEntity, TEntityVM>
where TEntity : class, new()
where TEntityVM : class, new()
{
/// <summary>
/// 对于命名空间的约束
/// </summary>
private const string configNamespace = "您的命名空间";
/// <summary>
/// 转视图模型的lambda表达式缓存
/// </summary>
private static Func<TEntity, TEntityVM> _mapToEntityVMCache;
/// <summary>
/// 转实体模型的Lambda表达式缓存
/// </summary>
private static Func<TEntityVM, TEntity> _mapToEntityCache;
static Mapper()
{
_mapToEntityVMCache = GetMapToEntityVMFunc();
_mapToEntityCache = GetMapToEntityFunc();
}
/// <summary>
/// 判断类型是否为可操作的列表类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static bool _isList(Type type)
{
if (typeof(System.Collections.IList).IsAssignableFrom(type))
{
return true;
}
foreach (var it in type.GetInterfaces())
{
if (it.IsGenericType && typeof(IList<>) == it.GetGenericTypeDefinition())
return true;
}
return false;
}
/// <summary>
/// 生成映射成视图模型的Lambda表达式
/// </summary>
/// <returns></returns>
private static Func<TEntity, TEntityVM> GetMapToEntityVMFunc()
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TEntity), "x");
// 一组用于映射的模型绑定表达式
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var entityProperty in typeof(TEntity).GetProperties())
{
// 这里的item是视图模型对应的属性
var item = typeof(TEntityVM).GetProperty(entityProperty.Name);
if (item == null)
{
// 如果是自定义类型
if (entityProperty.PropertyType.FullName.Contains(configNamespace) && entityProperty.PropertyType.IsClass
&& _isList(entityProperty.PropertyType) == false)
{
var rootName = "";
var nextLevelMemberExpression = Expression.Property(parameterExpression, entityProperty);
var res = GetSubPropertyMapConfig<TEntityVM>(memberBindingList, rootName, nextLevelMemberExpression, entityProperty,
new List<MemberExpression>() { nextLevelMemberExpression });
memberBindingList = res.Item1;
}
// 否则就是真的没有
else
{
continue;
}
}
else if (item.PropertyType == entityProperty.PropertyType)
{
// 如果是不能写的属性
if (!item.CanWrite)
continue;
MemberExpression eProperty = Expression.Property(parameterExpression, entityProperty.Name);
MemberBinding memberBindingResult = Expression.Bind(item, eProperty);
memberBindingList.Add(memberBindingResult);
if (entityProperty.PropertyType.IsEnum)
{
var enumNameProp = typeof(TEntityVM).GetProperty(entityProperty.Name + "Name");
// 搜索一下是否有后缀加Name的字符串值
if (enumNameProp != null)
{
MethodInfo methodInfo = entityProperty.PropertyType.GetMethod("ToString", new Type[] { });
var resultExpression = Expression.Call(eProperty, methodInfo);
MemberBinding enumNameMemberBindingResult = Expression.Bind(enumNameProp, resultExpression);
memberBindingList.Add(enumNameMemberBindingResult);
}
}
}
// 其他类型转string
else if (item.PropertyType != entityProperty.PropertyType && item.PropertyType == typeof(string))
{
// 如果是不能写的属性
if (!item.CanWrite)
continue;
MemberExpression memberExp = Expression.Property(parameterExpression, entityProperty.Name);
// 获取ToString方法,一般来说,都应该是String类型
MethodInfo methodInfo = memberExp.Type.GetMethod("ToString", new Type[] { });
var resultExpression = Expression.Call(memberExp, methodInfo);
// ToString()之后再传输过去
MemberBinding memberBindingResult = Expression.Bind(item, resultExpression);
memberBindingList.Add(memberBindingResult);
}
// string转其他类型
else if (item.PropertyType != entityProperty.PropertyType && entityProperty.PropertyType == typeof(string))
{
// 如果是不能写的属性
if (!item.CanWrite)
continue;
MemberExpression property = Expression.Property(parameterExpression, typeof(TEntity).GetProperty(entityProperty.Name));
MethodInfo parseMethod = item.PropertyType.GetMethod("Parse", new Type[] { typeof(string) });
MethodCallExpression resultExpression = Expression.Call(parseMethod, new List<Expression>() { property });
// p=> p.item.Name = p.item.Name
MemberBinding memberBindingResult = Expression.Bind(item, resultExpression);
memberBindingList.Add(memberBindingResult);
}
}
// p => new TOut(){item.Name = p.item.Name}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TEntityVM)), memberBindingList.ToArray());
Expression<Func<TEntity, TEntityVM>> lambda = Expression.Lambda<Func<TEntity, TEntityVM>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
return lambda.Compile();
}
/// <summary>
/// 二级类型映射处理
/// </summary>
/// <typeparam name="Target"></typeparam>
/// <param name="memberBindings"></param>
/// <param name="sourceType"></param>
/// <returns></returns>
private static Tuple<List<MemberBinding>, string, MemberExpression, List<MemberExpression>> GetSubPropertyMapConfig<Target>(List<MemberBinding> memberBindings, string rootName,
MemberExpression nowLevelMemberExpression,
PropertyInfo sourceSubTypeInfo,
List<MemberExpression> historyNullValueValidateMembers)
{
var targetModelProps = typeof(Target).GetProperties();
var sourceSubTypeProps = sourceSubTypeInfo.PropertyType.GetProperties();
// 根名称,根据层级不断叠加
rootName += sourceSubTypeInfo.Name;
if (historyNullValueValidateMembers == null)
{
historyNullValueValidateMembers = new List<MemberExpression>();
}
var subClassProps = sourceSubTypeProps.Where(x => x.PropertyType.FullName.Contains(configNamespace) && x.PropertyType.IsClass).ToList();
var baseClassProps = sourceSubTypeProps.Where(x => !x.PropertyType.FullName.Contains(configNamespace)).ToList();
// 二级的二级属性映射
foreach (var baseClassProp in baseClassProps)
{
var item = targetModelProps.FirstOrDefault(x => x.Name == rootName + baseClassProp.Name);
if (item == null)
{
continue;
}
Expression nullValueValidateMemberExpression = null;
if (historyNullValueValidateMembers == null)
{
return new Tuple<List<MemberBinding>, string, MemberExpression, List<MemberExpression>>(memberBindings, rootName, nowLevelMemberExpression, new List<MemberExpression>());
}
// 空值判断表达式拼接
else
{
for (var i = 0; i < historyNullValueValidateMembers.Count; i++)
{
if (i == 0)
{
nullValueValidateMemberExpression = Expression.Equal(historyNullValueValidateMembers[i], Expression.Constant(null));
}
else
{
nullValueValidateMemberExpression = Expression.OrElse(nullValueValidateMemberExpression, Expression.Equal(historyNullValueValidateMembers[i], Expression.Constant(null)));
}
}
}
// 类型一致才进行映射
if (item.PropertyType == baseClassProp.PropertyType)
{
MemberExpression memberExp = Expression.Property(nowLevelMemberExpression, baseClassProp);
Expression conditionExpr = Expression.Condition(
nullValueValidateMemberExpression,
Expression.Default(memberExp.Type),
memberExp
);
MemberBinding memberBindingResult = Expression.Bind(item, conditionExpr);
memberBindings.Add(memberBindingResult);
}
else if (item.PropertyType == typeof(string))
{
MemberExpression memberExp = Expression.Property(nowLevelMemberExpression, baseClassProp);
// 获取ToString方法,一般来说,都应该是String类型
MethodInfo methodInfo = memberExp.Type.GetMethod("ToString", new Type[] { });
var resultExpression = Expression.Call(memberExp, methodInfo);
Expression conditionExpr = Expression.Condition(
nullValueValidateMemberExpression,
Expression.Default(typeof(string)),
resultExpression
);
MemberBinding memberBindingResult = Expression.Bind(item, conditionExpr);
memberBindings.Add(memberBindingResult);
}
else if (baseClassProp.PropertyType == typeof(string))
{
MemberExpression memberExp = Expression.Property(nowLevelMemberExpression, baseClassProp);
// 获取ToString方法,一般来说,都应该是String类型
MethodInfo methodInfo = item.PropertyType.GetMethod("Parse", new Type[] { typeof(string) });
var resultExpression = Expression.Call(methodInfo, new List<Expression>() { memberExp });
Expression conditionExpr = Expression.Condition(
nullValueValidateMemberExpression,
Expression.Default(typeof(string)),
resultExpression
);
MemberBinding memberBindingResult = Expression.Bind(item, conditionExpr);
memberBindings.Add(memberBindingResult);
}
}
foreach (var subClassProp in subClassProps)
{
var nextLevelMemberExpression = Expression.Property(nowLevelMemberExpression, subClassProp);
if (!historyNullValueValidateMembers.Any(x => x.Type == nextLevelMemberExpression.Type))
{
historyNullValueValidateMembers.Add(nextLevelMemberExpression);
}
// 如果二级仍是当前类型,不继续做处理,只映射一层
if(subClassProp.PropertyType == sourceSubTypeInfo.PropertyType)
{
continue;
}
var res = GetSubPropertyMapConfig<Target>(memberBindings, rootName, nextLevelMemberExpression, subClassProp, historyNullValueValidateMembers);
}
return new Tuple<List<MemberBinding>, string, MemberExpression, List<MemberExpression>>(memberBindings, rootName, nowLevelMemberExpression, historyNullValueValidateMembers);
}
/// <summary>
/// 生成映射成实体模型的Lambda表达式
/// </summary>
/// <returns></returns>
private static Func<TEntityVM, TEntity> GetMapToEntityFunc()
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TEntityVM), "p");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
// 遍历实体模型
foreach (var item in typeof(TEntity).GetProperties())
{
// 同名的情况下
// 如果是不能写的属性
if (!item.CanWrite)
continue;
// 如果输出类型没有该属性
if (typeof(TEntityVM).GetProperty(item.Name) == null)
{
// 如果是自定义类型
if (item.PropertyType.FullName.Contains(configNamespace) && !item.PropertyType.IsEnum && _isList(item.PropertyType) == false)
{
List<MemberBinding> tempMemberBindingList = new List<MemberBinding>();
// 获取实体模型二级
// p=>p.item.Name,拿到实体模型嵌套属性Id和Name
var theIdProp = typeof(TEntityVM).GetProperty(item.Name + "Id");
if (theIdProp != null)
{
MemberExpression idProperty = Expression.Property(parameterExpression, theIdProp);
if (idProperty == null) continue;
// TEntity.Id
var id = item.PropertyType.GetProperty("Id");
if (id == null) continue;
// 视图模型string转其他类型
if (id.PropertyType != theIdProp.PropertyType && theIdProp.PropertyType == typeof(string))
{
MethodInfo toGuid = id.PropertyType.GetMethod("Parse", new Type[] { typeof(string) });
MethodCallExpression expression = Expression.Call(toGuid, new List<Expression>() { idProperty });
Expression idConditionExpr = Expression.Condition(
Expression.Equal(Expression.Property(parameterExpression, item.Name + "Id"), Expression.Constant(null)),
Expression.Default(id.PropertyType),
expression
);
MemberBinding idMemberBinding = Expression.Bind(id, idConditionExpr);
tempMemberBindingList.Add(idMemberBinding);
}
else
{
MemberBinding idMemberBinding = Expression.Bind(item.PropertyType.GetProperty("Id"), idProperty);
tempMemberBindingList.Add(idMemberBinding);
}
}
var theNameProp = typeof(TEntityVM).GetProperty(item.Name + "Name");
if (theNameProp != null)
{
MemberExpression NameProperty = Expression.Property(parameterExpression, theNameProp);
MemberBinding NameMemberBinding = Expression.Bind(item.PropertyType.GetProperty("Name"), NameProperty);
tempMemberBindingList.Add(NameMemberBinding);
}
MemberInitExpression includeBoInitExpression = Expression.MemberInit(Expression.New(item.PropertyType), tempMemberBindingList.ToArray());
if (typeof(TEntityVM).GetProperty(item.Name + "Id").PropertyType == typeof(string))
{
Expression includeBoInitExpressionC = Expression.Condition(
Expression.Equal(Expression.Property(parameterExpression, item.Name + "Id"), Expression.Constant(null)),
Expression.Default(item.PropertyType),
includeBoInitExpression
);
MemberBinding memberString = Expression.Bind(item, includeBoInitExpressionC);
memberBindingList.Add(memberString);
}
else if (typeof(TEntityVM).GetProperty(item.Name + "Id").PropertyType == typeof(Guid)
|| typeof(TEntityVM).GetProperty(item.Name + "Id").PropertyType == typeof(int))
{
Expression includeBoInitExpressionC = Expression.Condition(
Expression.Equal(Expression.Property(parameterExpression, item.Name + "Id"), Expression.Default(typeof(TEntityVM).GetProperty(item.Name + "Id").PropertyType)),
Expression.Default(item.PropertyType),
includeBoInitExpression
);
MemberBinding memberString = Expression.Bind(item, includeBoInitExpressionC);
memberBindingList.Add(memberString);
}
continue;
}
// 如果是字符串就绑定为""
else if (item.PropertyType == typeof(string))
{
// 将这个属性绑定到""
MemberBinding memberString = Expression.Bind(item, Expression.Constant(""));
memberBindingList.Add(memberString);
}
continue;
}
else if (item.PropertyType == typeof(TEntityVM).GetProperty(item.Name).PropertyType)
{
MemberExpression property = Expression.Property(parameterExpression, typeof(TEntityVM).GetProperty(item.Name));
// p=> p.item.Name = p.item.Name
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
// 基本类型 转string
else if (item.PropertyType != typeof(TEntityVM).GetProperty(item.Name).PropertyType
&& typeof(TEntityVM).GetProperty(item.Name).PropertyType != typeof(string))
{
MemberExpression property = Expression.Property(parameterExpression, typeof(TEntityVM).GetProperty(item.Name));
MethodInfo toString = property.Type.GetMethod("ToString", new Type[] { });
MethodCallExpression resultExpression = Expression.Call(property, toString);
// p=> p.item.Name = p.item.Name
MemberBinding memberBinding = Expression.Bind(item, resultExpression);
memberBindingList.Add(memberBinding);
}
// string转 基本类型
else if (item.PropertyType != typeof(TEntityVM).GetProperty(item.Name).PropertyType
&& typeof(TEntityVM).GetProperty(item.Name).PropertyType == typeof(string))
{
MemberExpression property = Expression.Property(parameterExpression, typeof(TEntityVM).GetProperty(item.Name));
MethodInfo toGuid = item.PropertyType.GetMethod("Parse", new Type[] { typeof(string) });
MethodCallExpression resultExpression = Expression.Call(toGuid, new List<Expression>() { property });
Expression guidValueExp = Expression.Condition(
Expression.Equal(property, Expression.Constant(null)),
Expression.Default(item.PropertyType),
resultExpression
);
// p=> p.item.Name = p.item.Name
MemberBinding memberBinding = Expression.Bind(item, guidValueExp);
memberBindingList.Add(memberBinding);
}
}
// p => new TOut(){item.Name = p.item.Name}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TEntity)), memberBindingList.ToArray());
Expression<Func<TEntityVM, TEntity>> lambda = Expression.Lambda<Func<TEntityVM, TEntity>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
return lambda.Compile();
}
public static TEntityVM MapToApiEntity(TEntity tIn, Action<TEntity, TEntityVM> mapperExtensionAction = null)
{
if (tIn == null)
{
return null;
}
if (mapperExtensionAction == null)
{
return _mapToEntityVMCache(tIn);
}
else
{
var res = _mapToEntityVMCache(tIn);
mapperExtensionAction(tIn, res);
return res;
}
}
public static TEntity MapToEntity(TEntityVM tout, Action<TEntityVM, TEntity> mapperExtensionAction = null)
{
if (tout == null)
{
return null;
}
if (mapperExtensionAction == null)
{
return _mapToEntityCache(tout);
}
else
{
var res = _mapToEntityCache(tout);
mapperExtensionAction(tout, res);
return res;
}
}
public static List<TEntityVM> MapToApiEntity(IEnumerable<TEntity> tIns, Action<TEntity, TEntityVM> mapperExtensionAction = null)
{
if (tIns == null)
{
return null;
}
if (mapperExtensionAction == null)
{
return tIns.ToList().Select(x => _mapToEntityVMCache(x)).ToList();
}
else
{
return tIns.ToList().Select(x =>
{
var temp = _mapToEntityVMCache(x);
mapperExtensionAction(x, temp);
return temp;
}).ToList();
}
}
public static List<TEntity> MapToEntity(IEnumerable<TEntityVM> touts, Action<TEntityVM, TEntity> mapperExtensionAction = null)
{
if (touts == null)
{
return null;
}
if (mapperExtensionAction == null)
{
return touts.ToList().Select(x => _mapToEntityCache(x)).ToList();
}
else
{
return touts.ToList().Select(x =>
{
var temp = _mapToEntityCache(x);
mapperExtensionAction(x, temp);
return temp;
}).ToList();
}
}
}
单句解释我就不做了,本质上就是表达式树的拼接,多看就理解了,实际上最核心的就是:
MemberBinding memberBinding = Expression.Bind(item, resultExpression);
以及一些情况下,需要先做不同类型的同名属性的映射后再进行Binding
MemberExpression property = Expression.Property(parameterExpression, typeof(TEntityVM).GetProperty(item.Name));
MethodInfo toString = property.Type.GetMethod("ToString", new Type[] { });
MethodCallExpression resultExpression = Expression.Call(property, toString);
// p=> p.item.Name = p.item.Name
MemberBinding memberBinding = Expression.Bind(item, resultExpression);
memberBindingList.Add(memberBinding);
或者导航属性拆解映射时,需要外层做一个判断导航属性是否为空的判断表达式
Expression includeBoInitExpressionC = Expression.Condition(Expression.Equal(Expression.Property(parameterExpression, item.Name + "Id"),
Expression.Default(typeof(TEntityVM).GetProperty(item.Name + "Id").PropertyType)),
Expression.Default(item.PropertyType),
includeBoInitExpression
);
MemberBinding memberString = Expression.Bind(item, includeBoInitExpressionC);
memberBindingList.Add(memberString);
最终生成Lambda Func,并且缓存在静态变量中,这样在一个服务的运行周期中,每两种实体的映射只需要编译一次Lambda表达式,往后的映射速度都会是最接近原生的,因为本质上已经编译成Func来执行了。
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TEntity)), memberBindingList.ToArray());
Expression<Func<TEntityVM, TEntity>> lambda = Expression.Lambda<Func<TEntityVM, TEntity>>(memberInitExpression, new ParameterExpression[] { parameterExpression });
return lambda.Compile();
好久前测过一次速度,找不到数据了,现在懒得搞了,有读者自己测出来的话可以发一下出来,
标签:Mapper,Name,C#,item,typeof,PropertyType,Expression,string,Lambda From: https://www.cnblogs.com/LazYu/p/18102030