c#:
I need to repeatedly execute same queries which returns single entity by id, like: Customer cust = (from c in db.Customers where c.CustomerID=="AIRBU" select c).SingleOrDefault(); DLinq holds tracked object list internally so customer "AIRBU" exists in memory. How to force DLinq to look its list and not to make round trip to server ? I looked into Entity Framework also but havent found any caching solution in EF. Andrus.
"LINQ in Action" states that Single() is an exception, in that it checks the cache first, not the database. I haven't tried it yet (long day...) - but worth a try ;-p (I'm assuming that SingleOrDefault doesn't qualify for this exception since you've tried it... - and it would presumably need the predicate to be based solely on the primary key column[s])
OK; I couldn't find a single case when Single() did anything other than hit the database... I wonder what the authors had in mind? Anyways... here is something truly, truly hacky that does the job; I'm off to lather... using System; using System.Data.Linq; using System.Data.Linq.Mapping; using System.Diagnostics; using System.Linq; using System.Reflection; using WindowsFormsApplication4; // ns to DataClasses1DataContext = Northwind static class DataContextExt { public static T GetFromCache<T>(this Table<T> table, params object[] keyValues) where T : class { if (table == null) throw new ArgumentNullException("table"); return GetFromCache<T>(table.Context, keyValues); } public static T GetFromCache<T>(this DataContext context, params object[] keyValues) where T : class { // surfactants on standby - this is dirty, plain and simple // (oh, and brittle, and not compile-time safe, and requires // sufficient trust... you get the idea; nasty nasty nasty) if (context == null) throw new ArgumentNullException("context"); const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; object services = context.GetType().GetProperty("Services", FLAGS).GetValue(context, null); object[] args = { context.Mapping.GetMetaType(typeof(T)), keyValues }; Type[] paramTypes = { typeof(MetaType), typeof(object[]) }; return (T)services.GetType().GetMethod("GetCachedObject", FLAGS, null, paramTypes, null).Invoke(services, args); } } static class Program { static void Main() { using (DataClasses1DataContext ctx = new DataClasses1DataContext()) { // cache the original Supplier sup = ctx.Suppliers.First(); int id = sup.SupplierID; // get from the context Supplier same = ctx.GetFromCache<Supplier>(id); Trace.WriteLine(ReferenceEquals(sup, same), "Got from Context"); // get from the table Supplier again = ctx.Suppliers.GetFromCache(id); Trace.WriteLine(ReferenceEquals(sup, again), "Got from Table"); // and check what happens if it isn't there... Supplier nothing = ctx.GetFromCache<Supplier>(53); Trace.WriteLine(ReferenceEquals(nothing, null), "Null when missing"); } }
Marc, [quoted text, click to view] > OK; I couldn't find a single case when Single() did anything other > than hit the database... I wonder what the authors had in mind?
MSDN clearly states that every simple query must return result form identity cache if it exists, not reaching to database. Do you have any idea why it is not working ? How to use identity cache in documented way ? [quoted text, click to view] > Anyways... here is something truly, truly hacky that does the job; I'm > off to lather...
Where did you found information which allows to create such program ? I havent found any documentation on Services property and GetCachedObject method. Andrus.
[quoted text, click to view] > MSDN clearly states that every simple query must return result form identity > cache if it exists, not reaching to database.
Can you cite a source there? [quoted text, click to view] > Do you have any idea why it is not working ? > How to use identity cache in documented way ?
I have asked that very question on a LINQ group; no answer yet... [quoted text, click to view] > Where did you found information which allows to create such program ?
From grubbling around... it is dirty and brittle (i.e. it will break if somebody [quite reasonably] refactors the internal implementation)...
[quoted text, click to view] > MSDN clearly states that every simple query must return result form identity > cache if it exists, not reaching to database.
(in particular - yes it should return the same instance from the cache [and it does] - the question is focused around whether it hits the db...
So I guess it comes down to defining "easily identifiable"... I'll run some more tests at some point (not "right now"), but I don't know how easy / hard it will be to get working... which is why I am trying to get some input from the LINQ people...
[quoted text, click to view] >> MSDN clearly states that every simple query must return result form >> identity cache if it exists, not reaching to database. > > (in particular - yes it should return the same instance from the cache > [and it does] - the question is focused around whether it hits the db... > can you cite a page for the above claim?)
http://msdn2.microsoft.com/en-us/library/bb399376.aspx Quote: If the object requested by the query is easily identifiable as one already retrieved, no query is executed. The identity table acts as a cache of all previously retrieved objects. Andrus.
[quoted text, click to view] > Can we submit product feedback to Microsoft?
Feel free ["connect", or the managed LINQ forum]; personally I'd like to do a little more looking first... [quoted text, click to view] > Is it reasonable to subclass DataContext to fix this ?
The cache is internal, not protected. Subclassing won't help. [quoted text, click to view] > Ca we inform author about this ?
Authors plural; but I guess I can try to find some e-mail addresses and see what they say...
Marc, [quoted text, click to view] > So I guess it comes down to defining "easily identifiable"... I'll run > some more tests at some point (not "right now"), but I don't know how easy > / hard it will be to get working... which is why I am trying to get some > input from the LINQ people...
id == constant expression if definitely "easily identifiable". So it seems that we have found serious bug in Linq-SQL. Can we submit product feedback to Microsoft? Is it reasonable to subclass DataContext to fix this ? There is also bug in "Linq in Action" book if it states that cache works only with Single(). Ca we inform author about this ? Andrus.
For info, looked at the EF offering... unfortunately it is harder to profile, and I don't have my SQL profiler handy; following gets the right objects at least - but I don't know (until I get a few seconds to switch servers) how many times it hits the database: using System; using System.Data; using System.Data.Objects.DataClasses; using System.Linq; using AdventureWorksModel; static class Program { [STAThread] static void Main() { AdventureWorksEntities1 ctx = new AdventureWorksEntities1(); string setName = GetEntitySetName<Customer>(false), qualifiedSetName = GetEntitySetName<Customer>(true), keyName = GetKeyName<Customer>(); // expect DB hit here var cust = ctx.Customer.First(); var id = cust.CustomerID; // look by known key EntityKey key = ctx.GetEntityKey(setName, cust); var byKey = ctx.GetObjectByKey(key); object obj; if (ctx.TryGetObjectByKey(key, out obj)) { var cust1 = (Customer)obj; Console.WriteLine("Got..."); } // look by custom key and a new ctx (assume a single primary key for keyName) AdventureWorksEntities1 ctx2 = new AdventureWorksEntities1(); EntityKey customKey = new EntityKey(qualifiedSetName, keyName, id); // (different instance, but it works...; expect another DB hit here) var cust2 = ctx2.GetObjectByKey(customKey); // look using predicates var cust3 = ctx.Customer.Where(x => x.CustomerID == id).First(); var cust4 = ctx.Customer.First(x => x.CustomerID == id); } static string GetEntitySetName<T>(bool qualified) where T : EntityObject { EdmEntityTypeAttribute attrib = (EdmEntityTypeAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(EdmEntityTypeAttribute)); if (attrib == null) return null; return qualified ? (attrib.NamespaceName + "." + attrib.Name) : attrib.Name; } static string GetKeyName<T>() where T : EntityObject { return ( from prop in typeof(T).GetProperties() let attrib = (EdmScalarPropertyAttribute) Attribute.GetCustomAttribute(prop, typeof(EdmScalarPropertyAttribute)) where attrib != null && attrib.EntityKeyProperty select prop.Name).Single(); }
No; I've been invited to log a "connect" bug about it - I just want to make sure I can't get it to work first...
Marc, [quoted text, click to view] > For info, looked at the EF offering... unfortunately it is harder to > profile, and I don't have my SQL profiler handy; following gets the > right objects at least - but I don't know (until I get a few seconds > to switch servers) how many times it hits the database:
Thank you. Have you got any idea why Linq-SQL does not use cache ? Andrus.
Don't see what you're looking for? Try a search.
|