Могу ли я (и должен ли я) принудительно выполнить будущие запросы NHibernate в определенный момент? - PullRequest
5 голосов
/ 13 июня 2011

Я использую будущие запросы NHibernate в веб-приложении MVC 3 и стараюсь, чтобы весь доступ к моей базе данных происходил в моих контроллерах, а не в моих представлениях. Сайт представляет собой каталог ресурсов (описательные пометки), которые имеют коллекцию оценок «многие ко многим» и коллекцию тем «многие ко многим». Пользователи выбирают одну или несколько оценок и одну или несколько тем, а затем получают список подходящих ресурсов.

Чтобы заполнить форму поиска, я использую будущие запросы, чтобы получить все оценки и темы:

    public Domain.SearchFormData GetSearchFormData()
    {
        // Get all grades and all topics using a multiquery.

        IEnumerable<Grade> grades = Session.QueryOver<Grade>()
            .Future();

        IEnumerable<Topic> topics = Session.QueryOver<Topic>()
            .Future();

        var result = new SearchFormData();
        result.Grades = grades;
        result.Topics = topics;
        return result;
    }

Это очень просто, отлично работает и возвращает SearchFormData, что является простым DTO. Этот запрос выполняется в контроллере, и результат не нуждается в дальнейшей обработке, поэтому он передается прямо в представление для отображения в виде списков флажков.

Но из-за ленивого выполнения будущих запросов доступ к базе данных не запускается до тех пор, пока представление не начнет перебирать списки. Некоторые считают, что это (например, NHibernate Profiler) - нет-нет, и они говорят, что к тому времени, когда представление начинает рендеринг, должен быть выполнен весь доступ к базе данных. Основная причина заключается в том, что при наличии скрытого SELECT N + 1 легко работать, если вы этого не сделаете.

Здесь это не относится; и класс, и тема - простые объекты без коллекций. Я не против того, что представление вызовет доступ к базе данных. Но это заставляет меня искать чистый, ясный способ инициировать все будущие запросы. Один из способов сделать это - получить доступ к результатам, например, скопировав IEnumerable в список. Но на самом деле это не то, что мне нужно делать; Я просто хочу выполнить поставленные в очередь запросы.

Я думаю, что имеет смысл запускать триггер вне запроса выше. Мне могут понадобиться другие данные для отображения полного просмотра страницы, и я могу использовать другие будущие запросы для их получения. Например, на домашней странице также могут отображаться пять самых популярных ресурсов и общее количество ресурсов. Тогда контроллер будет вызывать несколько методов для накопления того, что ему нужно для представления, и будет наиболее эффективно выполнять все из них одновременно. Конечно, один из способов сделать это - развернуть мой GetSearchFormData в GetAllHomePageData и вернуть DTO с полями для всего на домашней странице. Но я использую форму поиска повсюду, а не только на главной странице. Так что я бы потерял какую-то хорошую модульность таким образом.

Ответы [ 3 ]

2 голосов
/ 22 марта 2013

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

Для фьючерсов LINQ, HQL и SQL Query используйте:

public static void ExecuteFutureQueries(this ISession session)
{
    var sessionImpl = (ISessionImplementor) session;
    var dummy = sessionImpl.FutureQueryBatch.Results;
}

Для фьючерсов QueryOver и ICriteria используйте:

public static void ExecuteFutureCriteria(this ISession session)
{
    var sessionImpl = (ISessionImplementor) session;
    var dummy = sessionImpl.FutureCriteriaBatch.Results;
}

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

1 голос
/ 05 апреля 2018

Даниэль Шиллинг ответил хорошо, но API NH с тех пор немного изменился. Свойство .Results больше не доступно в этих будущих дозаторах.

Для NHibernate 5.0 он должен выглядеть примерно так:

var eng = (NHibernate.Engine.ISessionImplementor)sess;
await eng.FutureCriteriaBatch.GetEnumerator<object>().GetEnumerableAsync();
await eng.FutureQueryBatch.GetEnumerator<object>().GetEnumerableAsync();

Вместо GetEnumerator можно использовать и GetFutureValue.
Если вы не можете использовать await, вы можете использовать task.Result для ожидания блокировки, или вы можете использовать существующие синхронные версии GetEnumerableAsync / GetValueAsync.

Кроме того, предупреждение Даниэля по-прежнему применимо: и FutureCriteriaBatch, и FutureQueryBatch выдают, когда вы пытаетесь это сделать, и они пусты.

К сожалению, в NH5.0 нет способа проверить, являются ли они пустыми, поэтому единственные варианты:

  • попробуй поймать игнорируемые
  • используйте отражение для проверки FutureCriteriaBatch.queries.Count
  • добавить фиктивный запрос на будущее, чтобы убедиться, что хотя бы один из них
1 голос
/ 05 июля 2011

Я думаю, что лучшим "решением" этого является вовсе не использование IEnumerable в ваших классах моделей представлений - почему бы не использовать простые массивы? Это заставит вас полностью заполнить ваши модели контроллером, и вы не потеряете функциональность Linq и т. Д.

Вторая проблема заключается в том, что вы напрямую используете свои доменные объекты (класс, тема) в SearchFormData - вместо этого вы должны использовать DTO. Вы упоминаете, что SearchFormData - это DTO, но это не совсем так, потому что оно ссылается на ваши доменные сущности.

Таким образом, SearchFormData должен выглядеть примерно так:

public class SearchFormData
{
  public GradeDTO[] Grades { get; set; }
  public TopicDTO[] Topics { get; set; }
}

Ваша настоящая проблема связана с дизайном, а не с проблемой NHibernate Futures IMO.

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