none
OData client and caching RRS feed

  • Question

  • I'm working on a large application tha uses OData to communicate to the server to get its data. Everything works great ecept that there's no caching at all. This kind-of-like makes sense, but I cannot find a way to intercept LINQ queries so that I could implement some kind of caching myself.

    What options do I have?

    Tuesday, February 26, 2013 3:52 PM

Answers

  • Hey Matthijis,

    How are you querying for the original Products query? Perhaps what you can do is use $expand.

    In your case what you would want to do is:

    DataServiceQuery<Order> productsWithExpand = context.Products.Expand("Suppliers");

    The below article goes over this is a bit more detail.

    http://msdn.microsoft.com/en-us/library/ee358709.aspx

    If you do this on your original query it will bring back all the results in one query rather than going to the server 10 times.

    Thanks,

    Chris Robinson - WCF Data Services

    


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Thursday, February 28, 2013 4:30 PM
    Moderator

All replies

  • Hey Matthijis,

    How can you be sure that the data on the server has not changed? Is it because each query is bring back so much data you need to reduce it?

    For version 4 of the OData protocol we will be introducing a concept call Deltas. Essentially what you will ask for for all of the data since a particular time and it will give you all of the changes. OData is undergoing standardization and the protocol for this functionality is being created right now.

    Perhaps you can give me more information on your current scenario. I could think of one way you can maybe intercept the linq call. If you write a normal query like

       context.Customers.Where(c=>c.FirstName.Startswith("Chris")).ToString(), will yield the string "http://{context ServiceUri}/Customers?$filter=startswith(FirstName, 'Chris') eq true

    Perhaps you can use the fact that the linq statement builds a string that you can leverage to understand if you have queried it already or not. We have added api so that you then do Execute<Customer(uri) to then execute the it effectively intercepting it.

    Thinking a little more you could create an extension method like this.

    public static class ExtensionMethods

    {

        public static CacheExecute(this DataServiceQuery query)

        {

             // do logic to ToString() and see if its been executed or not.

            // either execute or return the results it would have returned.

         }

    }

    This might get really complicated though as you'll have to cache all the results in a dictionary and return these each time to be able to server up the results correctly.

    Lastly if this is a feature you want we have a place where people can vote for features. The feature you are asking for is located here.

    http://data.uservoice.com/forums/72027-wcf-data-services-feature-suggestions/suggestions/1490281-support-caching-of-reference-data-on-the-client

    I would vote for it if you need it.

    Thanks,

    Chris Robinson - WCF Data Services


    This posting is provided "AS IS" with no warranties, and confers no rights.


    Tuesday, February 26, 2013 11:01 PM
    Moderator
  • I'm showing a list of products to the user. Now each product has a related Supplier object. Problem currently is that when showing 10 products, it goes to the server 10 times to get the supplier by id, instead of "seeing" that it already has the supplier (assuming there's only 1 supplier object, it only needs to go to the server once).

    I definitely see the potential issue with using stale results, but that's the case with the good-old datasets as well...

    Wednesday, February 27, 2013 4:32 PM
  • Hey Matthijis,

    How are you querying for the original Products query? Perhaps what you can do is use $expand.

    In your case what you would want to do is:

    DataServiceQuery<Order> productsWithExpand = context.Products.Expand("Suppliers");

    The below article goes over this is a bit more detail.

    http://msdn.microsoft.com/en-us/library/ee358709.aspx

    If you do this on your original query it will bring back all the results in one query rather than going to the server 10 times.

    Thanks,

    Chris Robinson - WCF Data Services

    


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Thursday, February 28, 2013 4:30 PM
    Moderator
  • Hi Chris,

    I'm marking your reply as answer, although for me that wont work:

    I'm working on an infrastructural project, that lets me switch from direct EntityFramework usage, to using OData communication to a server. The IQueryable-using code shouldn't notice any difference so I'll need to fix things differently.

    I managed to solve things by making a wrapping IQueryable<T> (and IQueryProvider) implementation which then does some linq Expression parsing and afterwards looks up objects in DataServiceContext.Entities.

    Thanks anyway for the answer!

    Friday, March 1, 2013 9:54 AM
  • Hey Matthijis,

    Sorry I couldn't answer your exact query. This seems like quite a complicated piece of work.  I definitely would use expand syntax to get the graph in one request though or alternatively you can batch multiple queries into one request. I suppose you would need to translate that to an Include if it were going to EF directly. Does this mean that you would have to write a linq query that is the subset of what Linq to EF and Linq to OData uri supports? There are differences between the two linq providers.

    For example in the LinqToOData, we fixed a bug recently so that when you query for a resource like 'http://server/Service/Customers("FirstName", "LastName"), in linq lets say you wrote this as customers.Where(c=>c.Firstname=="FirstName" && c.LastName=="LastName") we didn't produce the correct uri and would instead produce a uri like "http://server/Service/Customers?$filter=Firstname eq 'FirstName' and LastName eq 'LastName'. We fixed this bug and this was released in 5.3. But my point is that the expression trees produce subtle differences and can cause problems. Also I believe that EF supports more canonical functions than OData as well. I'm not trying to dissuade you from doing this work but just trying to highlight that the same expression might not do the exact same thing you would expect on OData and EF.

    Thinking about this a bit more, would the context that you use, would it be both a DataServiceContext and a DbContext? There are many differences between these two contexts. The EF one has more advanced capabilities for state tracking for instance. If you change a relationship it will do the fix up, example would be Customer with Orders navigation collection, and Order with Customer Navigation Reference. It will auto fix these up DataServiceContext does not.

    Are you using the same classes in EF and WCF data services.

    Thanks,

    Chris Robinson - WCF Data Services.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    Friday, March 1, 2013 6:11 PM
    Moderator
  • It works great now. I did some additional changes to how the dataobjects are instantiated. I use the same assembly server-side (EF) and client-side (via OData). Only real issue I have left to fix, is that currently the WCF DataServices client has no way to exclude properties from serialization.
    Friday, March 1, 2013 6:14 PM