10.3 ObjectDataSourceオブジェクトを用いた大量データのページング制御
最後に、ObjectDataSourceオブジェクトを利用した大量データのページング制御方法について解説する。
第2部「9.3.2 ページング」の項で解説したように、2階層型自動データバインドを用いたページング制御方式では、大量データのページング制御ができない。これは、ページ切り替えのつどデータベースから全件のデータを取り寄せてしまうため、性能に優れたアプリケーションとならないからである(図10-13の上側)。
しかし、本章で解説したObjectDataSourceオブジェクトとGridViewコントロールを組み合わせた場合には、データアクセスコンポーネントがObjectDataSourceオブジェクトを介してインデックス情報を受け取れるようになっている。このため、データコンポーネントからSELECTクエリを発行する際に、現在表示するページ分だけのデータを取り寄せるようにすると、効率的なページングアプリケーションを作成することができる(図10-13の下側)。
|
図10-13 大量データのページング制御の問題点 |
以下に実装方法を解説する。なお、ここでは830行のデータが含まれている、Northwindサンプルデータベース上のOrdersテーブルのデータをページング制御するケースを例にとって解説する。
10.3.1 データアクセスコンポーネント側の準備
この機能を利用するためには、背後のデータアクセスコンポーネントが以下の2つのメソッドを持っている必要がある※3。
※3 なお、これらのメソッドのメソッド名は自由に決めてよいが、引数は固定であり、変更できない。 |
- データの総件数をint 値として返すメソッド
- 例) public int GetNumberOfOrders( )
- 特定ページ部分だけのデータを取得するデータ検索メソッド
- 例)public OrdersDataSet.OrdersDataTable GetDataByIndex(int startRowIndex, intmaximumRows)
これらのメソッドはテーブルアダプタ上に直接実装できないため、いずれもpartialクラス上に定義する。実装例を図10-14およびリスト10-3に示す※4。
※4 このうち、全件の件数を数えるクエリについては単一集計値クエリであるため、テーブルアダプタの標準機能を用いて定義できそうに見える。しかし現在のVisual Web Developerの場合には、残念ながらこのように定義したメソッドをObjectDataSourceオブジェクトに接続することができない。このため、partialクラス上に明示的に定義する必要がある。 |
|
図10-14 データコンポーネント機能とpartialクラスによる拡張 |
using System;
using System.Data;
using System.Data.SqlClient;
namespace OrdersDataSetTableAdapters
{
public partial class OrdersTableAdapter
{
public OrdersDataSet.OrdersDataTable GetDataByIndex(int startRowIndex,int maximumRows)
{
string query;
if (startRowIndex == 0)
{
query = string.Format("SELECT TOP {0} * FROM Orders ORDER BY OrderID", maximumRows);
}
else
{
// 手前のstartRowIndex - 1 件を避けて、最大maximumRows 件を取り出す
query = string.Format("SELECT TOP {0} * FROM Orders WHERE OrderID NOT IN (SELECT TOP {1} OrderID FROM Orders ORDER BY OrderID) ORDER BY OrderID",maximumRows, startRowIndex - 1);
}
SqlDataAdapter sqlda = new SqlDataAdapter(query, this.Connection);
OrdersDataSet ods = new OrdersDataSet();
sqlda.Fill(ods.Orders);
return ods.Orders;
}
public int GetNumberOfOrders()
{
SqlCommand sqlcmd = new SqlCommand("SELECT COUNT(*) FROM Orders",this.Connection);
this.Connection.Open();
int rows = (int)sqlcmd.ExecuteScalar();
this.Connection.Close();
return rows;
}
}
}
|
Imports System.Data
Imports System.Data.SqlClient
Namespace OrdersDataSetTableAdapters
Partial Public Class OrdersTableAdapter
Public Function GetDataByIndex(ByVal startRowIndex As Integer, ByValmaximumRows As Integer) As OrdersDataSet.OrdersDataTable
Dim query As String
If startRowIndex = 0 Then
query = String.Format("SELECT TOP {0} * FROM Orders ORDER BYOrderID", maximumRows)
Else
' 手前のstartRowIndex - 1 件を避けて、最大maximumRows 件を取り出す
query = String.Format("SELECT TOP {0} * FROM Orders WHERE OrderID NOT IN (SELECT TOP {1} OrderID FROM Orders ORDER BY OrderID) ORDER BY OrderID",maximumRows, startRowIndex - 1)
End If
Dim sqlda As SqlDataAdapter = New SqlDataAdapter(query,Me.Connection)
Dim ods As New OrdersDataSet()
sqlda.Fill(ods.Orders)
Return ods.Orders
End Function
Public Function GetNumberOfOrders() As Integer
Dim sqlcmd As SqlCommand = New SqlCommand("SELECT COUNT(*) FROM Orders", Me.Connection)
Me.Connection.Open()
Dim rows As Integer = CType(sqlcmd.ExecuteScalar(), Integer)
Me.Connection.Close()
Return rows
End Function
End Class
End Namespace
|
|
リスト10-3 partialクラスによるテーブルアダプタの拡張 |
10.3.2 ObjectDataSourceオブジェクトとの連結
上記の実装が済んだら、これをObjectDataSourceオブジェクトに連結し、プロパティ類を設定する。なお、このプロパティ設定はウィザードベースでの作業ができないため、すべて手作業で行う必要がある。
- EnablePaging = true (ページ情報をSELECT メソッドに引き渡す)
- TypeName = テーブルアダプタのクラス名
- SelectMethod = 当該ページ部分のみのデータを返すメソッド名
- SelectCountMethod = データ総件数をint 値として取得するメソッド
以上の作業により、大量データに対しても効率的なページング制御を行うことができる(図10-15)。
|
図10-15 ObjectDataSourceコントロールを利用した効率的なページング制御 |
10.3.3 より効率的な実装方法
なお、前述のリスト10-3ではn〜m件目のデータを取り寄せる際に、NOT INクエリを利用している(図10-16)。この方法はSQL Server 2000でも利用できる汎用的なものだが、SQL Server 2005をデータベースとして利用する場合には、新規に搭載されたROW_NUMBER( )関数を利用するとより効率的な実装ができる。
|
図10-16 特定ページのデータを検索するクエリの記述方法 |
このROW_NUMBER( )関数は、クエリの実行結果行セットに整数の連番を自動付与させる機能である※5。これによって連番を振っておき、これをWHERE句で絞り込むようにクエリを実装すると、効率的なデータ取得が可能になる(リスト10-4)。
※5 Oracleのrownum列に相当する機能である。 |
// startRowIndex 〜 startRowIndex + maximumRows - 1 番目のレコードを取り出す
query = string.Format("SELECT TOP {0} * FROM Orders WHERE OrderID NOT IN (SELECT
TOP {1} OrderID FROM Orders ORDER BY OrderID) ORDER BY OrderID", maximumRows,startRowIndex - 1);
↓
query = string.Format("SELECT * FROM (SELECT *, OW_NUMBER() OVER (ORDER BY
OrderID) rownum FROM [Order Details]) AS [Order Details] WHERE rownum BETWEEN
{0} AND {1} ORDER BY OrderID", startRowIndex, startRowIndex + maximumRows - 1);
|
|
リスト10-4 n〜m件目のデータを取得するためのクエリの記述方法 |
10.3.4 3階層型自動データバインドのまとめ
本章のキーポイントを整理すると、以下の通りとなる。
- ObjectDataSourceオブジェクトとデータコンポーネント機能のテーブルアダプタクラスにより、高い開発生産性と高い柔軟性を両立させることができる。
- これにより、2階層型自動データバインドでは記述することのできない可変型クエリなども利用することができるようになる。
- ObjectDataSource コントロールを利用すれば、大量データのページング制御も可能になる。
Insider.NET 記事ランキング
本日
月間