【问题标题】:Deep Clone of code first EF Entity深度克隆代码优先 EF 实体
【发布时间】:2024-01-04 05:27:01
【问题描述】:

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

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

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

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

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

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

我相信这与 EF 的 Proxy 类有关,但我不确定如何转换原始实体,以便将其设置在目标上。 以下是此问题的克隆例程的要点;

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);
                }

如有任何进一步的帮助,我们将不胜感激

最好的问候

兰斯

【问题讨论】:

    标签: c# entity-framework reflection entity-framework-6 clone


    【解决方案1】:

    您的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);
                }
             }
        }
    }
    

    【讨论】:

    • 谢谢 Federico。这对我帮助很大
    • 您好 Federico,我添加了另一个问题,该问题与第一部分有关操作反射子集合的范围相同。我希望答案对你来说和上一个问题一样简单:)
    • 很高兴为您提供帮助。下次,请创建另一个问题,编辑已回答的帖子添加其他问题不是最佳做法。创建一个描述问题的新文件通常更有用。
    【解决方案2】:

    我使用 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);
            }
        }
    }
    

    【讨论】: