NHibernate IQueryable не задерживает выполнение - PullRequest
2 голосов
/ 20 января 2012

Я использую NHibernate 3.2, и у меня есть метод репозитория, который выглядит следующим образом:

    public IEnumerable<MyModel> GetActiveMyModel()
    {
        return from m in Session.Query<MyModel>()
               where m.Active == true
               select m;
    }

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

    var models = MyRepository.GetActiveMyModel();
    var filtered = from m in models
                   where m.ID < 100
                   select new { m.Name };

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

Если нет, то это означает, что все мои методы репозитория должны возвращать именно то, что нужно, и я не могу использовать LINQ дальше по цепочке без штрафов.

Я правильно понял?

Обновлено

В ответ на комментарий ниже: я пропустил строку, в которой я повторяю результаты, что приводит к запуску исходного SQL (WHERE Active = 1), а второй фильтр (ID <100), очевидно, выполняется в .NET . </p>

Кроме того, если я заменю второй фрагмент кода на

var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
               where m.Items.Count > 0
               select new { m.Name };

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

SELECT Name 
FROM MyModel m 
WHERE Active = 1 
    AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0

Anthony

Ответы [ 2 ]

7 голосов
/ 20 января 2012

Вы возвращаете IEnumerable<MyModel> из метода, что приведет к оценке в памяти с этого момента, даже если базовая последовательность равна IQueryable<MyModel>.

Если вы хотите разрешить код после GetActiveMyModel чтобы добавить в SQL-запрос, вместо этого верните IQueryable<MyModel>.

1 голос
/ 20 января 2012

Вы используете метод расширения «Где» в IEnumerable вместо IQueryable. Он все равно будет выполнять ленивый анализ и выдаст тот же результат, однако он оценивает IQueryable при вводе, и вы фильтруете коллекцию в памяти вместо базы данных.

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

(Да, я также хотел бы использовать расширенные методы расширения в IEnumerable вместо того, чтобы быть виртуальными членами, но, увы, они не являются)

...