Кэширование скомпилированных лямбда-выражений - PullRequest
4 голосов
/ 22 сентября 2009

Мне нужно, чтобы некоторая информация передавалась в виде лямбда-выражения некоторым методам. По сути, это способ добавить информацию в запрос к базе данных. Простой пример будет:

companyData.GetAll(
   where => "SomeField = @SOMEFIELD",
   order => "Id",
   SOMEFIELD => new Parameter {DbType = DbType.String, Value = "some value"}
)

Работает довольно хорошо, за исключением того, что мне нужно вызвать LambdaExpression.Compile , чтобы получить объект параметра, который оказывает большое влияние на производительность.

Чтобы получить более быстрые результаты, я пришел с этим наивным тестом кэширования:

class ExpressionCache<T,U>
{

    private static ExpressionCache<T, U> instance;
    public static ExpressionCache<T, U> Instance
    {
        get
        {
            if (instance == null) {
                instance = new ExpressionCache<T, U>();
            }
            return instance;
        }
    }

    private ExpressionCache() { }

    private Dictionary<string, Func<T, U>> cache = new Dictionary<string, Func<T, U>>();

    public Func<T, U> Get(Expression<Func<T, U>> expression)
    {
        string key = expression.Body.ToString();
        Func<T,U> func;
        if (cache.TryGetValue(key, out func)) {
            return func;
        }
        func = expression.Compile();
        cache.Add(key, func);
        return func;
    }
}

Этот класс имел огромное значение: от 35000 миллисекунд на 10000 итераций до 700.

Теперь возникает вопрос: с какой проблемой я столкнусь, используя тело выражения в качестве ключа для словаря?

Ответы [ 2 ]

10 голосов
/ 22 сентября 2009

Мне не ясно, зачем вам вообще нужны деревья выражений, если вы собираете их для делегатов. Почему бы просто не использовать делегаты для начала и заставить компилятор преобразовать лямбда-выражение в делегат, а не в дерево выражений?

Что касается использования тела строки - вы можете попасть в нечетные случаи, когда вы используете разные типы, но с одинаковыми именами свойств. Однако, учитывая, что вы уже используете типы в качестве параметров универсального типа, это, вероятно, не проблема ... Я должен подумать, что это будет работать, хотя я хотел бы клясться .

Да, и, кстати, ваш одноэлементный кеш не поточно-ориентирован. Я бы посоветовал вам инициализировать переменную instance в статическом инициализаторе. Это приводит к упрощению кода, а также к повышению его безопасности ...

private static readonly ExpressionCache<T, U> instance
     = new ExpressionCache<T, U>();
public static ExpressionCache<T, U> Instance { get { return instance; } }
10 голосов
/ 22 сентября 2009

Лямбда-выражения могут создавать замыкания. Когда это происходит, тело выражения не охватывает все, что входит в полученный код. Вы можете пропустить потенциально важные локальные переменные, которые включены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...