У вас не может быть методов расширения, вызываемых на анонимных лямбда-выражениях, поэтому вы захотите использовать класс Cache. Чтобы правильно кэшировать запрос, вам также нужно «поднять» любые параметры (включая ваш DataContext) в параметры для вашего лямбда-выражения.Это приводит к очень многословному использованию как:
var results = QueryCache.Cache((MyModelDataContext db) =>
from x in db.Foo where !x.IsDisabled select x);
Для того, чтобы очистить что, мы можем создать экземпляр QueryCache на основе каждого контекста, если мы делаем это нестатическим:
public class FooRepository
{
readonly QueryCache<MyModelDataContext> q =
new QueryCache<MyModelDataContext>(new MyModelDataContext());
}
Тогда мы может написать метод кэша, который позволит нам написать следующее:
var results = q.Cache(db => from x in db.Foo where !x.IsDisabled select x);
Любые аргументы в запросе также должны быть сняты:
var results = q.Cache((db, bar) =>
from x in db.Foo where x.id != bar select x, localBarValue);
Вот реализация QueryCache я издевался до:
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);
}
}
Это может быть расширен для поддержки большего числа аргументов. Большой бит заключается в том, что, передавая значения параметров самому методу Cache, вы получаете неявное типирование для выражения лямбда.
EDIT: Обратите внимание, что вы не можете применить новые операторы для скомпилированных запросов .. В частности, вы не можете сделать что-то вроде этого:
var allresults = q.Cache(db => from f in db.Foo select f);
var page = allresults.Skip(currentPage * pageSize).Take(pageSize);
Так что, если вы планируете пейджинговой запрос, вы должны сделать это в вместо того, чтобы делать это позже. Это необходимо не только для исключения исключения, но и в соответствии со всей точкой Skip/Take (чтобы не возвращать все строки из базы данных). Эта модель будет работать:
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);
}
Другой подход к пейджинговой будет возвращать 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);
}
Эта модель используется как:
var results = GetPageableFoo()(currentPage, pageSize);
Это запутанный вопрос, потому что вы, кажется, объединяете LINQ и LINQ to SQL (который дополнительно генерирует, компилирует и кэширует планы выполнения за кулисами при каждом запуске запроса). Если вы спрашиваете о скомпилированных планах выполнения SQL Server, я не знаю (что я знаю), чтобы скомпилировать их и сохранить их в кэше, кроме их запуска. – 48klocs
Это не имеет никакого отношения к SQL Server. LINQ to SQL компилирует запросы, которые могут занимать довольно много времени - от обоих синтаксисов LINQ (цепочка или SQL-стиль) до SQL при каждом запуске этих запросов. Прочтите ссылку наверху, чтобы узнать больше. – tghw
Одна из проблем, которые я нашел с использованием скомпилированных запросов с L2S в веб-приложении, заключается в том, что для ее компиляции вам необходимо передать экземпляр DataContext - для веб-приложения это означает, что вам нужен один общий DataContext для всего сайта - который в результате вызвало несколько серьезных проблем с многопоточными проблемами, когда сайт начал иметь большую нагрузку. Мне действительно очень не нравится, как вы должны передать экземпляр datacontext при компиляции запроса ... – kastermester