正如评论中提到的,您的反射代码不是问题,而是(正如异常消息明确告诉的那样)您间接触发了您的一个实体引用的重置这一事实。我的建议是双重的:要么修改你的反射代码,只复制标量属性(字符串、日期等)——或者忽略引用和集合——或者使用序列化:
public static T CloneBySerialization<T>(this T source) where T : EntityObject {
var serializer = new DataContractSerializer(typeof(T));
using (var ios = new MemoryStream()) {
serializer.WriteObject(ios, source);
ios.Seek(0, SeekOrigin.Begin);
return ((T) serializer.ReadObject(ios));
}
}
我必须警告你,使用这种方法你最终会得到完整的对象图或引用。如果克隆的对象是一个实体,您将无法使用它/将其附加到另一个上下文,因为引用和外键也已被“逐字”复制,这都可能导致冲突。如果您在键中使用标识列,问题会变得更糟。
在我之前的工作中,我在这些问题上做了很多魔术,就克隆而言,上面的代码就是你所需要的。所有,真的。
但是,要解决上下文问题和克隆实体的可用性,您必须清除引用并假设您也在 ¹ ↔ * 方向关系图中使用“根”实体(我希望我很清楚,因为故事很长)以下也是必要的。
public static void ClearReferences(this EntityObject entity) {
if (entity == null)
return;
foreach (var p in entity.GetType().GetProperties()) {
if (p.PropertyType.IsGenericType) {
var propertyType = p.PropertyType.GetGenericTypeDefinition();
if (propertyType == typeof(EntityReference<>)) {
var reference = p.GetValue(entity) as dynamic;
if (reference.EntityKey != null) {
reference.EntityKey = null;
((EntityObject) reference.Value).ClearReferences();
}
}
if (propertyType == typeof(EntityCollection<>)) {
var children = (p.GetValue(entity) as IEnumerable<EntityObject>).ToList(); // covariance
foreach (var child in children)
child.ClearReferences();
}
}
}
}
所以这个想法是你首先克隆(通过序列化/反序列化),然后你“净化”。