Adicionando uma projeção de um critério NHibernate impede de realizar seleção de entidade padrão

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

Pergunta

Eu estou escrevendo um critério NHibernate que seleciona dados de apoio paginação. Eu estou usando a expressão COUNT(*) OVER() a partir do SQL Server 2005 (+) para se apossar do número total de linhas disponíveis, como sugeriu por Ayende Rahien. Eu preciso que o número seja capaz de calcular quantas páginas existem no total. A beleza desta solução é que eu não preciso para executar uma segunda consulta para se apossar da contagem de linhas.

No entanto, eu não consigo conseguem escrever um critério de trabalho (Ayende só fornece uma consulta HQL).

Aqui está uma consulta SQL que mostra o que eu quero e ele funciona muito bem. Nota que eu intencionalmente deixado de fora a lógica de paginação real para se concentrar no problema:

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

Aqui está o HQL:

select
    item, rowcount()
from 
    Item item

Note que a função rowcount() está registrado em um costume NHibernate dialeto e resolve COUNT(*) OVER() em SQL.

A exigência é que a consulta é expresso utilizando um critério. Infelizmente, eu não sei como obtê-lo direito:

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

Sempre que eu adicionar uma projeção, NHibernate não seleciona item (como seria sem uma projeção), apenas o rowcount() enquanto eu realmente precisa de ambos. Além disso, eu não consigo item projeto como um todo, só que é propriedades e eu realmente não quero listar todos eles.

Espero que alguém tem uma solução para isso. Obrigado na mesma.

Foi útil?

Solução

Eu acho que não é possível em Critérios, tem alguns limites.

Você pode obter os itens de identificação e de carga em uma consulta posterior:

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

Se você não gostar dele, o uso HQL, você pode definir o número máximo de resultados lá também:

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

Outras dicas

Use CreateMultiCriteria.

Você pode executar 2 declarações simples, com apenas um hit para o DB dessa forma.

Eu estou querendo saber por que usar critérios é um requisito. você não pode usar session.createSQLQuery? Se você realmente deve fazê-lo em uma consulta, eu teria sugerido puxando para trás os objetos item ea contagem, como:

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

... desta forma você pode voltar item objetos de sua consulta, juntamente com a contagem. Se você tiver um problema com o cache do Hibernate, você também pode configurar os espaços de consulta (entidade / caches de tabela) associados a uma consulta nativa de modo que as entradas de cache de consulta obsoleto serão apagados automaticamente.

Se entendi sua pergunta corretamente, eu tenho uma solução. Lutei um pouco com este mesmo problema.

Deixe-me descrever rapidamente o problema que tive, para se certificar de que estamos na mesma página. Meu problema desceu para paginação. Eu quero mostrar 10 registros na interface do usuário, mas eu também quero saber o Total número de registros que combinavam com os critérios de filtro. Eu queria fazer isso usando o NH critérios API, mas ao adicionar uma projeção para contagem de linhas, a minha consulta não funcionava mais, e eu não obter quaisquer resultados (não me lembro o erro específico, mas parece que o que você 're recebendo).

Aqui está a minha solução (copiar e colar do meu código de produção atual). Note-se que "sessionerror" é o nome da entidade empresarial estou recuperando dados paginados para, de acordo com o critério 3 filtro:. IsDev, IsRead e 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];

Então eu criar meus critérios de base, com restrições opcionais. Então eu cloná-lo e acrescentar uma projecção contagem de linhas para os critérios clonado. Nota que eu cloná-lo antes eu adicionar as restrições de paginação. Então eu configurar um IMultiCriteria para conter o original e clonado ICriteria objetos, e usar o IMultiCriteria para executar ambos. Agora eu tenho meus dados paginada dos ICriteria originais (e eu só arrastou os dados que eu preciso através do fio), e também uma contagem bruta de quantos registros real combinava os meus critérios (útil para exibição ou criação de links de paginação, ou qualquer outro). Esta estratégia tem funcionado bem para mim. Espero que este seja útil.

Eu sugeriria investigar transformador resultado costume chamando SetResultTransformer () na sua sessão.

Crie uma propriedade fórmula no mapeamento de classe:

<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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top