3

これが私のシナリオです。OrderItemsなどを含むOrdersを表すさまざまなナビゲーションプロパティ(1-1、1-many、many-manyなど)を備えたCustomersテーブルがあります(非常に深いオブジェクトグラフを読んでください)

非常に簡単な例: Customer1にはOrders [1..5]があり、Customer2にはOrders[6..9]があります

ここで、2つの顧客が実際には同じものであることがわかり、2つをマージしたいときが来ました。Customer1.Merge(Customer2);

マージ後、Customer1にはCustomer2の他のすべてのプロパティとともにOrders [1..9]があり、Customer2は空であり、安全に削除できます。

ディープクローニングに関するかなり古い投稿( http://www.urmanet.ch/?p=11)に出くわしました。これにより、新しいエンティティが作成されますが、既存のエンティティが2つあり、これらを次のように減らす必要があります。2番目のエンティティのオブジェクトグラフを追加した1つの既存のエンティティで、2番目のエンティティを削除して作業単位をコミットできます。 この例は2008年に始まり、EFの新しいバージョンが利用可能になったので、これを達成するための正しい方法は何でしょうか。

このタスクを一般的に(リフレクションを意味する)実行する拡張メソッドのアイデアが好きです。これにより、エンティティモデルが変更された場合(新しいプロパティ/関係が追加された場合など)、この一般的なMergeメソッドを変更する必要がなくなります...

これが私が始めている基本コードです。新しいエンティティ(クローン)を作成するのではなく、2つのエンティティをマージできるように変更したいと思います。

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

要約すると、このクローン拡張メソッドをマージ拡張メソッドに変換するのに助けが必要です。専門家が介入して全体像を完成させるのを手伝ってくれることを願っています。どんな助けでも大歓迎です!

4

0 に答える 0