Добавление проекции к критериям NHibernate останавливает его от выбора объекта по умолчанию

StackOverflow https://stackoverflow.com/questions/1644776

Вопрос

Я пишу критерий NHibernate, который выбирает данные, поддерживающие подкачку страниц. Я использую выражение COUNT (*) OVER () из SQL Server 2005 (+), чтобы получить общее количество доступных строк как , предложенный Айенде Рахьен. Мне нужно это число, чтобы подсчитать, сколько всего страниц. Прелесть этого решения в том, что мне не нужно выполнять второй запрос, чтобы получить счетчик строк.

Однако мне не удается написать рабочие критерии (Ayende предоставляет только HQL-запрос).

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

SELECT Items.*, COUNT(*) OVER() AS rowcount
FROM Items

Вот HQL:

select
    item, rowcount()
from 
    Item item

Обратите внимание, что функция rowcount () зарегистрирована в пользовательском диалекте NHibernate и преобразуется в COUNT (*) OVER () в SQL.

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

var query = Session
    .CreateCriteria<Item>("item")
    .SetProjection(
       Projections.SqlFunction("rowcount", NHibernateUtil.Int32));

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

Я надеюсь, что у кого-то есть решение для этого. В любом случае, спасибо.

Это было полезно?

Решение

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

Вы можете получить идентификатор и загрузить элементы в следующем запросе:

var query = Session
    .CreateCriteria<Item>("item")
    .SetProjection(Projections.ProjectionList()
       .Add(Projections.SqlFunction("rowcount", NHibernateUtil.Int32))
       .Add(Projections.Id()));

Если вам это не нравится, используйте HQL, вы также можете установить максимальное количество результатов:

IList<Item> result = Session
    .CreateQuery("select item, rowcount() from item where ..." )
    .SetMaxResult(100)
    .List<Item>();

Другие советы

Используйте CreateMultiCriteria.

Таким образом, вы можете выполнить 2 простых оператора с одним попаданием в БД.

Мне интересно, почему использование критериев является обязательным требованием. Не можете ли вы использовать session.CreateSQLQuery? Если вы действительно должны сделать это в одном запросе, я бы предложил вернуть объекты Item и их количество, например:

select {item.*}, count(*) over() 
from Item {item}

... таким образом вы можете получить обратно объекты Item из вашего запроса вместе с их количеством. Если у вас возникла проблема с кэшированием в Hibernate, вы также можете настроить пространства запросов (кэши сущностей / таблиц), связанные с собственным запросом, чтобы устаревшие записи кэша запросов автоматически очищались.

Если я правильно понимаю ваш вопрос, у меня есть решение. Я немного боролся с этой же проблемой.

Позвольте мне быстро описать проблему, с которой я столкнулся, чтобы убедиться, что мы на одной странице. Моя проблема сводилась к пейджингу. Я хочу отобразить 10 записей в пользовательском интерфейсе, но я также хочу знать общее количество записей, которые соответствуют критериям фильтра. Я хотел сделать это с помощью API критериев NH, но при добавлении прогноза для подсчета строк мой запрос больше не работал, и я не получил никаких результатов (я не помню конкретной ошибки, но звучит так, как вы получаю).

Вот мое решение (скопируйте и вставьте из моего текущего производственного кода). Обратите внимание, что " SessionError " имя бизнес-объекта, для которого я извлекаю выгружаемые данные в соответствии с 3 критериями фильтра: IsDev, IsRead и IsResolved.

ICriteria crit = CurrentSession.CreateCriteria(typeof (SessionError))
    .Add(Restrictions.Eq("WebApp", this));

if (isDev.HasValue)
    crit.Add(Restrictions.Eq("IsDev", isDev.Value));

if (isRead.HasValue)
    crit.Add(Restrictions.Eq("IsRead", isRead.Value));

if (isResolved.HasValue)
    crit.Add(Restrictions.Eq("IsResolved", isResolved.Value));

// Order by most recent
crit.AddOrder(Order.Desc("DateCreated"));

// Copy the ICriteria query to get a row count as well
ICriteria critCount = CriteriaTransformer.Clone(crit)
    .SetProjection(Projections.RowCountInt64());
critCount.Orders.Clear();

// NOW add the paging vars to the original query
crit = crit
    .SetMaxResults(pageSize)
    .SetFirstResult(pageNum_oneBased * pageSize);

// Set up a multi criteria to get your data in a single trip to the database
IMultiCriteria multCrit = CurrentSession.CreateMultiCriteria()
    .Add(crit)
    .Add(critCount);

// Get the results
IList results = multCrit.List();

List<SessionError> sessionErrors = new List<SessionError>();
foreach (SessionError sessErr in ((IList)results[0]))
    sessionErrors.Add(sessErr);

numResults = (long)((IList)results[1])[0];

Итак, я создаю свои базовые критерии с дополнительными ограничениями. Затем я клонирую его и добавляю проекцию подсчета строк в критерии CLONED. Обратите внимание, что я клонирую его до того, как добавляю ограничения по подкачке. Затем я настроил IMultiCriteria для хранения исходных и клонированных объектов ICriteria и использую IMultiCriteria для их выполнения. Теперь у меня есть страничные данные из исходной ICriteria (и я перетаскивал только те данные, которые мне нужны по сети), а также необработанный счетчик того, сколько фактических записей соответствовало моим критериям (полезно для отображения или создания ссылок на пейджинг или что-то еще). Эта стратегия хорошо сработала для меня. Я надеюсь, что это полезно.

Я бы предложил исследовать пользовательский преобразователь результатов, вызвав SetResultTransformer () в вашем сеансе.

Создайте свойство формулы в сопоставлении классов:

<property name="TotalRecords" formula="count(*) over()" type="Int32" not-null="true"/>;

IList<...> result = criteria.SetFirstResult(skip).SetMaxResults(take).List<...>();
totalRecords = (result != null && result.Count > 0) ? result[0].TotalRecords : 0;
return result;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top