OLAP for WPF /Silverlight
大規模なデータソース
C1Olap クイックスタート > 大規模なデータソース

データが多すぎて一度にメモリにロードできない場合があります。たとえば、行数が百万以上あるテーブルを考えてみます。すべてのデータをメモリにロードできたとしても、このプロセスには長い時間がかかります。

これらのシナリオに対処するには、多くの方法があります。サーバーでデータを集計およびキャッシュするクエリーを作成し、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 という名前を付けます。

 

0

 

次に、サーバーエクスプローラーから項目をドラッグして、[請求書]ビューからのすべてのデータフィールドを取り込みます。

 

 

その後、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 に接続して以下を確認する必要があります。

  1. ユーザーがフィルタ内のすべての顧客を表示できる(現在ロードされている顧客だけでなく)。
  2. ユーザーがフィルタを変更すると、新しいデータがロードされ、要求された新しい顧客が表示される。

これらのタスクを完了する前に、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 によって考慮されます。"アクティブ" とは、フィールドが RowFieldsColumnFieldsValueFields、または 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 ビュー」を参照してください。