sqlquery query linq nhibernate linq-to-nhibernate

query - Cómo contar con NHibernate+Linq+Future



nhibernate sql query string (4)

Quiero hacer paginación con NHibernate cuando escribo una consulta de Linq. Es fácil hacer algo como esto:

return session.Query<Payment>() .OrderByDescending(payment => payment.Created) .Skip((page - 1)*pageSize) .Take(pageSize) .ToArray();

Pero con esto no obtengo ninguna información sobre la cantidad total de artículos. Y si solo hago un simple .Count (), eso generará una nueva llamada a la base de datos.

Encontré esta respuesta que lo resolvió usando el futuro. Pero usa Criteria. ¿Cómo puedo hacer esto con Linq?


La dificultad con el uso de Futures con LINQ es que operaciones como Count se ejecutan inmediatamente.

Como @vandalo descubrió, Count() después de ToFuture() realmente ejecuta el conteo en la memoria, lo cual es malo.

La única forma de obtener el conteo en una futura consulta LINQ es usar GroupBy en un campo invariable. Una buena opción sería algo que ya forma parte de sus filtros (como una propiedad "IsActive")

Aquí hay un ejemplo suponiendo que tiene dicha propiedad en el Pago:

//Create base query. Filters should be specified here. var query = session.Query<Payment>().Where(x => x.IsActive == 1); //Create a sorted, paged, future query, //that will execute together with other statements var futureResults = query.OrderByDescending(payment => payment.Created) .Skip((page - 1) * pageSize) .Take(pageSize) .ToFuture(); //Create a Count future query based on the original one. //The paged query will be sent to the server in the same roundtrip. var futureCount = query.GroupBy(x => x.IsActive) .Select(x => x.Count()) .ToFutureValue(); //Get the results. var results = futureResults.ToArray(); var count = futureCount.Value;

Por supuesto, la alternativa es hacer dos recorridos de ida y vuelta, lo cual no es tan malo de todos modos. Todavía puede reutilizar el IQueryable original, que es útil cuando desea realizar paginación en una capa de nivel superior:

//Create base query. Filters should be specified here. var query = session.Query<Payment>(); //Create a sorted, paged query, var pagedQuery = query.OrderByDescending(payment => payment.Created) .Skip((page - 1) * pageSize) .Take(pageSize); //Get the count from the original query var count = query.Count(); //Get the results. var results = pagedQuery.ToArray();

Actualización (22-02-2011): escribí una publicación en el blog sobre este tema y una solución mucho mejor.


La siguiente publicación de blog tiene una implementación de ToFutureValue que funciona con LINQ.

http://sessionfactory.blogspot.com.br/2011/02/getting-row-count-with-future-linq.html

Tiene un pequeño error en la siguiente línea que se debe cambiar a partir de esto.

var provider = (NhQueryProvider)source.Provider;

A esto:

var provider = (INhQueryProvider)source.Provider;

Después de aplicar el cambio, puede usar las consultas de esa manera:

var query = session.Query<Foo>(); var futureCount = query.ToFutureValue(x => x.Count()); var page = query.Skip(pageIndex * pageSize).Take(pageSize).ToFuture();


Ok, parece que debería estar funcionando en tu caso, pero no he probado:

return session.QueryOver<Payment>() .Skip((page - 1) * pageSize) .Take(pageSize) .SelectList(r => r.SelectCount(f => f.Id)) .List<object[]>().First();

Prueba primero antes de la votación ascendente;)

UPD: lo siento, como te entiendo ahora, necesitas obtener el recuento de todos los artículos. Luego debe ejecutar la consulta sin paginación:

return session.QueryOver<Payment>() .SelectList(r => r.SelectCount(f => f.Id)) .List<object[]>().First();


var query = Session.QueryOver<Payment>() .OrderByDescending(payment => payment.Created) .Skip((page -1 ) * pageSize) .Take(pageSize)

Esto es algo que acabo de descubrir que el Linq to NH maneja muy bien, el ToRowCountQuery elimina take / skip de la consulta y hace un recuento de filas en el futuro.

var rowCount = query.ToRowCountQuery().FutureValue<int>(); var result = query.Future(); var asArray = result.ToArray(); var count = rowCount.Value();