我正在尝试首先编写一个通用的实体框架代码复制例程。 此例程复制源属性,创建新的子实体,复制对查找实体的引用以及复制子集合。 它似乎工作。 在检查创建的实体时,所有子节点,查找和集合都存在,但是当我调用DbContext.ChangeTracker.HasChanges()时,我得到以下错误;
'The property 'ID' is part of the object's key information and cannot be modified. '
在调查中,似乎新创建的子实体(而不是新创建的根实体/父实体)引发了错误
以下是填充相关部分的例程(if(dest.PropertyHasCustomAttribute(propertyInfo.Name,typeof(EntityChildAttribute)));)
public static void CloneCopy<T>( T original, object destination) where T : class
{
var dest = destination as T;
if (dest == null)
throw new Exception("destination does not match source type");
PropertyInfo[] props = typeof(T).GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
{
if (!propertyInfo.CanWrite) continue;
if (propertyInfo.Name.Equals("ID")) continue;
if (propertyInfo.Name.EndsWith("ID") && propertyInfo.Name.Length > 2) continue;
var pv = propertyInfo.GetValue(original, null);
propertyInfo.SetValue(destination, pv, null);
}
else
{
//dont need to copy parent entity
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityParentAttribute)))
...
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityLookupAttribute)))
...
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityInterfaceLookupAttribute)))
...
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildAttribute)))
{
dynamic source = propertyInfo.GetValue(original, null);
var target = propertyInfo.GetValue(dest, null);
if (source == null) return;
if (target == null)
{
var t = source.GetType();
target = Activator.CreateInstance(t);
}
source.CopyMeToProvidedEntity(target);
propertyInfo.SetValue(dest, target, null);
}
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildCollectionAttribute)))
...
}
}
}
因此,他们可以复制自己,我的所有实体都在其基类上定义了CopyMeToProvidedEntity(target)方法,并覆盖了它们的实现。看起来像这样并调用上面的函数;
public override void CopyMeToProvidedEntity(object destination)
{
CloneUtil.CloneCopy(this, destination);
}
我还使用自己创建的附加属性(EntityParent,EntityLookup,EntityChild,EntityChildCollection)进一步定义了我的实体上的关联。
我有点卡住了。复制例程忽略ID,并且永远不会在新实体上写入它们。因此定义了ID;
[Browsable(false)]
[Key]
public int ID { get; set; }
初始化时始终设置为0
将非常感激地收到任何帮助
24/06/2017 - 添加了完整的CloneCopy例程
public static void CloneCopy<T>( T original, object destination) where T : class
{
var dest = destination as T;
if (dest == null)
throw new Exception("destination does not match source type");
//set cloning property so update triggers etc can be ignored
((IsCloneable)original).IsCloning = true;
((IsCloneable)dest).IsCloning = true;
PropertyInfo[] props = typeof(T).GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
{
if (!propertyInfo.CanWrite) continue;
if (propertyInfo.Name.Equals("ID")) continue;
if (propertyInfo.Name.EndsWith("ID") && propertyInfo.Name.Length > 2) continue;
var pv = propertyInfo.GetValue(original, null);
propertyInfo.SetValue(destination, pv, null);
}
else
{
//Shouldn't need to do anything here as Entity Framework handles it
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityParentAttribute)))
{
//object pv = propertyInfo.GetValue(original, null);
//propertyInfo.SetValue(Destination, pv, null);
}
//Just put the entity here
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityLookupAttribute)))
{
var pv = propertyInfo.GetValue(original, null);
propertyInfo.SetValue(dest, pv, null);
}
//Just put the entity here
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityInterfaceLookupAttribute)))
{
var pv = propertyInfo.GetValue(original, null);
propertyInfo.SetValue(dest, pv, null);
}
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildAttribute)))
{
dynamic source = propertyInfo.GetValue(original, null);
var target = propertyInfo.GetValue(dest, null);
if (source == null) return;
if (target == null)
{
var t = source.GetType();
target = Activator.CreateInstance(t);
}
source.CopyMeToProvidedEntity(target);
propertyInfo.SetValue(dest, target, null);
}
if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildCollectionAttribute)))
{
var source = propertyInfo.GetValue(original, null) as IList;
var target = propertyInfo.GetValue(dest, null) as IList;
foreach (dynamic sourceEntity in source)
{
var found = false;
object targetEntity = null;
foreach (dynamic tEntity in target)
{
if (sourceEntity.IdentityGuid != tEntity.IdentityGuid) continue;
found = true;
targetEntity = tEntity;
break;
}
if (!found)
{
var b = propertyInfo.PropertyType.GetGenericArguments()[0];
targetEntity = Activator.CreateInstance(b);
}
sourceEntity.CopyMeToProvidedEntity(targetEntity);
if (!found)
{
target.Add(targetEntity);
}
}
}
}
}
((IsCloneable)original).IsCloning = false;
((IsCloneable)dest).IsCloning = false;
}
2017年6月27日 - 我找到了问题的原因
我在我的实体的基类上有一个属性,以帮助将克隆实体与其发起者配对;
private string _identityGuid = Guid.NewGuid().ToString();
[Browsable(false)]
[NotMapped]
public virtual string IdentityGuid
{
get { return _identityGuid; }
set { CheckPropertyChanged(ref _identityGuid, value); }
}
如果复制此属性,我会在问题标题中收到ID错误... 我不知道为什么会这样。 我把它改名为“彼得”只是因为它是一些EF自动的东西。
要修复我有条件地排除任何名为“IdentityGuid”的属性,并且复制例程现在可以正常工作,并且副本可以保存到数据库中。
如果有人能解释这个属性的问题,我将非常感激:)
答案 0 :(得分:1)
我不经常看到需要为此任务使用反射,因为您不太可能只想克隆任何实体。通常您想要克隆的内容是已知的,并且有一个业务用例。如果您以后确定是这种情况,您可以执行以下操作。
DbContext
实例尚未关闭,请关闭延迟加载。Include
语句,以获取您想要包含在克隆中的所有合成。AsNoTracking
Add
DbContext
过度简化的示例代码:
var root = dbContext.TypeA
.AsNoTracking()
.Where(x => someCondition)
.Include(x => composititions)
.SingleOrDefault();
root.Key = 0; // reset key
root.Comps.ForEach(comp => comp.Key = 0); // reset composition keys
dbContext.TypeA.Add(root);
dbContext.SaveChanges();