3

タイプのエンティティをデタッチしようとしましたgroup

実際には、キャッシュに保存し、クライアントに応答する少し前にデタッチします。

次のリクエストでgroup、キャッシュからを取得し、新しいobjectContextを再アタッチします。

しかし、私は得るAn entity object cannot be referenced by multiple instances of IEntityChangeTracker

アタッチには関連するすべてのエンティティが含まれますが、デタッチには含まれません。そこで、関連するすべてのエンティティを切り離す必要があります。

デタッチに何が欠けていますか?

これが私の実体のhirarchyです:

public partial class App
{
    public App()
    {
        this.Pairs = new HashSet<Pair>();
    }

    public string AppName { get; set; }
    public System.Guid AppGuid { get; set; }
    public string ClientAppID { get; set; }
    public bool IsDeleted { get; set; }
    public Nullable<System.DateTime> CreatedDate { get; set; }
    public Nullable<System.DateTime> UpdatedDate { get; set; }

    public virtual AppsData AppsData { get; set; }
    public virtual ICollection<Pair> Pairs { get; set; }
}


public partial class AppsData
{
    public System.Guid AppGuid { get; set; }
    public string Url { get; set; }
    public string DisplayName { get; set; }
    public string AppDesc { get; set; }
    public string PrivacyPolicyUrl { get; set; }
    public string TermsOfUseUrl { get; set; }
    public string LocalizationKey { get; set; }
    public string Compatibility { get; set; }
    public bool HiddenApp { get; set; }
    public bool IsExperimental { get; set; }

    public virtual App App { get; set; }
}

public partial class Browser
{
    public Browser()
    {
        this.BrowserVersions = new HashSet<BrowserVersion>();
    }

    public int BrowserID { get; set; }
    public string BrowserName { get; set; }
    public string BrowserCode { get; set; }

    public virtual ICollection<BrowserVersion> BrowserVersions { get; set; }
}

public partial class BrowserVersion
{
    public BrowserVersion()
    {
        this.BrowserVerToCriterias = new HashSet<BrowserVerToCriteria>();
    }

    public System.Guid BrowserVersionID { get; set; }
    public int BrowserID { get; set; }
    public string Version { get; set; }
    public System.DateTime CreatedDate { get; set; }
    public System.DateTime UpdatedDate { get; set; }
    public Nullable<int> Group_Id { get; set; }

    public virtual Browser Browser { get; set; }
    public virtual ICollection<BrowserVerToCriteria> BrowserVerToCriterias { get; set; }
}

public partial class BrowserVerToCriteria
{
    public System.Guid CriteriaID { get; set; }
    public System.Guid BrowserVersionID { get; set; }
    public string ConditionBrowserVersion { get; set; }

    public virtual BrowserVersion BrowserVersion { get; set; }
    public virtual Criterion Criterion { get; set; }
}

public partial class CommonConfig
{
    public int ID { get; set; }
    public string NAME { get; set; }
    public string VALUE { get; set; }
    public System.DateTime CREATED_DATE { get; set; }
    public System.DateTime UPDATED_DATE { get; set; }
    public byte GROUP_ID { get; set; }
    public string DESCRIPTION { get; set; }
}

public partial class Country
{
    public Country()
    {
        this.Criteria = new HashSet<Criterion>();
        this.Criteria1 = new HashSet<Criterion>();
    }

    public int CountryID { get; set; }
    public string CountryCode { get; set; }
    public string CountryName { get; set; }

    public virtual ICollection<Criterion> Criteria { get; set; }
    public virtual ICollection<Criterion> Criteria1 { get; set; }
}

    public Criterion()
    {
        this.BrowserVerToCriterias = new HashSet<BrowserVerToCriteria>();
        this.Countries = new HashSet<Country>();
        this.CountriesExceptions = new HashSet<Country>();
        this.Pairs = new HashSet<Pair>();
    }

    public System.Guid CriteriaID { get; set; }
    public string Domains { get; set; }
    public System.DateTime CreatedDate { get; set; }
    public System.DateTime UpdatedDate { get; set; }
    public string DomainsExclude { get; set; }

    public virtual ICollection<BrowserVerToCriteria> BrowserVerToCriterias { get; set; }
    public virtual ICollection<Country> Countries { get; set; }
    public virtual ICollection<Country> CountriesExceptions { get; set; }
    public virtual ICollection<Pair> Pairs { get; set; }
}

public partial class CTID
{
    public string CTID1 { get; set; }
    public string AppVersion { get; set; }
}

public partial class CtidPgPastExistence
{
    public string Ctid { get; set; }
}

public partial class Group
{
    public Group()
    {
        this.Pairs = new HashSet<Pair>();
    }

    public System.Guid GroupId { get; set; }
    public int TestId { get; set; }
    public int IdInTest { get; set; }
    public bool WelcomeExperienceEnabledByDefault { get; set; }

    public virtual MamConfiguration MamConfiguration { get; set; }
    public virtual ICollection<Pair> Pairs { get; set; }
}

public partial class MamConfiguration
{
    public MamConfiguration()
    {
        this.Groups = new HashSet<Group>();
        this.MamConfigurationCTIDs = new HashSet<MamConfigurationCTID>();
    }

    public int TestID { get; set; }
    public string TestName { get; set; }
    public string Description { get; set; }
    public int StatusId { get; set; }
    public System.DateTime CreatedDate { get; set; }
    public System.DateTime UpdatedDate { get; set; }
    public bool IsProd { get; set; }
    public int TestTraffic { get; set; }

    public virtual ICollection<Group> Groups { get; set; }
    public virtual MamConfigurationStatus MamConfigurationStatus { get; set; }
    public virtual ICollection<MamConfigurationCTID> MamConfigurationCTIDs { get; set; }
}

public partial class MamConfigurationCTID
{
    public int TestID { get; set; }
    public string CTID { get; set; }

    public virtual MamConfiguration MamConfiguration { get; set; }
}

public partial class MamConfigurationStatus
{
    public MamConfigurationStatus()
    {
        this.MamConfigurations = new HashSet<MamConfiguration>();
    }

    public int StatusId { get; set; }
    public string Status { get; set; }

    public virtual ICollection<MamConfiguration> MamConfigurations { get; set; }
}

public partial class Pair
{
    public Pair()
    {
        this.Groups = new HashSet<Group>();
    }

    public System.Guid PairID { get; set; }
    public System.Guid CriteriaID { get; set; }
    public System.Guid AppGuid { get; set; }

    public virtual App App { get; set; }
    public virtual Criterion Criterion { get; set; }
    public virtual ICollection<Group> Groups { get; set; }
}

public partial class SettingsServicesConfig
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string URL { get; set; }
    public int Interval { get; set; }
    public System.DateTime UPDATED_DATE { get; set; }
    public System.DateTime CREATED_DATE { get; set; }
    public int GROUP_ID { get; set; }
}

これが私のデタッチ機能です:

public void Detach<T>(MaMDBEntities maMdbEntities, T item) where T : class, new()
{
    switch (typeof (T).Name.ToLower())
    {
        case "group":
            {
                var group = item as Group;

                if (group == null)
                {
                    mApplicationLogger.Error(string.Format("Couldn't cast item to type 'Group'"));

                    throw new InvalidCastException(string.Format("Couldn't cast item to type 'Group'"));
                }

                DetachState(maMdbEntities, group.MamConfiguration);

                foreach (var pair in group.Pairs.ToList())
                {
                    DetachState(maMdbEntities, pair.App);

                    DetachState(maMdbEntities, pair.App.AppsData);

                    foreach (var country in pair.Criterion.Countries.ToList())
                    {
                        DetachState(maMdbEntities, country);
                    }

                    foreach (var country in pair.Criterion.CountriesExceptions.ToList())
                    {
                        DetachState(maMdbEntities, country);
                    }


                    foreach (var browserVerToCriterias in pair.Criterion.BrowserVerToCriterias.ToList())
                    {
                        DetachState(maMdbEntities, browserVerToCriterias.BrowserVersion.Browser);

                        DetachState(maMdbEntities, browserVerToCriterias.BrowserVersion);

                        DetachState(maMdbEntities, browserVerToCriterias);
                    }

                                                DetachState(maMdbEntities, pair.Criterion);

                    DetachState(maMdbEntities, pair);

                }

                break;
            }
    }
    maMdbEntities.Entry(item).State = EntityState.Detached;
}

private static void DetachState(MaMDBEntities maMdbEntities, object item)
{
    maMdbEntities.Entry(item).State = EntityState.Detached;
}
4

1 に答える 1

1

コンテキストに残っているエンティティが、分離されたエンティティを参照していないことを確認する必要があると思います。したがって、別の何かが Pair の切り離されたインスタンスを参照している場合、コンテキストはそれを非常に喜んで見つけ、そのナビゲーション プロパティをトラバースして、すべてを元に戻します。

State プロパティを設定するのではなく、次のことを試しましたか。

((IObjectContextAdapter)maMdbEntities).ObjectContext.Detach(item);

これは、アイテム自体に加えて、切り離されるアイテムへのリンクを切り離すことになっています。

編集

わかりました、「切り離されているアイテムへのリンクを切り離す...」を見てみましょう。ObjectContext.Detach は最終的にこのメソッドを呼び出します。

// System.Data.Objects.EntityEntry
internal void Detach()
    {
    base.ValidateState();
    bool flag = false;
    RelationshipManager relationshipManager = this._wrappedEntity.RelationshipManager;
    flag = (base.State != EntityState.Added && this.IsOneEndOfSomeRelationship());
    this._cache.TransactionManager.BeginDetaching();
    try
        {
        relationshipManager.DetachEntityFromRelationships(base.State);
        }
    finally
        {
        this._cache.TransactionManager.EndDetaching();
        }
    this.DetachRelationshipsEntries(relationshipManager);
    IEntityWrapper wrappedEntity = this._wrappedEntity;
    EntityKey entityKey = this._entityKey;
    EntityState state = base.State;
    if (flag)
        {
        this.DegradeEntry();
        }
    else
        {
        this._wrappedEntity.ObjectStateEntry = null;
        this._cache.ChangeState(this, base.State, EntityState.Detached);
        }
    if (state != EntityState.Added)
        {
        wrappedEntity.EntityKey = entityKey;
        }
    }

すべてのリンクを分解する DetachEntityFromRelationships。ObjectContext.Detach のドキュメントは、リンクの破棄について具体的ではありませんhttp://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.detach.aspx「Detach メソッドの後が呼び出されると、システムはこのオブジェクトを指す参照を保持しなくなり、ガベージ コレクターによって収集されます。これは、すべての LinkDescriptor も削除されることを意味します。

あなたの3番目のコメントに関して、「IObjectContextAdapterは完全なデタッチを可能にすると思いますか。それとも、見逃してデタッチしないコンテキスト内の他のオブジェクトが常に存在しますか?」ここには 2 つのことがあります。オブジェクトのプロパティと、コンテキストが関係を追跡するために使用する LinkDescriptor があります。デタッチは、LinkDescriptor をデタッチすることによってオブジェクトの関係の追跡を停止するだけであり、関係の反対側にあるオブジェクトをデタッチしません。デタッチ後にオブジェクトを調べると、これらのプロパティが設定されたままになります。

これは最善のアプローチですか?取り外しと再取り付けは、正しく行うのが困難です。デタッチして再アタッチする必要がある場合は、深いデタッチ ルーチンをジェネリック メソッドではなく、クラス自体に移動することをお勧めします。

そうは言っても、「次のリクエストでキャッシュからグループを取得します...」と書いたので、2つのリクエスト間の最長期間はどれくらいになるのでしょうか? キャッシュによって同時実行の問題が発生する可能性はありますか? IIS で WCF サービスをホストしていますか? 同時実行性が問題にならない場合は、IIS のキャッシュを使用できますか? すべてのリクエストを同じスレッドで処理していますか? ObjectContext インスタンス メソッドがスレッド セーフではないことに気付いていないかもしれません。

于 2013-02-11T14:54:18.313 に答える