Можно ли сохранить предварительно скомпилированные делегаты, которые я передаю в метод? - PullRequest
1 голос
/ 08 декабря 2011

У меня есть метод, который принимает делегата и запускает его на объекте DbContext.Local и, если он равен нулю, пытается найти его в базе данных, как указано ниже.

   public static T FirstOrDefaultInLocalOrDb<T>(this DbSet<T> myTable, Func<T, string, bool> criteria, string input) where T : class
    {
        var output = myTable.Local.Where(o => criteria((T)o, input)).FirstOrDefault();

        if (output == null)
        {
            Expression<Func<T, bool>> predicate = (u) => criteria(u, input);
            output = myTable.Where(predicate.Compile()).FirstOrDefault();
        }

        return output;
    }   

99% времени он находит его в локальной сущности и не нуждается в переходе в БД.

В другой части моей программы эта строка вызывает этот метод тысячи раз, каждый раз с уникальным HomeId.

var currHouse = db.Houses.FirstOrDefaultInLocalOrDb2(delegate(House h, string value) { return h.AllHomesID == value; }, HomeId);

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

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

UPDATE

Я написал эту неуниверсальную версию метода, которую, я думаю, я пытаюсь заставить сделать свой оригинальный метод, просто обобщенным способом. Этот метод, казалось, работал намного быстрее. Используя System.Diagnostic.Stopwatch, старый метод работал около 100 мс, а этот - около 7 мс.

public static House FirstOrDefaultAllHomesIdInLocalOrDb(this DbSet<House> myHouseTable, string allHomesId)
{

    var output = myHouseTable.Local.Where(o => o.AllHomesID == allHomesId).FirstOrDefault();

    if (output == null)
    {
        output = myHouseTable.Where(o => o.AllHomesID == allHomesId).FirstOrDefault();
    }

    return output;
}

1 Ответ

2 голосов
/ 08 декабря 2011

Самая большая проблема здесь в том, что вы объявили criteria как Func, а не как Expression.

Это означает, что запрос должен извлечь все строки myTable.Local в ваше приложение, а затем запустить ваш делегат против них.

Другими словами, если вы изменили тип на Expression<...> вместо Func<...>, выполнение SQL будет выполнено с этими критериями, и"TOP 1 "или любой другой синтаксис, который требуется вашему фактическому ядру базы данных.

Вместо этого вы в основном выполняете select * from myTable, а затем останавливаетесь на первом.

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

Таким образом, в этом случае, если в таблице 1 миллион строк и 500 000 совпадений, но вам нужно только первое, вы все равно получаете 500 000 строк и отбрасываете последние 499,999 из них.

...