2011-02-10 40 views
18

Tôi muốn phân trang với NHibernate khi viết truy vấn LINQ. Thật dễ dàng để làm điều gì đó như thế này:Bắt đếm với NHibernate + LINQ + Tương lai

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

Nhưng với điều này tôi không nhận được bất kỳ thông tin nào về tổng số mục. Và nếu tôi chỉ làm một đơn giản .Count(), điều đó sẽ tạo ra một cuộc gọi mới đến cơ sở dữ liệu.

Tôi đã tìm thấy this answer đã giải quyết vấn đề này bằng cách sử dụng trong tương lai. Nhưng nó sử dụng tiêu chí. Làm thế nào tôi có thể làm điều này với LINQ?

+1

Sử dụng Session.QueryOver thay - nó tiết kiệm IntelliSense và 'compileability', và có một phương pháp SelectCount. Nếu bạn cần tôi có thể cung cấp ví dụ chi tiết về việc sử dụng nó – Genius

+0

Có, vui lòng, QueryOver dường như cũng hoạt động tốt! – Allrameest

+1

(Tôi đã phát hiện lỗi trong giải pháp của mình và xóa lỗi đó để tránh nhầm lẫn. Tôi sẽ đăng phiên bản cố định ngay) –

Trả lời

30

Khó khăn khi sử dụng Tương lai với LINQ là các thao tác như Đếm thực hiện ngay lập tức.

Khi @vandalo phát hiện ra, Count() sau ToFuture() thực sự chạy Bộ đếm trong bộ nhớ, điều này rất tệ.

Cách duy nhất để tính số truy vấn LINQ trong tương lai là sử dụng GroupBy trong trường bất biến. Một sự lựa chọn tốt sẽ là một cái gì đó đã là một phần của bộ lọc của bạn (như một tài sản "IsActive")

Dưới đây là một ví dụ giả sử bạn có một tài sản như vậy trong thanh toán:

//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; 

Dĩ nhiên, sự lựa chọn là làm hai vòng, mà không phải là xấu anyway. Bạn vẫn có thể tái sử dụng IQueryable gốc, đó là hữu ích khi bạn muốn làm phân trang trong một lớp cấp cao hơn:

//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(); 

Cập nhật (2011/02/22): Tôi đã viết một blog post về vấn đề này và một giải pháp tốt hơn nhiều.

+5

phương pháp mở rộng trong bài đăng blog của bạn là một giải pháp rất tốt đẹp – Chad

-6

Ok, có vẻ như nó nên được làm việc trong trường hợp của bạn, nhưng tôi không thử nghiệm:

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

thử nghiệm đầu tiên trước khi upvoting;)

UPD: xin lỗi, như tôi hiểu bạn bây giờ, bạn cần lấy Tổng số của tất cả các mục. Sau đó, bạn cần chạy truy vấn mà không cần phân trang:

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

Đó KHÔNG phải là điều anh cần. –

+1

@Diego Mijelshon, wow! đại diện của tôi bây giờ là 666, cảm ơn! :) – Genius

+0

Cả hai truy vấn đó đều không thực hiện những gì được yêu cầu. Lấy tập hợp đối tượng được phân trang + nhận tổng số đối tượng đáp ứng tiêu chí. – Neal

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

Đây là điều tôi vừa phát hiện ra rằng LINQ to NH xử lý tốt, ToRowCountQuery xóa bỏ/bỏ qua truy vấn và thực hiện đếm hàng trong tương lai.

var rowCount = query.ToRowCountQuery().FutureValue<int>(); 

var result = query.Future(); 

var asArray = result.ToArray(); 
var count = rowCount.Value(); 
+0

Hoạt động tốt với SQL Server, nhưng không phải với SQL Server Compact Edition ... :( System.Data.SqlServerCe.SqlCeException: Đã xảy ra lỗi khi phân tích truy vấn. [ Mã số dòng = 2, Mã thông báo offset = 1, Mã thông báo lỗi = SELECT] – Allrameest

+1

Câu hỏi được yêu cầu cụ thể về giải pháp LINQ, không phải QueryOver –

+0

Kỳ lạ, ToRowCountQuery dường như xóa nhóm ... http://stackoverflow.com/ câu hỏi/8012966/torowcountquery-dường như bỏ qua nhóm –

4

Bài đăng trên blog sau đây có triển khai ToFutureValue hoạt động với LINQ.

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

Nó có một lỗi nhỏ trên dòng sau đó phải được thay đổi từ này.

var provider = (NhQueryProvider)source.Provider; 

Để này:

var provider = (INhQueryProvider)source.Provider; 

Sau khi áp dụng các thay đổi mà bạn có thể sử dụng que truy vấn theo cách này:

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