为什么我的EF分离不够?

时间:2013-02-10 16:57:19

标签: c# entity-framework caching

我尝试分离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;
}

1 个答案:

答案 0 :(得分: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方法之后,系统将不再保留指向此对象的引用,并且它可以由垃圾收集器“,这意味着所有LinkDescriptors也将被删除。

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

这是最好的方法吗?分离和重新连接很难做到。如果你需要分离并重新附加,我会建议你将深层分离计划移到类本身而不是通用方法中。

那就是说,你写了“在下一个请求我从缓存中获取组...”,这让我想知道两个请求之间最长的时间是什么?你能通过缓存引入并发问题吗?您是否在IIS中托管WCF服务?如果并发不是问题,你可以使用IIS的缓存吗?您是否在同一个线程上处理所有请求?您可能不知道ObjectContext实例方法不是线程安全的。