none
Caching the results of read-only queries RRS feed

  • Question

  • Hi EF team,

    I want to cache the results of my queries, perhaps in an ASP.NET application with one ObjectContext per request.


    I don't want to hold on to any unneccesary objects (in particular, the context
    ), but I've seen some contradicing advice around detaching.

    Is it enough to simply use a MergeOption.NoTracking query, or must I detach?

    If so, is there a way to specify NoTracking at the Context level (on all queries)? How can I ensure that users of my Context can only issue read-only queries?

    If not, how should I detach? How can I ensure that all entities are detached? For example, do I also need to detach any entites projected into anonymous types?

    ...
    select new { Foo = "Blah", Product = p }; // p is an entity

    Thanks!

    Monday, August 4, 2008 10:37 PM

Answers

  • If the entities are not necessarily read-only, then you have a few things to consider. You may or may not already know these things.

     

    1) If you query entities using no tracking and leave them in a cache and never attach them to a context (say for drop down lists etc) - not even accidentally by joining them into a graph, then it will be clean.

     

    2) If you want to ensure that they are not attached to the graph somehow before the context is destroyed, then you should iterate through them and explicitly detach each and every one. If you have items in a graph, you have to detach each item of the graph.

     

    3) If the entities are attached and the objectcontext is destroyed, the entities will not be properly detached and will still have knowledge of the change tracker. This will bite you in the rear if you want to attach those objects to another context.

     

    4) If you ever want to have them change tracked, then cache them, then reattach them and continue with the changes, you'll lose the entity state.

     

    I think the bottom line is Yes, mergeoptions.notracking is the way to go but be aware that it's possible to inadvertantly attach the entiteis to a context. This will be a problem if you do this, then cache the objects again, destory that context, then spin up a new context and call that code again that inadvertantly attempts to attach the object to the new context becuase you will then get an error saying that the enitiy is already attached to another context.

     

    julie

    Tuesday, August 5, 2008 12:35 PM

All replies

  • NoTracking & Detached Queries:

    Entities created with a NoTracking query will not be attached to a context and therefore will not get updated. If you are building a DAL and the other developers can only access data through that, then you can control it that way.You write the queries and they just access the methods that you expose which will just pass them the results.

     

    Keep in mind that even if the entites are detached if you build graphs with them and you are linking them to entities that ARE attached, then they will get attached to the context anyway.

     

    Permanently ReadOnly queries:

    If it's applicable, you can actually define an entity right in the model to be read only, then you don't have to worry about. But that data will still be attached to the context in a regular query, it just won't update.

     

    The way to do this is using a feature of the EDM called QueryView.

     

    It will replace the default mapping that allows Read and Updates and the entity will become a view --  permanently read-only.

     

    QueryViews have to be coded manually in the XML of your EDMX file and the syntax is Entity SQL, so they are a little tricky at first, but it is the model's way of turning your entities into views.

     

    Here's the MSDN topic on QueryView.

    http://vs2008sp1docs.msdn.microsoft.com/en-us/ms520405.aspx

     

    hth

    julie

    Tuesday, August 5, 2008 3:46 AM
  • Thanks for the info Julie - additionally thanks for all your work in the EF community!

    I'm not building a simple DAL; more like a query-enabled business layer which exposes the ability to issue arbitary queries against a context.

    Additionally my model isn't, in general, read-only - so that rules out using QueryViews; also from what you say about them they wouldn't address my question, for the following reason:

    The motivation isn't that these queries are necessarily read-only (although that's the presumed consequence). What I want to ensure is just that the cached query results do not hold on to references to any unnecessary resources.

    Tuesday, August 5, 2008 10:26 AM
  • If the entities are not necessarily read-only, then you have a few things to consider. You may or may not already know these things.

     

    1) If you query entities using no tracking and leave them in a cache and never attach them to a context (say for drop down lists etc) - not even accidentally by joining them into a graph, then it will be clean.

     

    2) If you want to ensure that they are not attached to the graph somehow before the context is destroyed, then you should iterate through them and explicitly detach each and every one. If you have items in a graph, you have to detach each item of the graph.

     

    3) If the entities are attached and the objectcontext is destroyed, the entities will not be properly detached and will still have knowledge of the change tracker. This will bite you in the rear if you want to attach those objects to another context.

     

    4) If you ever want to have them change tracked, then cache them, then reattach them and continue with the changes, you'll lose the entity state.

     

    I think the bottom line is Yes, mergeoptions.notracking is the way to go but be aware that it's possible to inadvertantly attach the entiteis to a context. This will be a problem if you do this, then cache the objects again, destory that context, then spin up a new context and call that code again that inadvertantly attempts to attach the object to the new context becuase you will then get an error saying that the enitiy is already attached to another context.

     

    julie

    Tuesday, August 5, 2008 12:35 PM
  • Ok, so if NoTracking is the way to go, how do I ensure that all queries against a particular Context are NoTracking queries?

    In Linq-to-Sql,
    you would set ObjectTrackingEnabled = false.
    Wednesday, August 6, 2008 11:34 AM
  •  

    I don't know of a single setting that will help you application wide. AFAIK, notracking is defined per query, whether that's an objectquery you create or you are using Load or CreateSourceQuery or Refresh.

     

    There aren't any hooks into the query pipeline so you can't override the query execution.

     

    I've been thinking of all the ways I might approach this but they are not satisfying.

     

    If you create your own generic query methods for your developers to use, so that you can ensure they are alwyas notracking, then they can't use LINQ.

     

    You could create a single method to set a query to no tracking, but they would have to explicitly call it.

     

    Maybe there's a setting somewhere that I just don't know about, but I have searched the APIs and the documentation nad cn't see MergeOption being used anywhere but in the expclicity query execution. I don't think it exists.

     

    Add it to the v2 wish list!!

     

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3391412&SiteID=1

    Wednesday, August 6, 2008 12:20 PM
  • Thanks for looking Julie.

    I'd like to hear what the EF team have to say about this - this is a basic scenario which they must have given some thought to.

    Just to restate the aim: I have an entity model, one use for which is to provide query facilities for a web application. The web application needs to cache the results of some queries for performance.

    I want to ensure that the results of arbitrary queries, once in cache, do not hold on to any unnecessary resources (such as the context itself). Such objects will not ever be mutated or re-persisted and will be read from multiple threads.

    In Linq-To-Sql I believe I could simply ensure I only cache queries against contexts with change tracking disabled.
    Thursday, August 7, 2008 10:45 AM
  • Another motivation for this is to make a general-purpose LINQ query result cache work with the Entity Framework.
    Saturday, August 9, 2008 9:39 AM
  • Julie,

     

    In point number 3, what's the purpose of having the entity track in "Change Tracker" when the object context is already destroyed?  Because this always give me a problem, thou I have to learn how to work with object context.

     

     

     Julie Lerman wrote:

     

    3) If the entities are attached and the objectcontext is destroyed, the entities will not be properly detached and will still have knowledge of the change tracker. This will bite you in the rear if you want to attach those objects to another context.

     

    julie

    Tuesday, August 12, 2008 1:58 PM
  • I questioned this myself. See Jeff Derstadt's explanation in this forum thread:

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3249871&SiteID=1

     

    Just remember to detach the entities from the context before the context is destroyed and you'll be good to go. But it's definitely a gotcha!

    Tuesday, August 12, 2008 2:04 PM
  • Julie,

     

    Object context/ObjectStateManager declare locally within a function doesn't share its content  with other Object context/ObjectStateManager in another function, right?  Is only the "Entity Change Tracker" which is causing the problem (and I assume this is global). 

     

    I'm kinda confuse with all these and I cant find a good site that explain these.  Any good site to recommend? Smile

     

    Thanks in advance...

     

     

    Thursday, August 14, 2008 3:30 PM
  • Yes, that's true. An objectContext is self-contained.

     

    This problem is caused by something deep in the entity that has a piece of data in it that contained the original referecnce to the objectContext. Maybe it's a string or a piece of binary data or something. And as you can see in the thread I pointed to, it is even possible to remove that from teh entity,but it's not recommended (Danny's follow up post) because doing that manually could cause other problems.

     

    For resources, to start with I think the documentation on objectcontext is pretty good on MSDN. http://msdn.microsoft.com/en-us/library/bb738465.aspx

    though it just doesn't get into these little nuances. Unfortunately, so much of this stuff we are discovering the hard way.

     

    Which is why I'm writing my book, but it's not going to be out for a few more months.

    Thursday, August 14, 2008 3:47 PM