使用表达式进行深度克隆.新建和表达式树
本文关键字:表达式 新建 深度 | 更新日期: 2023-09-27 17:59:21
我有两个生成的接口IPerson
和IAddress
。
然而,我已经定义了从那些基本接口继承的属性接口
接口
public interface IPerson_Name : IPerson { String Name{get;set;}}
public interface IPerson_Addresses : IPerson
{
ICollection<IAddress> Addresses{ get; set; }
IAddress NewAddress();
}
public interface IAddress_Line1 : IAddress
{
String Line1 { get; set; }
}
然后我有两个基本接口的实现
实施
public class Person : IPerson
{
public String Name { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class PersonPoco : IPerson_Name, IPerson_Addresses
{
public string Name { get; set; }
public ICollection<IAddress> Addresses { get; set; }
public IAddress NewAddress()
{
return new AddressPoco();
}
}
public class Address : IAddress
{
public String Line1 { get; set; }
}
public class AddressPoco : IAddress_Line1
{
public String Line1 { get; set; }
}
我正在尝试构建表达式树,以便在PersonPoco和Person以及任何其他IPerson 之间进行转换
我正在创建一个CopyTo函数。
复制范围
public static class IPersonExt
{
public static IQueryable<TDest> CopyTo<TSrc,TDest>(this IQueryable<TSrc> persons)
where TSrc : IPerson, new()
where TDest: IPerson, new()
{
var innerLambda = (LambdaExpression)CopyTo(typeof (TSrc), typeof (TDest));
var copyExpr = Expression.Lambda<Func<TSrc, TDest>>(innerLambda.Body, innerLambda.Parameters);
return persons.Select(copyExpr);
}
internal static LambdaExpression CopyTo(Type tSrc, Type tDest)
{
var dest = Activator.CreateInstance(tDest);
var personparam = Expression.Parameter(tSrc);
var destNewExpr = Expression.New(tDest);
var memberbindings = new List<MemberBinding>();
IPerson_Name destName;
IPerson_Name srcName;
if ((tDest.IsInstanceOfType(typeof(IPerson_Name)) || tDest.IsInstanceOfType(typeof(Person))) &&
(tSrc.IsInstanceOfType(typeof(IPerson_Name)) || tSrc.IsInstanceOfType(typeof(Person))))
{
memberbindings.Add(Expression.Bind(tDest.GetProperty("Name"), Expression.Property(personparam, "Name")));
}
var toEntityParameterExpression = Expression.MemberInit(destNewExpr, memberbindings);
return Expression.Lambda(
toEntityParameterExpression,
personparam
);
}
}
我的意图是保存IPerson和IAddress的每次实现到实现转换的编译输出。
如果你想了解更多关于我意图的信息,请看这个问题
编辑
我把基本参数复制到了一个新版本。到目前为止,它对基本属性有效。我已经更新了上的代码
EDIT:显然linq-to实体无法理解这些表达式中的方法调用。所以这也于事无补。
以下是要点
实现以下接口的接口将能够复制其通用接口的属性。
public interface IEntity
{
}
public interface IEntityObject : IEntity
{
}
public interface IEntityCollection : IEntity
{
}
public interface IEntityProperty : IEntity
{
}
然后,对于实体的每个属性,您需要创建一个接口
public interface IThing:IEntity{}
public interface IThing_Property: IThing, IEntityProperty
{
int SimpleProperty{get;set;}
}
public interface IThing_ComplexProperty: IThing, IEntityObject{}
public interface IThing_ComplexProperty<T> : IEntity_ComplexProperty where T:class, IEntity,new()
{
T SomeProperty{get;set;}
}
public interface IThing_CollectionProperty: IThing, IEntityCollection{}
public interface IThing_CollectionProperty<T> :IEntity_CollectionProperty where T:class, IEntity,new()
{
IEnumerable<T> SomeCollectionProperty{get;set;}
}
现在如果你做
IQueryable<SomeThingImpl> x;
IQueryable<SomeOtherThingImpl> y = x.CopyTo<SomeThingImpl,SomeOtherThingImpl>()
在它枚举之前,我还没有被调用。
此外,如果泛型类型共享任何IEntity接口,它们也将被复制。
为了更全面地实现,这里有另一个要点
以下是类的外观
public static class CopyToExt
{
private static readonly ConcurrentDictionary<Type, LambdaExpression> expressions;
private static readonly ConcurrentDictionary<Type, Object> funcs;
private static readonly ConcurrentDictionary<Type, LambdaExpression> mergeStackExpr;
private static ConcurrentDictionary<Type, Object> mergeStackFunc;
public static MethodInfo AsQueryableMethod;
public static MethodInfo SelectMethod;
public static MethodInfo ToListMethod;
public static MethodInfo CopyToMethod;
public static MethodInfo AddMergeMethod;
public static MethodInfo CreateMergeMethod;
public static MethodInfo TryCopyMethod;
static CopyToExt()
{
mergeStackExpr = new ConcurrentDictionary<Type, LambdaExpression>(new Dictionary<Type, LambdaExpression>());
foreach (MethodInfo m in typeof(Queryable).GetMethods().Where(m => m.Name == "Select"))
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("selector")))
if (p.ParameterType.GetGenericArguments().Any(x => x.GetGenericArguments().Count() == 2))
SelectMethod = (MethodInfo)p.Member;
foreach (MethodInfo m in typeof(Enumerable).GetMethods().Where(m => m.Name == "ToList"))
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("source")))
if (p.ParameterType.GetGenericArguments().Count() == 1)
ToListMethod = (MethodInfo)p.Member;
foreach (MethodInfo m in typeof(Queryable).GetMethods().Where(m => m.Name == "AsQueryable"))
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("source")))
if (p.ParameterType.GetGenericArguments().Count() == 1)
AsQueryableMethod = (MethodInfo)p.Member;
CreateMergeMethod = typeof(CopyToExt).GetMethods().First(m => m.Name == "CreateMergeStack");
AddMergeMethod = typeof(MergeStack).GetMethods().First(m => m.Name == "AddReturnOrCreateAddReturn");
TryCopyMethod = typeof(MergeStack).GetMethods().First(m => m.Name == "TryCopy");
}
public static IQueryable<TDest> CopyTo<TSrc, TDest>(this IQueryable<TSrc> queryable)
where TSrc : class, IEntity
where TDest : class, IEntity
{
var copyExpr = CreateTryCopy<TSrc, TDest>();
var ms = CreateMergeStack<TSrc>();
return queryable.Select(ms).Select(copyExpr);
}
public static IEnumerable<TDest> CopyTo<TSrc, TDest>(this IEnumerable<TSrc> queryable)
where TSrc : class, IEntity
where TDest : class, IEntity
{
return queryable.AsQueryable().CopyTo<TSrc, TDest>().AsEnumerable();
}
public static Expression<Func<TSrc, Stackholder<TSrc>>> CreateMergeStack<TSrc>()
{
return mergeStackExpr.GetOrAdd(typeof(TSrc), (type) =>
{
var shtype = typeof(Stackholder<TSrc>);
var parm = Expression.Parameter(typeof(TSrc));
var destNewExpr = Expression.New(shtype);
var methodcall = Expression.Call(null,
CopyToExt.AddMergeMethod);
var memInit = Expression.MemberInit(destNewExpr,
Expression.Bind(shtype.GetField("Src"), parm),
Expression.Bind(shtype.GetField("Ms"), methodcall));
return Expression.Lambda<Func<TSrc, Stackholder<TSrc>>>(memInit, parm);
}) as Expression<Func<TSrc, Stackholder<TSrc>>>;
}
public static Expression<Func<Stackholder<TSrc>, TDest>> CreateTryCopy<TSrc, TDest>()
{
var shtype = typeof(Stackholder<TSrc>);
var parentParam = Expression.Parameter(shtype);
var SrcExpr = Expression.PropertyOrField(parentParam, "Src");
var MSExpr = Expression.PropertyOrField(parentParam, "Ms");
var tcMethod = Expression.Call(MSExpr,
TryCopyMethod.MakeGenericMethod(new Type[] { typeof(TSrc), typeof(TDest) }),
SrcExpr);
return Expression.Lambda<Func<Stackholder<TSrc>, TDest>>(tcMethod, parentParam);
}
internal static bool Impliments(this Type type, Type inheritedType)
{
return (type.IsSubclassOf(inheritedType) || type.GetInterface(inheritedType.FullName) != null);
}
internal static bool Impliments<T>(this Type type, Type inheritedType = null)
{
return type.Impliments(typeof(T));
}
private static ConcurrentDictionary<Type, Object> AssignDict =
new ConcurrentDictionary<Type, Object>(new Dictionary<Type, Object>());
internal static Func<TSrc, TDest, MergeStack, TDest> Assign<TSrc, TDest>()
{
return (Func<TSrc, TDest, MergeStack, TDest>) AssignDict.GetOrAdd(typeof (Func<TSrc, TDest>), (indexType) =>
{
var tSrc = typeof (TSrc);
var tDest = typeof (TDest);
var srcEntityInterfaces = tSrc.GetInterfaces().Where(x => x.Impliments<IEntity>());
var destEntityInterfaces = tDest.GetInterfaces().Where(x => x.Impliments<IEntity>());
var srcParam = Expression.Parameter(tSrc);
var destParam = Expression.Parameter(tDest);
var MSExpr = Expression.Parameter(typeof (MergeStack));
var common = destEntityInterfaces.Intersect(srcEntityInterfaces);
var memberbindings = common.Where(x => x.Impliments<IEntityProperty>())
.Select(type => type.GetProperties().First())
.Select(
prop =>
Expression.Assign(Expression.Property(destParam, prop.Name),
Expression.Property(srcParam, prop.Name)))
.Cast<Expression>().ToList();
foreach (var type in common.Where(x => x.Impliments<IEntityObject>()))
{
var destSubType = destEntityInterfaces.First(x => x.Impliments(type))
.GetGenericArguments()
.First();
var srcSubType = srcEntityInterfaces.First(x => x.Impliments(type))
.GetGenericArguments()
.First();
var dProp = destEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First();
var sProp = srcEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First();
var tcParam = Expression.Parameter(srcSubType);
var tcMethod = Expression.Call(MSExpr,
TryCopyMethod.MakeGenericMethod(new Type[] {srcSubType, destSubType}),
tcParam);
LambdaExpression mergeLambda = Expression.Lambda(tcMethod, tcParam);
MemberExpression memberExpression = Expression.Property(srcParam, sProp.Name);
InvocationExpression invocationExpression = Expression.Invoke(mergeLambda,
Expression.Property(srcParam, sProp.Name));
var check = Expression.Condition(
Expression.MakeBinary(ExpressionType.NotEqual, memberExpression,
Expression.Constant(null, sProp.PropertyType)), invocationExpression,
Expression.Constant(null, invocationExpression.Type));
BinaryExpression binaryExpression = Expression.Assign(Expression.Property(destParam, dProp.Name),
check);
memberbindings.Add(binaryExpression);
}
foreach (var type in common.Where(x => x.Impliments<IEntityCollection>()))
{
var destSubType = destEntityInterfaces.First(x => x.Impliments(type))
.GetGenericArguments()
.First();
var srcSubType = srcEntityInterfaces.First(x => x.Impliments(type))
.GetGenericArguments()
.First();
var dProp = destEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First();
var sProp = srcEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First();
var tcParam = Expression.Parameter(srcSubType);
var tcMethod = Expression.Call(MSExpr,
TryCopyMethod.MakeGenericMethod(new Type[] {srcSubType, destSubType}),
tcParam);
LambdaExpression mergeLambda = Expression.Lambda(tcMethod, tcParam);
var memberExpression = Expression.Property(srcParam, sProp.Name);
var selectExpr = Expression.Call(null,
AsQueryableMethod.MakeGenericMethod(new Type[] {srcSubType}),
new Expression[] {memberExpression});
selectExpr = Expression.Call(null,
CopyToExt.SelectMethod.MakeGenericMethod(new Type[] {srcSubType, destSubType}),
new Expression[] {selectExpr, mergeLambda});
selectExpr = Expression.Call(null,
CopyToExt.ToListMethod.MakeGenericMethod(new Type[] {destSubType}),
new Expression[] {selectExpr});
var check = Expression.Condition(
Expression.MakeBinary(ExpressionType.NotEqual, memberExpression,
Expression.Constant(null, sProp.PropertyType)), selectExpr,
Expression.Constant(null, selectExpr.Type));
memberbindings.Add(Expression.Assign(Expression.Property(destParam, dProp.Name), check));
}
memberbindings.Add(destParam);
return
Expression.Lambda<Func<TSrc, TDest, MergeStack, TDest>>(Expression.Block(memberbindings),
new ParameterExpression[] {srcParam, destParam, MSExpr}).Compile();
});
}
}
public class Stackholder<TSrc>
{
public MergeStack Ms;
public TSrc Src;
}
public class MergeStack
{
private static ConcurrentDictionary<Thread, MergeStack> StackDict = new ConcurrentDictionary<Thread, MergeStack>(new Dictionary<Thread, MergeStack>());
private readonly Dictionary<Type, Dictionary<Object, Object>> _mergeObjDict = new Dictionary<Type, Dictionary<object, object>>();
public static MergeStack AddReturnOrCreateAddReturn()
{
return StackDict.GetOrAdd(Thread.CurrentThread, (x) => new MergeStack() { });
}
public TDest TryCopy<TSrc, TDest>(TSrc Src)
where TSrc : class, IEntity
where TDest : class, IEntity, new()
{
if (Src == null) return null;
var objToIndex = new TDest();
Dictionary<object, object> objToObj;
if (!_mergeObjDict.ContainsKey(objToIndex.GetType()))
{
objToObj = new Dictionary<object, object>();
_mergeObjDict.Add(objToIndex.GetType(), objToObj);
}
else
{
objToObj = _mergeObjDict[objToIndex.GetType()];
}
if (!objToObj.ContainsKey(Src))
{
objToObj.Add(Src, objToIndex);
return CopyToExt.Assign<TSrc, TDest>()(Src, objToIndex, this);
}
return objToObj[Src] as TDest;
}
}
下面是一个文本模板文件,用于从EF图