using Masuit.Tools.Mapping.Exceptions;
using Masuit.Tools.Mapping.Helper;
using Masuit.Tools.Mapping.Visitor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Masuit.Tools.Mapping.Core
{
///
/// mapper配置基类
///
public abstract class MapperConfigurationBase
{
private Delegate _delegateCallForNew;
private Delegate _delegateCallForExisting;
private Func _constructorFunc;
private bool _isInitialized;
private readonly MethodInfo _selectMethod;
private readonly MethodInfo _toListMethod;
private readonly List _propertiesToIgnore;
internal ParameterExpression paramClassSource;
internal MapperExpressionVisitor visitorMapper;
internal List memberForNew;
internal LambdaExpression expressionForExisting;
///
/// 属性映射对应关系
/// Item1 : 源表达式
/// Item2 : 目标表达式
/// Item3 : 检查null值
/// Item4 : mapper别名
///
protected List> PropertiesMapping { get; private set; }
///
/// 需要被忽略映射的属性
///
protected ReadOnlyCollection PropertiesToIgnore => _propertiesToIgnore.AsReadOnly();
///
/// 是否使用服务依赖注入
///
public bool UseServiceLocator { get; protected set; }
///
/// 对象源类型
///
public Type SourceType { get; private set; }
///
/// 对象目标类型
///
public Type TargetType { get; private set; }
///
/// 获取mapper映射成员
///
public ReadOnlyCollection MemberToMapForNew => new ReadOnlyCollection(memberForNew);
///
/// mapper别名
///
public string Name { get; protected set; }
///
/// 构造函数
///
/// 源类型
/// 目标类型
/// 属性名
/// 别名
protected MapperConfigurationBase(Type source, Type destination, string paramName, string name = null)
{
TargetType = destination;
SourceType = source;
paramClassSource = Expression.Parameter(source, paramName);
Name = string.IsNullOrEmpty(name) ? paramName : name;
_propertiesToIgnore = new List();
PropertiesMapping = new List>();
visitorMapper = new MapperExpressionVisitor(paramClassSource);
memberForNew = new List();
_selectMethod = typeof(Enumerable).GetMethods().Where(m => m.Name == "Select").Select(x => x.GetParameters().First(p => p.Name.Equals("selector") && p.ParameterType.GetGenericArguments().Length == 2)).First().Member as MethodInfo;
_toListMethod = typeof(Enumerable).GetMethod("ToList");
}
///
/// 获取mapper委托
///
///
public Delegate GetDelegate()
{
if (!_isInitialized)
{
throw new MapperNotInitializedException(SourceType, TargetType);
}
// 因为在这里有映射器的性能问题,而缓存委托会显着缩短处理时间,如果没有表达式编译每次编译会很慢
if (_delegateCallForNew == null)
{
MemberInitExpression exp = GetMemberInitExpression();
_delegateCallForNew = Expression.Lambda(exp, paramClassSource).Compile();
}
return _delegateCallForNew;
}
///
/// 获取现有目标类型的委托。
///
///
public Delegate GetDelegateForExistingTarget()
{
if (!_isInitialized)
{
throw new MapperNotInitializedException(SourceType, TargetType);
}
// 因为在这里有映射器的性能问题,而缓存委托会显着缩短处理时间,如果没有表达式编译每次编译会很慢
if (_delegateCallForExisting == null)
{
CreateMemberAssignementForExistingTarget();
}
return _delegateCallForExisting;
}
///
/// 获取泛型的Lambda表达式
///
public LambdaExpression GetGenericLambdaExpression()
{
MemberInitExpression exp = GetMemberInitExpression();
return Expression.Lambda(exp, paramClassSource);
}
///
/// 获取目标类型的实际类型
///
public Type GetDestinationType()
{
return GetRealType(TargetType);
}
///
/// 忽略目标类型的属性
///
/// 对象源类型
/// 对象目标类型
/// 目标对象的属性
///
protected MapperConfigurationBase IgnoreBase(Expression> propertyDest)
{
// 添加到映射列表并且可以继续操作
_propertiesToIgnore.Add(GetPropertyInfo(propertyDest));
return this;
}
///
/// 获取映射器实例
///
/// 源类型
/// 目标类型
/// 如果没找到是否需要抛出异常
/// mapper别名
///
///
protected static MapperConfigurationBase GetMapper(Type typeOfSource, Type typeOfTarget, bool throwExceptionOnNoFound, string name = null)
{
var mapperExterne = MapperConfigurationCollectionContainer.Instance.Find(typeOfSource, typeOfTarget, name);
// 如果没有任何配置,手动抛出异常
if (mapperExterne == null && throwExceptionOnNoFound)
{
throw new NoFoundMapperException(typeOfSource, typeOfTarget);
}
return mapperExterne;
}
///
/// 创建公共成员
///
protected void CreateCommonMember()
{
PropertyInfo[] propertiesSource = SourceType.GetProperties();
foreach (PropertyInfo propSource in propertiesSource)
{
PropertyInfo propDest = TargetType.GetProperty(propSource.Name);
if (propDest != null)
{
// 检查是否已存在或被忽略。
bool ignorePropDest = _propertiesToIgnore.Exists(x => x.Name == propDest.Name) || PropertiesMapping.Exists(x => GetPropertyInfo(x.Item2).Name == propDest.Name);
if (propDest.CanWrite && !ignorePropDest)
{
Type sourceType = propSource.PropertyType;
Type destType = propDest.PropertyType;
bool isList = IsListOf(destType);
if (isList)
{
sourceType = TypeSystem.GetElementType(propSource.PropertyType);
destType = TypeSystem.GetElementType(propDest.PropertyType);
}
var canCreateConfig = CanCreateConfig(sourceType, destType);
if (canCreateConfig.CanCreate)
{
// 只创造现有的关系
Expression expSource = Expression.MakeMemberAccess(paramClassSource, propSource);
ParameterExpression paramDest = Expression.Parameter(TargetType, "t");
Expression expDest = Expression.MakeMemberAccess(paramDest, propDest);
PropertiesMapping.Add(Tuple.Create(expSource, expDest, false, canCreateConfig.MapperName));
}
}
}
}
}
private static CreateConfig CanCreateConfig(Type typeSource, Type typeTarget)
{
CreateConfig result = new CreateConfig
{
CanCreate = typeSource == typeTarget
};
//不是同一类型
if (!result.CanCreate)
{
//查找是否存在映射器
var mapper = MapperConfigurationCollectionContainer.Instance.Find(typeSource, typeTarget);
if (mapper != null)
{
result.MapperName = mapper.Name;
result.CanCreate = true;
}
}
return result;
}
///
/// 检查并配置mapper
///
/// 配置表达式树
///
///
///
protected void CheckAndConfigureMapping(ref Tuple configExpression)
{
Type typeSource = configExpression.Item1.Type;
Type typeTarget = configExpression.Item2.Type;
// 正常情况下,目标表达式是一个成员表达式树
PropertyInfo propTarget = GetPropertyInfo(configExpression.Item2);
if (propTarget.CanWrite)
{
CheckAndRemoveMemberDest(propTarget.Name);
if (!IsListOf(typeTarget))
{
CreatBindingFromSimple(ref configExpression, typeSource, typeTarget, propTarget);
}
else
{
CreateBindingFromList(ref configExpression, typeSource, typeTarget, propTarget);
}
}
else
{
throw new ReadOnlyPropertyException(propTarget);
}
}
///
/// 检查并移除目标成员
///
/// 属性名
protected void CheckAndRemoveMemberDest(string properyName)
{
Predicate exp = m => m.Member.Name == properyName;
if (memberForNew.Exists(exp))
{
memberForNew.RemoveAll(exp);
}
}
///
/// 获取成员初始化表达式。
///
///
protected MemberInitExpression GetMemberInitExpression()
{
Type typeDest = GetDestinationType();
NewExpression newClassDest = Expression.New(typeDest);
MemberInitExpression exp = Expression.MemberInit(newClassDest, MemberToMapForNew);
return exp;
}
///
/// 创建成员绑定。
///
/// 属性表达式
/// 目标属性
/// 是否检查null值
protected void CreateMemberBinding(Expression propertyExpression, MemberInfo propertyTarget, bool checkIfNull)
{
// 访问表达式进行转换
Expression result = visitorMapper.Visit(propertyExpression, checkIfNull);
MemberAssignment bind = Expression.Bind(propertyTarget, result);
memberForNew.Add(bind);
}
///
/// 将表达式源的映射分配给属性目标。
///
/// 属性源类型
/// 属性目标类型
/// 是否检查null值
/// 要使用的映射器的别名
internal MapperConfigurationBase ForMemberBase(Expression getPropertySource, Expression getPropertyDest, bool checkIfNull, string name = null)
{
// 添加到映射列表并且可以继续操作
PropertiesMapping.Add(Tuple.Create(getPropertySource, getPropertyDest, checkIfNull, name));
return this;
}
///
/// 获取属性信息。
///
/// 属性表达式树
///
///
/// 这种表达方式不承担职责,或者这种类型的表达式是无效的
///
protected static PropertyInfo GetPropertyInfo(Expression propertyExpression)
{
var expressionToAnalyse = propertyExpression.NodeType == ExpressionType.Lambda ? (propertyExpression as LambdaExpression).Body : propertyExpression;
switch (expressionToAnalyse.NodeType)
{
case ExpressionType.Convert:
Expression operand = (expressionToAnalyse as UnaryExpression).Operand;
switch (operand.NodeType)
{
case ExpressionType.MemberAccess:
return (operand as MemberExpression).Member as PropertyInfo;
default:
throw new NotImplementedException("这种表达方式目前尚未支持");
}
case ExpressionType.MemberAccess:
return (expressionToAnalyse as MemberExpression).Member as PropertyInfo;
default:
throw new NotImplementedException("这种表达方式目前尚未支持");
}
}
internal void Initialize(Func constructor)
{
CreateMappingExpression(constructor);
CreateMemberAssignementForExistingTarget();
}
///
/// 为现有目标对象创建成员
///
public virtual void CreateMemberAssignementForExistingTarget()
{
if (PropertiesMapping.Count > 0)
{
// 用于更改原始表达式的参数。
var paramTarget = Expression.Parameter(TargetType, paramClassSource.Name.Replace("s", "t"));
ChangParameterExpressionVisitor visitSource = new ChangParameterExpressionVisitor(paramClassSource);
ChangParameterExpressionVisitor visitTarget = new ChangParameterExpressionVisitor(paramTarget);
List finalAssign = new List();
foreach (var item in PropertiesMapping)
{
var propToAssign = visitTarget.Visit(item.Item2);
var assignExpression = visitSource.Visit(item.Item1);
Type sourceType = TypeSystem.GetElementType(item.Item2.Type);
Type targetType = TypeSystem.GetElementType(item.Item1.Type);
if (string.IsNullOrEmpty(item.Item4))
{
object defaultValue = MapperHelper.GetDefaultValue(item.Item2.Type);
Expression defaultExpression = Expression.Constant(defaultValue, item.Item2.Type);
Expression checkIfNull = Expression.NotEqual(assignExpression, defaultExpression);
if (item.Item3)
{
Expression setIf = Expression.IfThen(checkIfNull, Expression.Assign(propToAssign, assignExpression));
finalAssign.Add(setIf);
}
else
{
if (!IsListOf(propToAssign.Type))
{
finalAssign.Add(Expression.Assign(propToAssign, assignExpression));
}
else
{
if (sourceType == targetType)
{
Expression toListExp = Expression.Call(_toListMethod.MakeGenericMethod(sourceType), assignExpression);
Expression setIf = Expression.IfThen(checkIfNull, Expression.Assign(propToAssign, assignExpression));
finalAssign.Add(setIf);
finalAssign.Add(toListExp);
}
}
}
}
else // 来自其他映射器。
{
var mapper = GetMapper(sourceType, targetType, false, item.Item4);
if (mapper != null)
{
mapper.Initialize(_constructorFunc);
Expression defaultExpression = Expression.Constant(MapperHelper.GetDefaultValue(item.Item2.Type), item.Item2.Type);
if (!IsListOf(propToAssign.Type))
{
ChangParameterExpressionVisitor changeVisitor = new ChangParameterExpressionVisitor(propToAssign, assignExpression);
Expression modifiedExpression = changeVisitor.Visit(mapper.expressionForExisting.Body);
Expression checkIfNull = Expression.NotEqual(propToAssign, defaultExpression);
Expression setIf = Expression.IfThen(checkIfNull, modifiedExpression);
assignExpression = setIf;
}
else
{
//Expression selectExp = Expression.Call(_selectMethod.MakeGenericMethod(sourceType), Expression.Constant(mapper.GetDelegate()));
Expression checkIfNull = Expression.NotEqual(propToAssign, defaultExpression);
Expression setIf = Expression.IfThen(checkIfNull, Expression.Assign(propToAssign, assignExpression));
assignExpression = setIf;
}
finalAssign.Add(assignExpression);
}
}
}
if (finalAssign.Count > 0 && _delegateCallForExisting == null)
{
expressionForExisting = Expression.Lambda(Expression.Block(typeof(void), finalAssign), paramClassSource, paramTarget);
// 编译
_delegateCallForExisting = expressionForExisting.Compile();
}
}
}
internal Expression GetLambdaDest(string propertyName)
{
var exp = PropertiesMapping.Find(x => GetPropertyInfo(x.Item1).Name == propertyName);
if (exp != null)
{
var final = exp.Item2;
if (final.NodeType == ExpressionType.Convert)
{
final = (final as UnaryExpression).Operand;
}
return final;
}
return null;
}
///
/// 创建映射表达式树
///
///
public virtual void CreateMappingExpression(Func constructor)
{
if (!_isInitialized)
{
// 它是在处理前放置以避免递归循环。
_isInitialized = true;
_constructorFunc = constructor;
CreateCommonMember();
var propsToAnalyse = PropertiesMapping.ToList(); // 克隆列表以便于更改。
for (int i = 0; i < propsToAnalyse.Count; i++)
{
var propToAnalyse = propsToAnalyse[i];
CheckAndConfigureMapping(ref propToAnalyse);
propsToAnalyse[i] = propToAnalyse;
}
PropertiesMapping = propsToAnalyse;
// 编译
GetDelegate();
}
}
internal Type GetRealType(Type typeToFind)
{
if (UseServiceLocator)
return _constructorFunc(typeToFind).GetType();
return typeToFind;
}
internal PropertiesNotMapped GetPropertiesNotMapped()
{
PropertiesNotMapped result = new PropertiesNotMapped();
// 克隆属性信息
List sourceProperties = SourceType.GetProperties().ToList();
List targetProperties = TargetType.GetProperties().ToList();
PropertiesVisitor visitor = new PropertiesVisitor(TargetType);
foreach (var members in memberForNew)
{
var members1 = members;
sourceProperties.RemoveAll((p) => members1.Member.Name == p.Name);
targetProperties.RemoveAll((p) => visitor.GetProperties(members.Expression).Contains(p));
}
// 检查被忽略映射的成员
sourceProperties.RemoveAll((p) => _propertiesToIgnore.Contains(p));
result.sourceProperties = sourceProperties;
result.targetProperties = targetProperties;
return result;
}
///
/// 获取排序表达式树
///
/// 属性名
///
public LambdaExpression GetSortedExpression(string propertySource)
{
var exp = PropertiesMapping.Find(x => GetPropertyInfo(x.Item2).Name == propertySource);
if (exp == null)
{
throw new PropertyNoExistException(propertySource, TargetType);
}
// 更改参数
var visitor = new MapperExpressionVisitor(paramClassSource);
var result = visitor.Visit(exp.Item1);
return Expression.Lambda(result, paramClassSource);
}
private static bool IsListOf(Type typeTarget)
{
// 特殊情况字符串是char数组。
if (typeTarget == typeof(string))
{
return false;
}
Func test = t => t.IsAssignableFrom(typeof(IEnumerable));
return test(typeTarget) || typeTarget.GetInterfaces().Any(test);
}
private MapperConfigurationBase GetAndCheckMapper(Type typeOfSource, Type typeOfTarget, string name)
{
var externalMapper = GetMapper(typeOfSource, typeOfTarget, false, name);
if (externalMapper != null)
{
return externalMapper;
}
//如果找不到具有别名的映射器
if (!string.IsNullOrEmpty(name))
{
throw new NoFoundMapperException(name);
}
throw new NotSameTypePropertyException(typeOfSource, typeOfTarget);
}
private void CreatBindingFromSimple(ref Tuple configExpression, Type typeSource, Type typeTarget, PropertyInfo propTarget)
{
// 没有特殊的操作
if (typeSource == typeTarget)
{
// 创建成员绑定
CreateMemberBinding(configExpression.Item1, propTarget, configExpression.Item3);
}
else
{
// 尝试查找mapper
MapperConfigurationBase externalMapper = GetAndCheckMapper(typeSource, typeTarget, configExpression.Item4);
// 如果此时未初始化映射器
externalMapper.CreateMappingExpression(_constructorFunc);
// 默认情况下,检查对象的null
Expression mapExpression = externalMapper.GetMemberInitExpression();
Expression defaultExpression = Expression.Constant(MapperHelper.GetDefaultValue(configExpression.Item1.Type), configExpression.Item1.Type);
// 修改成员
Expression expSource = visitorMapper.Visit(configExpression.Item1);
ChangParameterExpressionVisitor changeParamaterVisitor = new ChangParameterExpressionVisitor(expSource);
mapExpression = changeParamaterVisitor.Visit(mapExpression);
// 现在可以创建正确的参数。
Expression checkIfNull = Expression.NotEqual(expSource, defaultExpression);
// 创建条件
var checkExpression = Expression.Condition(checkIfNull, mapExpression, Expression.Constant(MapperHelper.GetDefaultValue(mapExpression.Type), mapExpression.Type), mapExpression.Type);
MemberAssignment bindExpression = Expression.Bind(propTarget, checkExpression);
// 找到了映射器但没有配置
if (string.IsNullOrEmpty(configExpression.Item4))
{
configExpression = Tuple.Create(configExpression.Item1, configExpression.Item2, configExpression.Item3, externalMapper.Name);
}
memberForNew.Add(bindExpression);
}
}
private void CreateBindingFromList(ref Tuple configExpression, Type typeSource, Type typeTarget, PropertyInfo propTarget)
{
Type sourceTypeList = TypeSystem.GetElementType(typeSource);
Type destTypeList = TypeSystem.GetElementType(typeTarget);
if (sourceTypeList == destTypeList)
{
if (configExpression.Item2.NodeType == ExpressionType.MemberAccess)
{
CreateMemberBinding(configExpression.Item1, propTarget, configExpression.Item3);
}
}
// 使用Enumerable类的select方法来更改类型
else
{
var externalMapper = GetAndCheckMapper(sourceTypeList, destTypeList, configExpression.Item4);
externalMapper.CreateMappingExpression(_constructorFunc);
MemberAssignment expBind;
Expression expSource = configExpression.Item1;
ChangParameterExpressionVisitor visitor = new ChangParameterExpressionVisitor(paramClassSource);
expSource = visitor.Visit(expSource);
// 为了与EF / LINQ2SQL兼容。
LambdaExpression expMappeur = externalMapper.GetGenericLambdaExpression();
// 创建对Select方法的调用,在Enumerable的Select中插入一个lambda表达式(参数是一个委托),通常情况下,这是不可能的,但(个人认为)编译器就像这样创建并且LINQ2SQL / EF是可以进行sql查询的
Expression select = Expression.Call(_selectMethod.MakeGenericMethod(sourceTypeList, destTypeList), new[]
{
expSource,
expMappeur
});
// 创建对ToList方法的调用
Expression toList = Expression.Call(_toListMethod.MakeGenericMethod(destTypeList), select);
if (configExpression.Item3) // 如果要检查无效(使用EF / LinqTosql,则不需要)。
{
// 测试source的属性是否为null。
Expression checkIfNull = Expression.NotEqual(expSource, Expression.Constant(MapperHelper.GetDefaultValue(expSource.Type), expSource.Type));
// 有时候ToList方法不起作用,则使用实现ToList不起作用的类
Expression asExp = Expression.TypeAs(toList, propTarget.PropertyType);
// 创建条件表达式
Expression expCondition = Expression.Condition(checkIfNull, asExp, Expression.Constant(MapperHelper.GetDefaultValue(typeTarget), typeTarget));
// 分配给目标属性。
expBind = Expression.Bind(propTarget, expCondition);
}
else
{
// 分配给目标属性。
expBind = Expression.Bind(propTarget, toList);
}
// 查找mapper
if (string.IsNullOrEmpty(configExpression.Item4))
{
configExpression = Tuple.Create(configExpression.Item1, configExpression.Item2, configExpression.Item3, externalMapper.Name);
}
memberForNew.Add(expBind);
}
}
}
}