代码优先 EF 实体的深度克隆

Deep Clone of code first EF Entity

我正在为代码优先 Entity Framework 实体尝试通用深度克隆例程。

我已经针对标准系统 属性 类型破解了它,但在使用代理实体(使用虚拟定义)时遇到了问题,即

[EntityLookup]
public virtual Person { get; set; }

[EntityLookup] 是我自己的属性之一,有助于进一步定义关联。

如果我删除 "virtual" 关键字,我的例程可以更新目标实体 属性 没问题(但我失去了额外的 EF 功能) 使用虚拟我得到以下错误;

System.Reflection.TargetException: 'Object does not match target type.'

我认为这与 EF 的代理有关 class 但我不确定如何转换原始实体以便将其设置在目标位置。 以下是针对此问题的 Clone 例程的要点;

public static void CloneProperties<T>(T Original, T Destination)    
{
    PropertyInfo[] props = Original.GetType().GetProperties();
    foreach (var propertyInfo in props)
    {
        if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)....
        else
        {
                if (Destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
                {
                    var pv = propertyInfo.GetValue(Original, null);
                    propertyInfo.SetValue(Destination, pv, null);
                }
         }
    }
}

当实体被声明为虚拟时,是 "propertyInfo.SetValue(Destination, pv, null);" 产生了错误。

我们将不胜感激地接受任何关于让它工作的帮助

此致

兰斯

此外,以类似的方式,我现在正尝试在我的实体中克隆子集合。

我正在遍历源 属性 集合并需要将缺失的记录添加到目标属性集合

a.Add(targetEntity);行给出以下错误;

"The best overloaded method match for 'System.Collections.ObjectModel.Collection<FmCosting.Entities.CsJobDetail>.Add(FmCosting.Entities.CsJobDetail)' has some invalid arguments"

相关代码为;

                if (dest.PropertyHasCustomAttribute(propertyInfo.Name, typeof(EntityChildCollectionAttribute)))
                {
                    var source = propertyInfo.GetValue(original, null) as ICollection;
                    var target = propertyInfo.GetValue(dest, null) as ICollection;
                    foreach (dynamic sourceEntity in source)
                    {
                        var found = false;
                        object targetEntity = null;

                        foreach (dynamic tEntity in target)
                        {
                            if (sourceEntity.IdentityGuid == tEntity.IdentityGuid)
                            {
                                found = true;
                                targetEntity = tEntity;
                                continue;
                            }

                        }

                        if (!found)
                        {
                            var t = sourceEntity.GetType();
                            targetEntity = Activator.CreateInstance(t);
                        }


                        sourceEntity.CloneMeToProvidedEntity(targetEntity);

                        if (!found)
                        {
                            dynamic a = target;
                            a.Add(targetEntity);
                        }


                    }
                    //propertyInfo.SetValue(Destination, pv, null);
                }

任何进一步的帮助将不胜感激

此致

兰斯

您的 destination 对象的具体类型可能与 T 不同,因此您必须使用 destinationPropertyInfo 而不是 original:

public static void CloneProperties<T>(T original, T destination)    
{
    var originalType = original.GetType();
    var destinationType = destination.GetType();

    PropertyInfo[] props = originalType.GetProperties();
    foreach (var propertyInfo in props)
    {
        if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
        {
            // ....
        }
        else
        {
            if (destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
            {
                var pv = propertyInfo.GetValue(original, null);
                var destinationProperty = destinationType.GetProperty(propertyInfo.Name);
                destinationProperty.SetValue(destination, pv, null);
            }
         }
    }
}

注:

另一种选择是恢复为编译时类型,因此两个对象都使用 T 的属性,避免可能从 GetType():

返回的派生类型
public static void CloneProperties<T>(T original, T destination)    
{
    PropertyInfo[] props = typeof(T).GetProperties();
    foreach (var propertyInfo in props)
    {
        if (propertyInfo.PropertyType.Namespace == "System" || propertyInfo.PropertyType.IsEnum)
        {
            // ....
        }
        else
        {
            if (destination.PropertyHasCustomAttribute (propertyInfo.Name, typeof(EntityLookupAttribute)))
            {
                var pv = propertyInfo.GetValue(original, null);
                propertyInfo.SetValue(destination, pv, null);
            }
         }
    }
}

我使用 IList 得到了集合克隆,这里是相关代码,如果它对其他人有帮助的话。

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.CloneMeToProvidedEntity(targetEntity);

        if (!found)
        {
            target.Add(targetEntity);
        }
    }
}