Cache in Dlinq

A

Andrus

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.
 
M

Marc Gravell

"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)

Marc
 
M

Marc Gravell

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

}
}
 
A

Andrus

Marc,
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 ?
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.
 
M

Marc Gravell

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?
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...
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)...

Marc
 
M

Marc Gravell

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?)
 
A

Andrus

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.
 
M

Marc Gravell

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...

Marc
 
A

Andrus

Marc,
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.
 
M

Marc Gravell

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...
Is it reasonable to subclass DataContext to fix this ?
The cache is internal, not protected. Subclassing won't help.
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
 
M

Marc Gravell

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();
}
}
 
A

Andrus

Marc,
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.
 
M

Marc Gravell

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
 
J

Jim Wooley

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...
Is it reasonable to subclass DataContext to fix this ?

The cache is internal, not protected. Subclassing won't help.
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

Andrus, thanks for letting us know about this issue. I suspect the
confusion stems from a change between the betas and the final RTM of
LINQ to SQL. I'm trying to get confirmation from the product teams and
will let you know if we find otherwise. If you find other issues with
the LINQ in Action book, feel free to let us know in our forum at
http://www.manning-sandbox.com/forum.jspa?forumID=302. Also, we do
have an errata list at http://linqinaction.net/blogs/main/pages/linq-in-action-errata.aspx
which we will update once we receive confirmation on this issue.

Jim Wooley
www.ThinqLinq.Com
www.LinqInAction.net
 
M

Marc Gravell

Now that is service with a smile! Anything you can find out would be
appreciated...

Loving the book, btw.

Marc
 
M

Marc Gravell

Do you have any more information about this issue ?

My response is already on connect (from ages ago). I know nothing more.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top