DataSource for Entity Framework for WinForms
LiveLinq による LINQ クエリーの最適化
C1LiveLinq > LiveLinq の開始 > LiveLinq による LINQ クエリーの最適化

このセクションでは、where 演算子を使用してデータをフィルタ処理するクエリーを LiveLinq で最適化する方法について、サンプルを使用して説明します。

最初に、次の手順を実行します。

  1. 新しい WinForms プロジェクトを作成します
  2. C1.LiveLinq.dll アセンブリへの参照を追加します
  3. [データ]→[新しいデータソースの追加]メニューを使用して、NORTHWND.MDF データベースへの参照を追加します。ウィザードに表示されるデフォルトのオプションをすべて受け入れ、さらにデータベース内のテーブルをすべて選択します。

この時点では、プロジェクトは次のようになります。

次に、フォームをダブルクリックし、次のコードを追加します。

C#
コードのコピー
// northwind データセットを宣言します
NORTHWNDDataSet _ds = new NORTHWNDDataSet();
private void Form1_Load(object sender, EventArgs e)
{
  // データセットにデータをロードします
  new NORTHWNDDataSetTableAdapters.CustomersTableAdapter()
    .Fill(_ds.Customers);
  new NORTHWNDDataSetTableAdapters.OrdersTableAdapter()
    .Fill(_ds.Orders);
}
  このコードは ADO.NET DataSet を宣言し、そのデータセットに一部のデータをロードします。

データを取得できたので、そのデータを操作します。

ボタンをフォームに追加してダブルクリックし、次のコードを入力します。

C#
コードのコピー
private void button1_Click(object sender, EventArgs e)
{
  // ソースデータへの参照を取得します
  var customers = _ds.Customers;
  var orders = _ds.Orders;
  // 最初の顧客の全注文を検索します
  var q =
    from o in orders
    where o.CustomerID == customers[0].CustomerID
    select o;
  // クエリーを評価します(1000 回実行)
  var start = DateTime.Now;
  int count = 0;
  for(int i = 0; i < 1000; i++)
  {
    foreach (var d in q)
      count++;
  }
  Console.WriteLine("LINQ query done in {0} ms",
     DateTime.Now.Subtract(start).TotalMilliseconds);
}
    このコードは、データベース内の最初の顧客の全注文を列挙する単純な LINQ クエリーを作成し、そのクエリーを 1000 回実行して、処理にかかった時間を報告します。

ここで、プロジェクトを実行してボタンをクリックすると、Visual Studio の出力ウィンドウに次のように表示されます。

LINK query done in 262 ms

次に、LiveLinq を使用して、このクエリーを最適化します。

最初に、次の using 文をコードの先頭に追加します。

C#
コードのコピー
using C1.LiveLinq;    

using C1.LiveLinq.AdoNet;

次に、2つ目のボタンをフォームに追加してダブルクリックし、次のコードを入力します。

C#
コードのコピー
private void button2_Click(object sender, EventArgs e)
{
  // ソースデータへの参照を取得します
  var customers = _ds.Customers;
  var orders = _ds.Orders; 
  // 最初の顧客の全注文を検索します
  var q = 
    from o in orders.AsIndexed()
    where o.CustomerID.Indexed() == customers[0].CustomerID
    select o; 
  // クエリーを評価します(1000 回実行)
  var start = DateTime.Now; 
  int count = 0;
  for(int i = 0; i < 1000; i++)
  { 
    foreach (var d in q)
      count++; 
  }
  Console.WriteLine("LiveLinq query done in {0} ms",
     DateTime.Now.Subtract(start).TotalMilliseconds);
}

 コードは、前に使用したプレーンな LINQ バージョンとほぼ同じですが、以下の点だけが異なります。

また、次の点も異なります。

プロジェクトを再度実行し、両方のボタンを何回かクリックすると、次のようなメッセージが表示されます。

LINQ query done in 265 ms

LINQ query done in 305 ms

LINQ query done in 278 ms

LiveLinq query done in 124 ms

LiveLinq query done in 7 ms

LiveLinq query done in 2 ms

プレーンな LINQ クエリーにかかる時間は、常にほぼ同じであることがわかります。一方で、LiveLinq クエリーの速度は、初回からプレーンな LINQ クエリーの約2倍になっています。また、その後の実行では、速度が約 100 倍になっています。この種のクエリーで、この程度のパフォーマンスが得られることは普通です。

LiveLinq クエリーでは、初回実行時にインデックスを構築する必要があるため、その速度は前述のようになります。その後は、同じインデックスが再利用されるため、パフォーマンスが劇的に向上します。インデックスは自動的に管理されます。このため、基底のデータが変更された場合でも、インデックスは常に最新の状態に保たれます。

この例では、Indexed メソッドが最初に実行されたときにインデックスが作成されました。特別な制御が必要な場合は、コードを使用してインデックスを管理することもできます。たとえば、次のコードは、インデックス付きのコレクションを宣言し、CustomerID フィールドに明示的にインデックスを追加します。

C#
コードのコピー
  var ordersIndexed = _ds.Orders.AsIndexed();    
  ordersIndexed.Indexes.Add(o => o.CustomerID);

インデックスがソースデータに関連付けられており、コレクションがスコープから外れても、インデックスが維持されることに注意してください。上のコードを一旦実行すると、ordersIndexed コレクションがスコープから外れた場合でも、インデックスは有効なままです。

また、AsIndexed メソッドは、変更通知を提供するコレクションでのみサポートされることに注意してください。LiveLinq は、インデックスを維持するために変更を監視する必要があります。WinForms や WPF のデータ連結で一般的に使用されているコレクションでは、すべてこの要件が満たされています。特に、DataTableDataViewBindingList<T>ObservableCollection<T>、および LINQ to XML のコレクションでは AsIndexed を使用できます。

LiveLinq インストールに付属する LiveLinqQueries という名前のサンプルには、さまざまなベンチマークの例と、プレーンな CLR オブジェクト、ADO.NET、および XML データに対するクエリーが含まれています。

まとめると、LiveLinq を使用することで、ソート可能な任意のタイプのデータを検索するクエリーを最適化することができます。たとえば、特定の顧客や製品を検索したり、特定の価格帯の製品をリストするクエリーです。これらは標準的なクエリーですが、簡単に最適化して、プレーンな LINQ を使用した場合より 100 倍高速に実行することができます。