Я пытаюсь реализовать схему кэширования для моего репозитория EF, аналогичную той, что была опубликована в блоге здесь . Как сообщили автор и комментаторы, ограничение заключается в том, что метод генерации ключей не может создавать ключи кеша, которые изменяются в зависимости от параметров данного запроса. Вот метод генерации ключа кеша:
private static string GetKey(IQueryable query)
{
string key = string.Concat(query.ToString(), "\n\r",
typeof(T).AssemblyQualifiedName);
return key;
}
Таким образом, следующие запросы дадут один и тот же ключ кеша:
var isActive = true;
var query = context.Products
.OrderBy(one => one.ProductNumber)
.Where(one => one.IsActive == isActive).AsCacheable();
и
var isActive = false;
var query = context.Products
.OrderBy(one => one.ProductNumber)
.Where(one => one.IsActive == isActive).AsCacheable();
Обратите внимание, что единственное различие состоит в том, что isActive = true
в первом запросе и isActive = false
во втором.
Любые предложения / идеи по эффективной генерации ключей кэша, которые различаются параметрами IQueryable
, были бы искренне признательны.
Благодарность Сергею Барскому за использование схемы кэширования EF CodeFirst.
Я сам использовал метод обхода дерева выражений IQueryable с целью определения значений параметров, используемых в запросе. Предложив maxlego , я расширил класс System.Linq.Expressions.ExpressionVisitor для посещения интересующих нас узлов выражений - в данном случае MemberExpression ]. Обновленный метод GetKey
выглядит примерно так:
public static string GetKey(IQueryable query)
{
var keyBuilder = new StringBuilder(query.ToString());
var queryParamVisitor = new QueryParameterVisitor(keyBuilder);
queryParamVisitor.GetQueryParameters(query.Expression);
keyBuilder.Append("\n\r");
keyBuilder.Append(typeof (T).AssemblyQualifiedName);
return keyBuilder.ToString();
}
И класс QueryParameterVisitor
, созданный на основе ответов Брайана Уоттса и Марка Гравелла. на этот вопрос , выглядит так:
///
/// subclass which encapsulates logic to
/// traverse an expression tree and resolve all the query parameter values
///
internal class QueryParameterVisitor : ExpressionVisitor
{
public QueryParameterVisitor(StringBuilder sb)
{
QueryParamBuilder = sb;
Visited = new Dictionary();
}
protected StringBuilder QueryParamBuilder { get; set; }
protected Dictionary Visited { get; set; }
public StringBuilder GetQueryParameters(Expression expression)
{
Visit(expression);
return QueryParamBuilder;
}
private static object GetMemberValue(MemberExpression memberExpression, Dictionary visited)
{
object value;
if (!TryGetMemberValue(memberExpression, out value, visited))
{
UnaryExpression objectMember = Expression.Convert(memberExpression, typeof (object));
Expression> getterLambda = Expression.Lambda>(objectMember);
Func
Я все еще занимаюсь профилированием производительности при генерации ключей кеша и надеюсь, что это не слишком дорого (я обновлю вопрос с результаты, как только я их получу). Я оставлю вопрос открытым, на случай, если у кого-то есть предложения по оптимизации этого процесса или рекомендации по более эффективному методу генерации ключей кеша, зависящих от параметров запроса. Хотя этот метод дает желаемый результат, он ни в коем случае не оптимален.