NOTE: If you set your LinqDataSource's AutoPage property to true and are using SQL Server (2005+ I believe) then it will automatically use Skip() and Take() as seen below. However, if you want to do it manually or wonder how it works then read on.
Creating a GridView (or other data control) with efficient paging is very easy with LINQ. You can thank the Take and Skip operators for this; they allow you to only pull back the records you need. In the simple example below we are using a LinqDataSource and handling its onselecting method to create our LINQ query and do our paging. Set AutoPage to false since we are writing code to handle paging ourselves. Also the PageSize property of the GridView control is being populated from a constant in the code-behind class.
ASPX:
<asp:LinqDataSource ID="linqDS" runat="server" AutoPage="false"
ContextTypeName="Namespace.MyDataContext"
onselecting="linqDS_Selecting" />
<asp:GridView ID="myGV" runat="server" DataSourceID="linqDS"
AllowPaging="true" PageSize="<%# PAGE_SIZE %>"
AutoGenerateColumns="false">
<Columns>
<!--Removed for simplicity -->
</Columns>
</asp:GridView>
Code-behind:
// Const declared at top of code-behind
public const int PAGE_SIZE = 100;
protected void linqDS_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
// LINQ query
var query = from c in myDC.Class
select c;
// Do advanced query logic here (dynamically build WHERE clause, etc.)
// Set the total count
// so GridView knows how many pages to create
e.Arguments.TotalRowCount = query.Count();
// Get only the rows we need for the page requested
query = query.Skip(myGV.PageIndex * PAGE_SIZE).Take(PAGE_SIZE);
e.Result = query;
}