我正在使用Code First开发与Entity Framework。问题是: 克隆延迟加载列表时,列表元素的类型为:
System.Data.Entity.DynamicProxies.Node_CB2936E7A8389F56009639CD3D732E4B509C4467531A6AFB3A143429D77A07DF
我的泛型函数将其视为System.Object
。在传递给Clone
函数之前,有没有办法将此对象强制转换为父类?还是其他想法?
因为我只需要克隆到特定的深度,所以我不能序列化整个树结构然后反序列化它。
我的模特是:
public class Node
{
public int Id { get; set; }
public String Name { get; set; }
public virtual IList<Node> Nodes { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public virtual Node Parent { get; set; }
}
功能用于克隆:
protected T Clone<T>(T entity, int depth) where T : new()
{
var cloned = new T();
foreach (var property in cloned.GetType().GetProperties())
{
if (property.PropertyType.Namespace == "System" && property.CanWrite)
{
property.SetValue(cloned, property.GetValue(entity));
}
else if (depth > 0 && property.CanWrite)
{
if (property.PropertyType.Namespace == "System.Collections.Generic")
{
var type = property.PropertyType.GetGenericArguments()[0];
Type genericListType = typeof(List<>).MakeGenericType(type);
var collection = (IList)Activator.CreateInstance(genericListType);
var value = property.GetValue(entity);
foreach (var element in value as IEnumerable)
{
collection.Add(Clone(element, depth - 1)); // here is Error:
//The value “System.Object” is not of type “Sandbox.Models.Node” and cannot be used in this generic collection. Parameter name: value
//I should cast element to its parent class but how?
}
property.SetValue(cloned, collection);
}
}
}
return cloned;
}
此函数适用于非实体框架对象。
Clone
函数的用法是:
var cloned = Clone(context.Nodes.Find(10), 2);
任何帮助都将不胜感激。
答案 0 :(得分:1)
您的foreach循环中var
所遇到的问题属于Object
类型,因此new T();
内部调用Clone
将执行{{1}而不是new Object()
。你需要做的是使用反射进行新的调用。
new WhateverTheTypeTheListHad()
因为递归调用不依赖于知道传递的类型,所以我更喜欢将逻辑拆分并使递归内部版本非泛型并且只传递Object。当你有错误的假设时(例如在protected T Clone<T>(T entity, int depth) where T : new()
{
return (T)CloneInternal(entity, depth);
}
private object CloneInternal(object entity, int depth)
{
var cloned = Activator.CreateInstance(entity.GetType());
foreach (var property in cloned.GetType().GetProperties())
{
if (property.PropertyType.Namespace == "System" && property.CanWrite)
{
property.SetValue(cloned, property.GetValue(entity));
}
else if (depth > 0 && property.CanWrite)
{
if (property.PropertyType.Namespace == "System.Collections.Generic")
{
var type = property.PropertyType.GetGenericArguments()[0];
Type genericListType = typeof(List<>).MakeGenericType(type);
var collection = (IList)Activator.CreateInstance(genericListType);
var value = property.GetValue(entity);
foreach (var element in value as IEnumerable)
{
collection.Add(CloneInternal(element, depth - 1));
}
property.SetValue(cloned, collection);
}
}
}
return cloned;
}
上调用new
),它会更加明显。
但是,您的代码还有其他问题。例如,你对Object
中的任何类型进行内部递归,但是你总是创建一个System.Collections.Generic
,该集合是其他的(例如List<genericListType>
,在EF中非常常见)您的代码将在HashSet<genericListType>
来电时失败。
这是一个快速重写来处理任何包含property.SetValue(cloned, collection);
,IList
和IList<T>
(并且有一个默认构造函数)的内容,它应该覆盖您将遇到的所有集合中的90%
ISet<T>