3

这是我的场景。我们有一个 Customers 表,具有各种导航属性(1-1、1-many、many-many 等)来表示 Orders,其中包含 OrderItems 等。(阅读非常深入的对象图)

非常简单的例子: Customer1 有 Orders[1..5] 而 Customer2 有 Orders[6..9]

现在有一段时间我们发现这两个客户实际上是同一个客户,我们想合并这两个客户:Customer1.Merge(Customer2);

合并后,我们应该看到Customer1 具有 Orders[1..9] 以及 Customer2 的所有其他属性,并且 Customer2 为空并且可以安全地删除。

现在我遇到了一篇关于深度克隆的相当老的帖子(http://www.urmanet.ch/?p=11),它产生了一个新实体,但我有两个现有实体需要减少到一个现有实体添加了第二个实体的对象图,然后我可以在其中删除第二个实体并提交工作单元。 这个例子起源于 2008 年,并且现在有新版本的 EF,我想知道实现这一目标的正确方法是什么?

我喜欢一个扩展方法的想法,它可以通用地执行此任务(意味着反射),这样当/如果实体模型发生变化(添加新属性/关系等),这个通用的 Merge 方法就不需要改变......

这是我开始的基本代码,我想对其进行修改,以便它可以合并两个实体,而不是创建一个新实体(克隆)。

/// This class is used to store self references for
/// back tracking
public class SelfReferencesTracking
{
    public string EntitySetName;
    public EntityObject NewEntityObject;
    public EntityKey OriginalKeys;
}

/// Extension method class for the EntityObject class
public static class EntityObjectExtension
{
    //Enable tracking
    private static readonly List<SelfReferencesTracking> Tracking =
        new List<SelfReferencesTracking>();

    /// These method makes a 1:1 copy of the original entity object
    /// 
    /// The original entity object /// The copied entity object
    public static EntityObject Clone(this EntityObject entityObject)
    {
        //Get constructor for new object
        object newEntityObject = entityObject.GetType().GetConstructor(new Type[0]).Invoke(new object[0]);

        Tracking.Add(new SelfReferencesTracking
            {
                EntitySetName = entityObject.EntityKey.EntitySetName,
                OriginalKeys = entityObject.EntityKey,
                NewEntityObject = (EntityObject) newEntityObject
            });

        //Copy all properties and its values of the given type
        PropertyInfo[] properties = entityObject.GetType().GetProperties();
        foreach (PropertyInfo property in properties)
        {
            try
            {
                object propertyValue = property.GetValue(entityObject, null);
                PropertyInfo myProperty = property;
                if (!entityObject.EntityKey.EntityKeyValues.Any(x => x.Key == myProperty.Name))
                {
                    //Ignore all properties of these types
                    if (property.PropertyType != typeof (EntityKey) &&
                        property.PropertyType != typeof (EntityState) &&
                        property.PropertyType != typeof (EntityReference<>))
                    {
                        //Check, if the property is a complex type (collection), in that
                        //case, some special calls are necessary
                        if (
                            property.GetCustomAttributes(typeof (EdmRelationshipNavigationPropertyAttribute), false)
                                    .Count() == 1)
                        {
                            //Check for self referencing entities
                            if (propertyValue.GetType() == entityObject.GetType())
                            {
                                //Get the self referenced entity object
                                var selfRefrencedEntityObject = (EntityObject) property.GetValue(entityObject, null);

                                //This variable is used to store the new parent entity objects
                                EntityObject newParentEntityObject = null;

                                //This loops might be replaced by LINQ queries... I didn't try that
                                foreach (
                                    SelfReferencesTracking tracking in
                                        Tracking.Where(
                                            x =>
                                            x.EntitySetName == selfRefrencedEntityObject.EntityKey.EntitySetName))
                                {
                                    //Check, if the key is in the tracking list
                                    foreach (
                                        EntityKeyMember newKeyValues in
                                            selfRefrencedEntityObject.EntityKey.EntityKeyValues)
                                    {
                                        //Iterate trough the keys and values
                                        foreach (
                                            EntityKeyMember orgKeyValues in tracking.OriginalKeys.EntityKeyValues)
                                        {
                                            //The key is stored in the tracking list, which means, this is
                                            //the foreign key used by the self referencing property
                                            if (newParentEntityObject == null)
                                            {
                                                if (orgKeyValues.Key == newKeyValues.Key &&
                                                    orgKeyValues.Value == newKeyValues.Value)
                                                {
                                                    //Store the parent entity object
                                                    newParentEntityObject = tracking.NewEntityObject;
                                                }
                                            }
                                            else
                                            {
                                                break;
                                            }
                                        }
                                    }
                                }

                                //Set the value to the new parent entity object
                                property.SetValue(newEntityObject, newParentEntityObject, null);
                            }
                            else
                            {
                                //Entity collections are always generic
                                if (propertyValue.GetType().IsGenericType)
                                {
                                    //Don't include self references collection, e.g. Orders1, Orders2 etc.
                                    //Check for equality of the types (string comparison)
                                    if (
                                        !propertyValue.GetType()
                                                      .GetGenericArguments()
                                                      .First()
                                                      .FullName.Equals(entityObject.GetType().FullName))
                                    {
                                        //Get the entities of the given property
                                        var entities = (RelatedEnd) property.GetValue(entityObject, null);

                                        //Load underlying collection, if not yet done...
                                        if (!entities.IsLoaded) entities.Load();

                                        //Create a generic instance of the entities collection object
                                        Type t =
                                            typeof (EntityCollection<>).MakeGenericType(new[]
                                                {property.PropertyType.GetGenericArguments()[0]});

                                        object newEntityCollection = Activator.CreateInstance(t);

                                        //Iterate trough the entities collection
                                        foreach (object entity in entities)
                                        {
                                            //Add the found entity to the dynamic generic collection
                                            MethodInfo addToCollection =
                                                newEntityCollection.GetType().GetMethod("Add");
                                            addToCollection.Invoke(newEntityCollection,
                                                                   new object[] {Clone((EntityObject) entity)});
                                        }

                                        //Set the property value
                                        property.SetValue(newEntityObject, newEntityCollection, null);
                                    }
                                }
                            }
                        }
                        else
                        {
                            var baseType = propertyValue.GetType().BaseType;
                            if (baseType != null && baseType.Name == "EntityReference")
                            {
                                //Simply copy the EntityKey to the new entity object’s EntityReference
                                ((EntityReference) property.GetValue(newEntityObject, null)).EntityKey =
                                    ((EntityReference) property.GetValue(entityObject, null)).EntityKey;
                            }
                            else
                            {
                                //Common task, just copy the simple type property into the new entity object
                                property.SetValue(
                                    newEntityObject,
                                    property.GetValue(entityObject, null), null);
                            }
                        }
                    }
                }
            }
            catch (InvalidCastException ie)
            {
                //Hmm, something happend...
                Debug.WriteLine(ie.ToString());

                continue;
            }
            catch (Exception ex)
            {
                //Hmm, something happend...
                Debug.WriteLine(ex.ToString());

                continue;
            }
        }
        return (EntityObject) newEntityObject;
    }
}

回顾一下:在将这个 Clone 扩展方法转换为 Merge 扩展方法时,我需要一些帮助,我希望专家可以介入并帮助完成图片。任何帮助是极大的赞赏!

4

0 回答 0