データが多すぎて一度にメモリにロードできない場合があります。たとえば、行数が百万以上あるテーブルを考えてみます。すべてのデータをメモリにロードできたとしても、このプロセスには長い時間がかかります。
これらのシナリオに対処するには、多くの方法があります。サーバーでデータを集計およびキャッシュするクエリーを作成し、Web サービスを使用して、データを Silverlight クライアントに配信する方法があります。これでも、C1OlapPage で使用可能なテーブルが作成されます。
この例では、WCF サービスを使用して、SQL データベースに格納されている Northwind データにアクセスします。この例の注目すべき点は、必ずしもすべてのデータが一度にメモリにロードされるわけではないことです。C1OlapPage は、フィルタに現在含まれる顧客のデータだけを要求します。
この例では、ASP.Net Web サイト内に Silverlight プロジェクトを作成します。また、LINQ to SQL クラスを使用して、サンプルの Northwind データベースからデータをクエリーします。LINQ to SQL は、Visual Studio(2008 以降)に含まれている ORM(オブジェクト関係マッピング)実装です。これにより、.NET クラスを使用してリレーショナルデータベースをモデル化し、そのデータベースに対して LINQ を使用してクエリーできます。
最初に、Northwind データベースの LINQ to SQL 表現を作成します。Silverlight プロジェクトに関連付けられている Web サイトプロジェクトを右クリックし、[新しい項目の追加]をクリックします。[LINQ to SQL クラス]を選択し、NorthwindDataClasses.dbml という名前を付けます。
次に、サーバーエクスプローラーから項目をドラッグして、[請求書]ビューからのすべてのデータフィールドを取り込みます。
その後、LINQ および作成したばかりの LINQ to SQL クラス(NorthwindDataClasses)を使用して、このデータをクエリーする WCF サービスを作成します。Web サイトプロジェクトノードを右クリックし、[新しい項目の追加]をクリックします。[WCF サービス]を選択し、NorthwindDataService.svc という名前を付けます。
NorthwindDataService.svc のコードを次のコードに置き換えます。
C# |
コードのコピー
|
---|---|
using System; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Activation; using System.Collections.Generic; using System.Text; namespace SqlFilter.Web { [ServiceContract(Namespace = "")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class NorthwindDataService { /// <summary>/// すべての請求書を取得します。/// </summary> [OperationContract] public List<Invoice> GetInvoices() { var ctx = new NorthwindDataClassesDataContext(); var invoices = from inv in ctx.Invoices select inv; return invoices.ToList(); } /// <summary>/// すべての顧客を取得します。/// </summary> [OperationContract] public List<string> GetCustomers() { var ctx = new NorthwindDataClassesDataContext(); var customers = (from inv in ctx.Invoices select inv.CustomerName).Distinct(); return customers.ToList(); } /// <summary>/// 特定の顧客セットのすべての請求書を取得します。/// </summary> [OperationContract] public List<Invoice> GetCustomerInvoices(params string[] customers) { // ハッシュセットを構築します var hash = new HashSet<string>(); foreach (string c in customers) { hash.Add(c); } string[] customerList = hash.ToArray(); // リスト内の顧客の請求書を取得します var ctx = new NorthwindDataClassesDataContext(); var invoices = from inv in ctx.Invoices where customerList.Contains(inv.CustomerName) select inv; return invoices.ToList(); } } } |
ここで、Web サービスに対して3つのメソッドを定義したことに注目してください。最初の2つは単純な Get メソッドであり、LINQ および以前に作成した LINQ to SQL クラスを使用して、項目のリストを返します。GetCustomerInvoices メソッドは、顧客の配列をパラメータとして受け取るという点で特殊です。これは、Silverlight C1OlapGrid プロジェクトのクライアント側で定義されるフィルタです。
Silverlight プロジェクトに移動する前に、Web サイトプロジェクトを構築し、Web サービスへの参照を追加する必要があります。参照を追加するには、ソリューションエクスプローラーで、Silverlight プロジェクトノードを右クリックし、[サービス参照の追加]をクリックします。次に、[探索]をクリックし、NorthwindDataService.svc を選択します。「NorthwindDataServiceReference」に名前を変更し、[OK]をクリックします。
これでデータソースの準備ができたので、これを C1OlapPage に接続して以下を確認する必要があります。
これらのタスクを完了する前に、UI を設定する必要があります。MainPage.XAML に、1つの C1OlapPage コントロールと、ステータスストリップとして使用されるいくつかの TextBlock を追加します。
XAML |
コードのコピー
|
---|---|
<Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <olap:C1OlapPage x:Name="_c1OlapPage"/> <TextBlock x:Name="_lblLoading"FontSize="24"Opacity=".5"Text="Loading data..."HorizontalAlignment="Center"VerticalAlignment="Center"/> <TextBlock x:Name="_lblStatus"Text="Ready"HorizontalAlignment="Right"Grid.Row="1"/> </Grid> |
次に、次のコードをフォームに追加します。
C# |
コードのコピー
|
---|---|
ObservableCollection<string> _allCustomers;
ObservableCollection<NorthwindDataServiceReference.Invoice> _invoices;
C1OlapFilter _customerFilter;
|
これらのフィールドには、データベース内のすべての顧客の完全なリスト、現在ユーザーによって選択されている顧客のリスト、および任意の一時点で選択可能な顧客の最大数が含まれます。
顧客の完全なリストは、C1OlapField.Values プロパティに割り当てる必要があります。このプロパティには、フィルタで表示される値のリストが含まれます。デフォルトでは、C1OlapPage により、このリストには生データから検出された値が挿入されます。この場合、生データに含まれているのはリストの一部分だけなので、代わりに完全なバージョンを提供する必要があります。_allCustomers ObservableCollection は、ユーザーが選択できる顧客のコレクション全体を保持します。C1OlapPage は、実際には _invoices コレクションとの組み合わせで使用されます。このコレクションは、選択した顧客によってフィルタ処理されたデータセットです。
MainPage() のコードを次のコードに置き換えます。
C# |
コードのコピー
|
---|---|
public MainPage() { InitializeComponent(); // OlapPage データソースを初期化します _invoices = new ObservableCollection<SqlFilter.NorthwindDataServiceReference.Invoice>(); _c1OlapPage.DataSource = _invoices; // OlapPage ビューを初期化します var olap = _c1OlapPage.OlapEngine; olap.BeginUpdate(); olap.ColumnFields.Add("OrderDate"); olap.RowFields.Add("CustomerName"); olap.ValueFields.Add("ExtendedPrice"); olap.RowFields[0].Width = 200; olap.Fields["OrderDate"].Format = "yyyy"; olap.Fields["CustomerName"].Filter.ShowValues = selectedCustomers.ToArray(); olap.EndUpdate(); // データベース内のすべての顧客のリストを取得します var sc = new SqlFilter.NorthwindDataServiceReference.NorthwindDataServiceClient(); sc.GetCustomersCompleted += sc_GetCustomersCompleted; sc.GetCustomersAsync(); // ステータスを表示します _lblStatus.Text = "Retrieving customer list..."; } |
ここでは、C1OlapPage データソースを初期化し、デフォルトビューを作成し、データベース内のすべての顧客のリストを取得します。ユーザーが必要な顧客を選択できるように、データベース内のすべての顧客の完全なリストを取得する必要があります。このリストは、件数は多いもののサイズは大きくありません。リストには顧客名だけが含まれ、注文や注文詳細などの関連付けられた詳細は含まれません。
データは Web サービスから入手し、非同期に取得されるため、データのロードが終了すると sc_GetCustomersCompleted イベントが発生します。
C# |
コードのコピー
|
---|---|
void sc_GetCustomersCompleted(object sender, SqlFilter.NorthwindDataServiceReference.GetCustomersCompletedEventArgs e) { // "ロード中" メッセージを非表示にします _lblLoading.Visibility = Visibility.Collapsed; // CustomerName フィルタを監視します _customerFilter = _c1OlapPage.OlapEngine.Fields["CustomerName"].Filter; _customerFilter.PropertyChanged += filter_PropertyChanged; // ビュー定義を監視して、[CustomerName]フィールドが常にアクティブであることを確認します _c1OlapPage.ViewDefinitionChanged += _c1OlapPage_ViewDefinitionChanged; // 利用可能な顧客を[CustomerName]フィールドのフィルタに表示します _allCustomers = e.Result; _customerFilter.Values = _allCustomers; // データを取得します GetData(); } |
このイベントは、データベース内の顧客の完全なリストを取得します。フィルタで表示するために、このリストを保存します。C1OlapField.PropertyChanged イベントを監視する必要があります。このイベントは、ユーザーがフィルタを含む任意のフィールドプロパティを変更すると発生します。このイベントが発生したら、ユーザーによって選択された顧客のリストを取得し、そのリストをデータソースに渡します。
次は、フィルタが変更されたときにデータソースを更新するイベントハンドラです。
C# |
コードのコピー
|
---|---|
// [CustomerName]フィールドのフィルタが変更されています。新しいデータを取得します void filter_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { GetData(); } |
フィールドの Filter プロパティは、このフィールドがビューで "アクティブ" である場合に、C1OlapEngine によって考慮されます。"アクティブ" とは、フィールドが RowFields、ColumnFields、ValueFields、または FilterFields コレクションのメンバであることを意味します。この場合、[CustomerName]フィールドは特殊なフィルタが設定され、常にアクティブである必要があります。このためには、エンジンの ViewDefinitionChanged イベントを処理して、[Customers]フィールドを常にアクティブにします。
次は、[CustomerName]フィールドを常にアクティブにするコードです。
C# |
コードのコピー
|
---|---|
// Customer フィールドを常にアクティブにします void _c1OlapPage_ViewDefinitionChanged(object sender, EventArgs e) { var olap = _c1OlapPage.OlapEngine; var field = olap.Fields["CustomerName"]; if (!field.IsActive) { olap.FilterFields.Add(field); } } |
GetData メソッドが呼び出されて、フィルタで選択された顧客の請求書データが取得されます。
C# |
コードのコピー
|
---|---|
// 選択された顧客の請求書データを取得します void GetData() { // 現在のフィルタ設定に基づいて、アクティブな顧客リストを再作成します var selectedCustomers = new ObservableCollection<string>(); foreach (string customer in _allCustomers) { if (_customerFilter.Apply(customer)) { selectedCustomers.Add(customer); } } _customerFilter.ShowValues = selectedCustomers.ToArray(); // 選択された顧客の請求書を取得します var sc = new SqlFilter.NorthwindDataServiceReference.NorthwindDataServiceClient(); sc.GetCustomerInvoicesCompleted += sc_GetCustomerInvoicesCompleted; sc.GetCustomerInvoicesAsync(selectedCustomers); // ステータスを表示します _lblStatus.Text = string.Format("Retrieving invoices for {0} customers...", selectedCustomers.Count); } |
ここでは、ユーザーによって選択された顧客のリストを構築するために、C1OlapFilter(_customFilter)を使用して、その Apply メソッドを呼び出します。次のイベントでは、フィルタ処理された請求書データを返す Web サービスへの別の非同期呼び出しを行います。
C# |
コードのコピー
|
---|---|
// 新しいデータを取得し、C1OlapPage に表示します void sc_GetCustomerInvoicesCompleted(object sender, SqlFilter.NorthwindDataServiceReference.GetCustomerInvoicesCompletedEventArgs e) { if (e.Cancelled || e.Error != null) { _lblStatus.Text = string.Format("** Error: {0}", e.Error != null ? e.Error.Message : "Canceled"); } else { _lblStatus.Text = string.Format("Received {0} invoices ({1} customers).", e.Result.Count, _customerFilter.ShowValues.Length); // 更新を開始します var olap = _c1OlapPage.OlapEngine; olap.BeginUpdate(); // データソースを更新します _invoices.Clear(); foreach (var invoice in e.Result) { _invoices.Add(invoice); } // 更新を終了します olap.EndUpdate(); } } |
ここでアプリケーションを実行すると、"CustomerName" 設定に含まれる顧客だけがビューに含まれていることがわかります。
その他の顧客を表示するには、[CustomerName]フィールドをダブルクリックし、[フィールドの設定]を選択して、[フィルタ]設定を開きます。
次に、特定の顧客を選択するか、条件を定義して、フィルタを編集します。カスタムフィルタ条件を定義するには、[フィールドの設定]の[フィルタ]タブの下にある[テキストフィルタ]をクリックし、条件タイプ(次の値と等しい、次の値で始まるなど)を選択してから、次のように条件を入力します。
[OK]をクリックすると、アプリケーションによって変更が検出され、GetData メソッドから追加データが要求されます。新しいデータがロードされると、C1OlapPage によって変更が検出され、OLAP テーブルが自動的に更新されます。
前のセクションで説明した完全な実装については、付属しているサンプル "SqlFilter" を参照してください。このサンプルを拡張して、フィルタを含む OLAP ビューをローカルストレージに格納することもできます。「ローカルストレージ内の永続的な OLAP ビュー」を参照してください。