Замедляет ли использование лямбда-выражения, переданного в метод, запрос Entity Framework?

У меня есть метод:

public static void GetObjects()
{
    using(MyContext context = new MyContext())
    {
         var objects = context.Bars.Where(b => b.Prop1 != null)
                       .Select(b => new MyObject{Prop = b.Prop1, Name = b.Name})
                       .ToList();
         foreach(var object in objects)
         {
             // do something with the object
         }
    }
}

Я реорганизовал метод, чтобы сделать его более общим, чтобы я мог передать Func, чтобы я мог указать оператор where и какое свойство из таблицы Bars назначается MyObject.Prop следующим образом:

public static void GetObjectsV2(Func<Bar, bool> whereFunc, Func<Bar, string> selectPropFunc)
{
    using(MyContext context = new MyContext())
    {
         var objects = context.Bars.Where(whereFunc)
                       .Select(b => new MyObject{Prop = selectPropFunc(b), Name = b.Name})
                       .ToList();
         foreach(var object in objects)
         {
             // do something with the object
         }
    }
}

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


person aubreyrhodes    schedule 31.01.2011    source источник


Ответы (2)


Причина, по которой он работает медленнее, заключается в том, что вы передаете Func<Bar, bool>, который заставляет контекст извлекать ВСЕ полосы, а затем запускать Func для возвращенного набора результатов. Чтобы сделать этот пробег лучше, нужно передать Expression<Func<Bar, bool>>.

Если сложить все вместе, получится следующее:

public static void GetObjectsV2(Expression<Func<Bar, bool>> whereFunc, Expression<Func<Bar, string>> selectPropFunc)
{
    using(MyContext context = new MyContext())
    {
         var objects = context.Bars.Where(whereFunc)
                       .Select(selectPropFunc)
                       .ToList();
         foreach(var object in objects)
         {
             // do something with the object
         }
    }
}
person Mark Coleman    schedule 31.01.2011
comment
Большое спасибо за быстрый ответ! - person aubreyrhodes; 01.02.2011

Как я обнаружил в своем собственном вопросе, .Where(o => whereFunc(o)) не совпадает с .Where(whereFunc) в Entity Framework.

Первый, .Where(Expression<Func<Bar, bool>>), работает как любой другой вызов linq, просто добавляя выражение к дереву выражений.

Во втором случае, .Where(Func<Bar, bool>>), он скомпилирует и оценит вызов linq (который пока что является просто context.Bars) перед применением предиката whereFunc.


Итак, чтобы ответить на ваш вопрос, второй намного медленнее, потому что он загружает всю таблицу Bars в память, прежде чем что-либо с ней делать. Использование вместо этого .Where(o => whereFunc(o)) должно исправить это

(или, как предлагает Марк, измените тип whereFunc на Expression<Func<Bar, bool>>, в который неявно преобразуется Func<Bar, bool>)

person BlueRaja - Danny Pflughoeft    schedule 31.01.2011