locked
Confused - caching and detached entities RRS feed

  • Question

  • I'm trying to create some framework implementing caching of entity objects created by Entity Framework beta 3, but i'm confused how Entity Framework is working.

    The scenario is:

    1. I load entity from datasource
    2. I store this object (or clone) in cache (HttpContext.Current.Cache for asp.net applications)
    3. Detach this object from object context (object context scoope is httpcontext in asp.net application)
    Problems:
    - Navigation properties are now empty (due to deatach) even if loaded before entity is stored in cache
    - If navigation property is not loaded before stored in cache, entity framework does not attach this object to context (I can make this if I modify automatic generated entity classes, but modifyng this classes is not funny due to visual studio is deleting my modifications if .edmx file are modified and saved ;-(

    I suppose you should deatach enteties if you put them in for example HttpContext.Current.Cache

    By the way is there a way to load referenced entities un-lazy if using GetObjectByKey? I know you can use Include when using ObjectQuerty<T> to grab enteties from datasource.

    /A
    Sunday, April 13, 2008 6:26 PM

Answers

  • Hello,

    If you detach your entities like this, you will keep navigation properties:

    Code Snippet

    var trackedEntity = myEntity as IEntityWithChangeTracker;

    trackedEntity.SetChangeTracker(null);

     

     

     

    Monday, April 14, 2008 12:43 AM
  • A,

     

    >> Navigation properties are now empty (due to deatach) even if loaded before entity is stored in cache

    In the current release of the Entity Framework, this is the design of Detach. The solution that Matthieu suggests should help you work around this for now.


    >> If navigation property is not loaded before stored in cache, entity framework does not attach this object to context

    I am not sure what you mean here. If you are caching a "Customer" and would also like to cache all of the Order entities in the Customer.Orders collection, you would have to:

    1. Query for the Customer and Load() the Orders prior to destroying the context

    2. Using the workaround that Matthieu suggests to clear the change tracker for each entity in your graph

    3. Cache the Customer

    You can then reattach the Customer to a new ObjectContext at a later time. However, if you want to recache the Customer, you must reclear the change tracker again.


    >> By the way is there a way to load referenced entities un-lazy if using GetObjectByKey? I know you can use Include when using ObjectQuerty<T> to grab enteties from datasource.

    Currently this is not supported by the Entity Framework. You could write your own extension method for Refresh and GetObjectByKey where you can specify an Include statement on your ObjectQuery.

     

    Jeff

     

    Monday, April 14, 2008 4:50 PM

All replies

  • Hello,

    If you detach your entities like this, you will keep navigation properties:

    Code Snippet

    var trackedEntity = myEntity as IEntityWithChangeTracker;

    trackedEntity.SetChangeTracker(null);

     

     

     

    Monday, April 14, 2008 12:43 AM
  • A,

     

    >> Navigation properties are now empty (due to deatach) even if loaded before entity is stored in cache

    In the current release of the Entity Framework, this is the design of Detach. The solution that Matthieu suggests should help you work around this for now.


    >> If navigation property is not loaded before stored in cache, entity framework does not attach this object to context

    I am not sure what you mean here. If you are caching a "Customer" and would also like to cache all of the Order entities in the Customer.Orders collection, you would have to:

    1. Query for the Customer and Load() the Orders prior to destroying the context

    2. Using the workaround that Matthieu suggests to clear the change tracker for each entity in your graph

    3. Cache the Customer

    You can then reattach the Customer to a new ObjectContext at a later time. However, if you want to recache the Customer, you must reclear the change tracker again.


    >> By the way is there a way to load referenced entities un-lazy if using GetObjectByKey? I know you can use Include when using ObjectQuerty<T> to grab enteties from datasource.

    Currently this is not supported by the Entity Framework. You could write your own extension method for Refresh and GetObjectByKey where you can specify an Include statement on your ObjectQuery.

     

    Jeff

     

    Monday, April 14, 2008 4:50 PM
  •  MatthieuMEZIL wrote:

    Hello,

    If you detach your entities like this, you will keep navigation properties:

    Code Snippet

    var trackedEntity = myEntity as IEntityWithChangeTracker;

    trackedEntity.SetChangeTracker(null);

     

    If I detach a group of entities with this approach, aren't there any side effects? For example the RelationshipManager still holds a reference to the original context. Could this cause conflicts, like calling a Load() on an EntityCollection trying to use the disposed context? Are there any other similar objects tied to the original context, and how should I detach them?
    Tuesday, April 15, 2008 1:26 PM
  • Hi,

    I am using the following to keep the course property, Once I detach it dept doesnt have courses anymore. how do I keep the courses after detach

     

    Department dept;

     

    using (SchoolEntities ctx = new SchoolEntities())

    {

    dept = ctx.Department.Where(d1 => (d1.Name == "App Development")).FirstOrDefault();

    dept.Course.Load();

    IEntityWithChangeTracker ct = dept as IEntityWithChangeTracker;

    ct.SetChangeTracker(null);

    ctx.Detach(ct);

    }

     

    Thanks

    Thursday, May 15, 2008 9:28 AM
  • ctx.Detach(ct) automatically removes the ChangeTracker so calling ct.SetChangeTracker(null) is unnecessary.

    Unfortunately if you detach an entity from its ObjectContext all of its relationships are automatically broken so if you detach all entities from the context you won't get a detached entity graph, but a set of distinct entities.

    You have two choices here.

    1) you can do a NoTracking query, so the results are not attached to the context, but this has some side effects: there is no identity resolution so you might end up with duplicate objects; also you cannot use Load() to load relations, you have to use Include() in the query, and you won't have foreign reference keys which you don't explicitly include so you can't do on-demand loading later (possibly with a different context).

    2) you can write an extension for the ObjectContext to detach all (or some) objects. You can enumerate all entities and all relations via ObjectContext.ObjectStateManager.GetObjectStateEntries(), and temporarily store the relevant info then restore the graph after the detach.

    There's a feature (see comments in http://forums.microsoft.com/Forums/ShowPost.aspx?PostID=2198238&SiteID=1) that didn't make v1 that will allow the detach of an entire object graph in one pass that'll make your job considerably easier.

    Thursday, May 15, 2008 9:33 PM
  • Thank you. 

    I am trying to do the following , (for ex.) a service call returns customer and his orders

    I could use MergeOption.NoTracking and get the graph I want.

    client makes changes(changes customer info, adds 2 orders) and passes the new object to save the data.

    I could use ctx.GetObjectByKey to get the customer object in the database to which I can apply changes

    How would I know what orders are already there and what orders are new

     

    Friday, May 16, 2008 8:49 AM
  •  BACHRATY Gergely wrote:

    1) you can do a NoTracking query, so the results are not attached to the context, but this has some side effects: there is no identity resolution so you might end up with duplicate objects; also you cannot use Load() to load relations, you have to use Include() in the query, and you won't have foreign reference keys which you don't explicitly include so you can't do on-demand loading later (possibly with a different context).



    Calling Load() on EntityReferences of an object loaded with NoTracking still works.  The related objects will also be loaded with NoTracking.  If you try to use a different MergeOption when you call Load() an exception is thrown.  You are right that the EntityKey of any related EntityReferences will be null.
    Friday, May 16, 2008 9:14 PM
  • My bad. The result is only detached from the change tracker, the RelatedEnds are still tied to the ObjectContext so subsequent queries based on relation metadata can be made if the OC is not disposed. (Note: I can't seem to find a way to completely detach theese objects from the ObjectContext, Detach() doesn't work so with NoTracking the OC might linger around until you attach to a different context.) Though the foreign key is still not loaded, so manually loading the reference later via Load() incurs an inner join on Customers-Orders, and the related customer object is retrieved by OrderID. Include() is still a better alternative, and it does one less roundtrip.

    Saturday, May 17, 2008 11:17 AM