3

我尝试分离类型的实体group

实际上,我将它保存在我的缓存中,并在响应客户端之前将其分离。

在下一个请求中,我group从缓存中获取并重新附加一个新的 objectContext。

但是我得到An entity object cannot be referenced by multiple instances of IEntityChangeTracker

我知道附加包括所有相关实体,但分离不包括。在那里我必须分离每个相关实体。

我的分离中缺少什么?

这是我的实体层次结构:

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 也将被删除。

关于您的第三条评论“您认为 IObjectContextAdapter 会启用完全分离吗?或者在上下文中总会有其他对象我会错过而不分离?” 这里有两件事;有对象的属性和上下文用来跟踪关系的 LinkDescriptor。分离只是通过分离 LinkDescriptor 来停止跟踪对象的关系,它不会分离关系另一端的对象。它也不会将此类属性设置为 null,如果您在分离后检查对象,它仍将设置这些属性。

这是最好的方法吗?分离和重新连接很难正确。如果您需要分离和重新连接,我建议您将深度分离例程移到类本身而不是通用方法中。

That said, you wrote "On the next request I get the group from the cache..." which leads to me wonder what would be the longest period of time between two requests? Could you be introducing concurrency issues by caching? Are you hosting your WCF service in IIS? Could you use IIS's caching if concurrency will not be a problem? Are you handling all requests on the same thread? You might not be aware that ObjectContext instance methods are not thread safe.

于 2013-02-11T14:54:18.313 回答