为什么我的EF分离不够?

我尝试分离类型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(); } public string AppName { get; set; } public System.Guid AppGuid { get; set; } public string ClientAppID { get; set; } public bool IsDeleted { get; set; } public Nullable CreatedDate { get; set; } public Nullable UpdatedDate { get; set; } public virtual AppsData AppsData { get; set; } public virtual ICollection 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(); } public int BrowserID { get; set; } public string BrowserName { get; set; } public string BrowserCode { get; set; } public virtual ICollection BrowserVersions { get; set; } } public partial class BrowserVersion { public BrowserVersion() { this.BrowserVerToCriterias = new HashSet(); } 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 Group_Id { get; set; } public virtual Browser Browser { get; set; } public virtual ICollection 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(); this.Criteria1 = new HashSet(); } public int CountryID { get; set; } public string CountryCode { get; set; } public string CountryName { get; set; } public virtual ICollection Criteria { get; set; } public virtual ICollection Criteria1 { get; set; } } public Criterion() { this.BrowserVerToCriterias = new HashSet(); this.Countries = new HashSet(); this.CountriesExceptions = new HashSet(); this.Pairs = new HashSet(); } 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 BrowserVerToCriterias { get; set; } public virtual ICollection Countries { get; set; } public virtual ICollection CountriesExceptions { get; set; } public virtual ICollection 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(); } 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 Pairs { get; set; } } public partial class MamConfiguration { public MamConfiguration() { this.Groups = new HashSet(); this.MamConfigurationCTIDs = new HashSet(); } 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 Groups { get; set; } public virtual MamConfigurationStatus MamConfigurationStatus { get; set; } public virtual ICollection 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(); } public int StatusId { get; set; } public string Status { get; set; } public virtual ICollection MamConfigurations { get; set; } } public partial class Pair { public Pair() { this.Groups = new HashSet(); } 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 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; } } 

这是我的分离function:

 public void Detach(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; } 

我相信你需要确保在你的上下文中没有任何实体引用任何已经分离的实体。 因此,如果说其他东西引用了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也将被删除。

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

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

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