Non è possibile richiamare metodi di estensione su espressioni lambda anonime, quindi è consigliabile utilizzare una classe cache. Per poter archiviare correttamente una query è necessario anche "sollevare" qualsiasi parametro (incluso il DataContext) in parametri per l'espressione lambda.Ciò si traduce in un uso molto dettagliata come:
var results = QueryCache.Cache((MyModelDataContext db) =>
from x in db.Foo where !x.IsDisabled select x);
Per pulire che fino, siamo in grado di creare un'istanza di un QueryCache su una base per-contesto se facciamo non statico:
public class FooRepository
{
readonly QueryCache<MyModelDataContext> q =
new QueryCache<MyModelDataContext>(new MyModelDataContext());
}
Poi siamo in grado di scrivere un metodo di cache, che ci permetterà di scrivere la seguente: dovrà anche essere sollevato
var results = q.Cache(db => from x in db.Foo where !x.IsDisabled select x);
Qualsiasi argomento nella query:
012.
var results = q.Cache((db, bar) =>
from x in db.Foo where x.id != bar select x, localBarValue);
Ecco l'implementazione QueryCache ho preso in giro fino:
public class QueryCache<TContext> where TContext : DataContext
{
private readonly TContext db;
public QueryCache(TContext db)
{
this.db = db;
}
private static readonly Dictionary<string, Delegate> cache = new Dictionary<string, Delegate>();
public IQueryable<T> Cache<T>(Expression<Func<TContext, IQueryable<T>>> q)
{
string key = q.ToString();
Delegate result;
lock (cache) if (!cache.TryGetValue(key, out result))
{
result = cache[key] = CompiledQuery.Compile(q);
}
return ((Func<TContext, IQueryable<T>>)result)(db);
}
public IQueryable<T> Cache<T, TArg1>(Expression<Func<TContext, TArg1, IQueryable<T>>> q, TArg1 param1)
{
string key = q.ToString();
Delegate result;
lock (cache) if (!cache.TryGetValue(key, out result))
{
result = cache[key] = CompiledQuery.Compile(q);
}
return ((Func<TContext, TArg1, IQueryable<T>>)result)(db, param1);
}
public IQueryable<T> Cache<T, TArg1, TArg2>(Expression<Func<TContext, TArg1, TArg2, IQueryable<T>>> q, TArg1 param1, TArg2 param2)
{
string key = q.ToString();
Delegate result;
lock (cache) if (!cache.TryGetValue(key, out result))
{
result = cache[key] = CompiledQuery.Compile(q);
}
return ((Func<TContext, TArg1, TArg2, IQueryable<T>>)result)(db, param1, param2);
}
}
Questo può essere esteso per supportare più argomenti. Il bello è che passando i valori dei parametri nel metodo Cache stesso, si ottiene una digitazione implicita per l'espressione lambda.
EDIT: Si noti che non è possibile applicare nuovi operatori alle domande compilate .. In particolare, non si può fare qualcosa di simile:
var allresults = q.Cache(db => from f in db.Foo select f);
var page = allresults.Skip(currentPage * pageSize).Take(pageSize);
Quindi, se si ha intenzione di paging una query, è necessario farlo nel compila operazione invece di farlo in seguito. Ciò è necessario non solo per evitare un'eccezione, ma anche per mantenere l'intero punto di Skip/Take (per evitare di restituire tutte le righe dal database). Questo modello potrebbe funzionare:
public IQueryable<Foo> GetFooPaged(int currentPage, int pageSize)
{
return q.Cache((db, cur, size) => (from f in db.Foo select f)
.Skip(cur*size).Take(size), currentPage, pageSize);
}
Un altro approccio per il paging sarebbe quella di restituire un Func
:
public Func<int, int, IQueryable<Foo>> GetPageableFoo()
{
return (cur, size) => q.Cache((db, c, s) => (from f in db.foo select f)
.Skip(c*s).Take(s), c, s);
}
Questo modello è usato come:
var results = GetPageableFoo()(currentPage, pageSize);
Questa è una domanda confusa perché sembra che tu stia confondendo LINQ e LINQ con SQL (che in aggiunta genera, compila e memorizza nella cache i piani di esecuzione dietro le quinte ogni volta che viene eseguita una query). Se stai chiedendo dei piani di esecuzione compilati di SQL Server, non c'è modo (che io sappia) di compilarli e tenerli in cache diversi da quelli in esecuzione. – 48klocs
Questo non ha nulla a che fare con SQL Server. LINQ to SQL compila query - che possono richiedere un po 'di tempo - da entrambe le sintassi LINQ (concatenamento o stile SQL) a SQL ogni volta che vengono eseguite tali query. Leggi il link in alto per saperne di più. – tghw
Un problema che ho riscontrato con l'utilizzo di query compilate con L2S in un'app web è che per compilarlo è necessario passare l'istanza di DataContext - per un'applicazione Web ciò significa che è necessario un DataContext condiviso per l'intero sito, che in cambio mi hanno causato alcuni problemi con molti thread multipli quando il sito ha iniziato ad avere un grosso carico. Non mi piace davvero come si debba passare l'istanza di datacontext quando si compila la query ... – kastermester