Автоматически скомпилируйте запросы Linq

Едва ли, потому что, как Вы говорите, компилятор только знает, что callFriend () возвращает Животное, не Собаку или Утку.

разве Вы не можете добавить краткий обзор makeNoise () метод Животному, которое было бы реализовано как кора или шарлатан ее подклассами?

36
задан Jason 4 August 2009 в 19:53
поделиться

2 ответа

Вы не можете использовать методы расширения для анонимных лямбда-выражений, поэтому вы захотите использовать класс 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());
}

Затем мы можем написать метод Cache, который включит мы должны написать следующее:

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, вы получаете неявную типизацию лямбда-выражения.

РЕДАКТИРОВАТЬ: Обратите внимание, что вы не можете применять новые операторы к скомпилированным запросам. В частности, вы не можете делать что-то вроде этого:

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);
18
ответ дан 27 November 2019 в 06:17
поделиться

Поскольку никто не пытается, я попробую. Может, мы оба сможем как-нибудь с этим разобраться. Вот моя попытка.

Я установил это с помощью словаря, я также не использую DataContext, хотя я считаю, что это тривиально.

public static class CompiledExtensions
    {
        private static Dictionary<string, object> _dictionary = new Dictionary<string, object>();

        public static IEnumerable<TResult> Cache<TArg, TResult>(this IEnumerable<TArg> list, string name, Expression<Func<IEnumerable<TArg>, IEnumerable<TResult>>> expression)
        {
            Func<IEnumerable<TArg>,IEnumerable<TResult>> _pointer;

            if (_dictionary.ContainsKey(name))
            {
                _pointer = _dictionary[name] as Func<IEnumerable<TArg>, IEnumerable<TResult>>;
            }
            else
            {
                _pointer = expression.Compile();
                _dictionary.Add(name, _pointer as object);
            }

            IEnumerable<TResult> result;
            result = _pointer(list);

            return result;
        }
    }

теперь это позволяет мне сделать это

  List<string> list = typeof(string).GetMethods().Select(x => x.Name).ToList();

  IEnumerable<string> results = list.Cache("To",x => x.Where( y => y.Contains("To")));
  IEnumerable<string> cachedResult = list.Cache("To", x => x.Where(y => y.Contains("To")));
  IEnumerable<string> anotherCachedResult = list.Cache("To", x => from item in x where item.Contains("To") select item);

с нетерпением жду некоторого обсуждения этого вопроса. это для дальнейшего развития этой идеи.

2
ответ дан 27 November 2019 в 06:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: