No se pueden invocar métodos de extensión en expresiones lambda anónimas, por lo que querrá usar una clase Cache. Para cachear correctamente una consulta, también tendrá que "levantar" cualquier parámetro (incluido su DataContext) en parámetros para su expresión lambda.Esto se traduce en un uso muy prolijo como:
var results = QueryCache.Cache((MyModelDataContext db) =>
from x in db.Foo where !x.IsDisabled select x);
Con el fin de limpiar eso, podemos crear instancias de un QueryCache en función de cada contexto si lo hacemos no estático:
public class FooRepository
{
readonly QueryCache<MyModelDataContext> q =
new QueryCache<MyModelDataContext>(new MyModelDataContext());
}
Luego puede escribir un método de caché que nos permitirá escribir la siguiente: también tendrán que ser levantado
var results = q.Cache(db => from x in db.Foo where !x.IsDisabled select x);
Cualquier argumento en su consulta:
var results = q.Cache((db, bar) =>
from x in db.Foo where x.id != bar select x, localBarValue);
Aquí está la aplicación QueryCache me burlé de arriba:
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);
}
}
Esto se puede ampliar para que admita más argumentos. Lo mejor de todo es que al pasar los valores de los parámetros al método Cache mismo, se obtiene el tipeo implícito para la expresión lambda.
EDIT: Tenga en cuenta que no se puede aplicar nuevos operadores a las consultas compiladas .. En concreto no se puede hacer algo como esto:
var allresults = q.Cache(db => from f in db.Foo select f);
var page = allresults.Skip(currentPage * pageSize).Take(pageSize);
Así que si usted planea en la paginación de una consulta, es necesario hacerlo en el compilar la operación en lugar de hacerlo más tarde. Esto es necesario no solo para evitar una excepción, sino también para cumplir con el objetivo de Saltar/Tomar (para evitar devolver todas las filas de la base de datos). Este patrón funciona:
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);
}
Otro enfoque para paginación habría que devolver 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);
}
Este patrón se utiliza como:
var results = GetPageableFoo()(currentPage, pageSize);
Esta es una pregunta confusa porque parece que está combinando LINQ y LINQ con SQL (que además genera, compila y almacena en caché los planes de ejecución detrás de escena cada vez que se ejecuta una consulta). Si está preguntando sobre los planes de ejecución compilados de SQL Server, no hay forma (que yo sepa) de compilarlos y mantenerlos en la memoria caché que no sean ejecutarlos. – 48klocs
Esto no tiene nada que ver con SQL Server. LINQ to SQL compila consultas (que pueden llevar bastante tiempo) desde las dos sintaxis de LINQ (encadenamiento o estilo SQL) hasta SQL cada vez que se ejecutan esas consultas. Lea el enlace en la parte superior para obtener más información. – tghw
Un problema que he encontrado con el uso de consultas compiladas con L2S en una aplicación web es que para compilarlo debes pasar la instancia de DataContext; para una aplicación web, esto significa que necesitas un DataContext compartido para todo el sitio, que a cambio, me causó algunos problemas principales de múltiples hilos cuando el sitio comenzó a tener una gran carga. Realmente no me gusta cómo debe pasar la instancia del contexto de datos al compilar la consulta ... – kastermester