Why is my EF disconnect not enough?

I am trying to detach an object of type group

.

I actually save it in my cache and disable it a moment before the client responds.

In the next request, I get group

from the cache and re-clean the new objectcontext.

However I am getting An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I know that attach includes all related entities, but detaches. There I have to disable all related objects.

What am I missing in my disconnect?

here's my entities 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; }
}

      

here's my detach function:

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

      

+3


source to share


1 answer


I believe you need to make sure that none of the entities that remain in your context are referencing any of the ones that were detached. So let's say something else is referencing a single instance of a pair, the context will quite happily find it, move its nav properties, and add the whole part back in.

Instead of setting the State property, you've tried:

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

      

Any references to an element being detached in addition to the element itself are intended to be detached.

EDIT

Okay, let's take a look at "detach any references to detachable item ..." ObjectContext.Detach eventually calls this method:



// 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 destroys all references. The documentation for ObjectContext.Detach is not referring to breaking links http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.detach.aspx , it says, "After calling Detach method, the system will no longer contain links pointing to this object and can be garbage collected ", which means that all LinkDescriptors will be removed as well.

Regarding your 3rd comment "Do you think the IObjectContextAdapter will allow you to completely detach. Or will there always be another object in context that I will get bored and not detach?" there are two things here; there is an object property and a LinkDescriptor that the context uses to keep track of the relationship. Disconnecting simply stops tracking the object relationship by detaching the LinkDescriptors, it does not separate the object from the other end of the relationship. Also it doesn't set such properties to null, if you check the object after deleting it, these properties will still be set.

Is this the best approach? Disconnecting and reconnecting is difficult to obtain. If you need to detach and reconnect, I would suggest that you move your deep rountines units to the classes themselves rather than the general method.

However, you wrote "On the next request, I get a group from the cache ..." which leads me to wonder what the longest time interval would be between two requests? Could you introduce concurrency issues by caching? Are you hosting your WCF service in IIS? Could you use IIS caching if concurrency wouldn't be an issue? Are you processing all requests in one thread? You may not be aware that the methods of the ObjectContext instance are not thread safe.

+1


source







All Articles