DataSource for Entity Framework for WinForms
コードでのデータソースの操作
設計時の機能 > コードでのデータソースの操作

ここまでは、ほとんどコードを記述せずに、デザインサーフェスで直接データソースを設定してきました。これらの設定は DataSource for Entity Framework によってたいへん簡単になりましたが、すべてをコードで行いたい場合もあります。C1DataSource はこれも可能にします。これまでに行ったことはすべて、実行時にコードで行うことができます。

その取りかかりとしてわかりやすい方法は、C1DataSourceViewSourceCollection の要素として実質的にデザイナで設定してきた ClientViewSource オブジェクトを C1DataSource なしで独自に作成できるなら、これを使用することです。ただし、一歩下がって、下位レベルのクラス ClientView<T> を使用することもできます。これにより、サーバーからのデータのロードを完全に制御できます。また、このクラスは C1.LiveLinq.LiveViews.View<T> から派生しているため、任意の LiveLinq 演算子を適用できます。データソースを View<T> に設定できる任意の GUI コントロールに対してこのクラスを連結できるということは、完全に編集可能なデータビューが手に入るということでもあります。

サーバー側のフィルタ処理は、おそらく最もよく使用されている操作です。普通、データベーステーブル全体を無制限のままクライアントに提供しようとは誰も考えないからです。先ほどは、C1DataSource を使用してこれをコードなしで簡単に実行できることを確認しましたが、ここでは実行時コードを使用します。

C1DataSource なしの実行時コードでクライアント側のデータキャッシュを使用するには、プロジェクトのメインクラスに数行を追加して、グローバルなクライアント側データキャッシュを作成します。C1DataSource を使用する場合、これはバックグラウンドで作成されました。今回は、次のコードを使用して明示的に作成します。

コードのコピー
Imports C1.Data.Entities
 Public Class Program
     Public Shared ClientCache As EntityClientCache
     Public Shared ObjectContext As NORTHWNDEntities
     <STAThread()> _
     Shared Sub Main()
         ObjectContext = New NORTHWNDEntities
         ClientCache = New EntityClientCache(ObjectContext)
         Application.EnableVisualStyles()
         Application.SetCompatibleTextRenderingDefault(False)
         Application.Run(New MainForm())
     End Sub
 End Class
コードのコピー
using C1.Data.Entities;
 static class Program
 {
     public static EntityClientCache ClientCache;
     public static NORTHWNDEntities ObjectContext;
     [STAThread]
     static void Main()
     {
         ObjectContext = new NORTHWNDEntities();
         ClientCache = new EntityClientCache(ObjectContext);
         Application.EnableVisualStyles();
         Application.SetCompatibleTextRenderingDefault(false);
         Application.Run(new MainForm());
 }

このコードは、アプリケーションレベルの(静的な)ObjectContext を1つ作成し、それを EntityClientCache に関連付けます。先に「クライアントデータキャッシュの能力」トピックで説明したように、アプリケーション全体で1つのコンテキスト(およびキャッシュ)を保持できることは、C1DataSource によって可能になった大幅な簡略化です。

実行時コードでサーバー側のフィルタ処理を実行するには、次の手順に従います。

  1. ビューのカスタマイズ」の例で作成したプロジェクトを使用して、新しいフォームを追加します。
  2. フォームにグリッド(dataGridView1)、コンボボックス(comboBox1)、およびボタン(btnSaveChanges)を追加します。
  3. フォームクラスに次のコードを追加します。
    コードのコピー
    Imports C1.Data.Entities
     Imports C1.Data
     Public Class DataSourcesInCode
         Private _scope As EntityClientScope
         Public Sub New()
             InitializeComponent()
             _scope = Program.ClientCache.CreateScope()
             Dim viewCategories As ClientView(Of Category) = _scope.GetItems(Of Category)()
             comboBox1.DisplayMember = "CategoryName"
             comboBox1.ValueMember = "CategoryID"
             comboBox1.DataSource = viewCategories
             BindGrid(viewCategories.First().CategoryID)
         End Sub
         Private Sub BindGrid(categoryID As Integer)
             dataGridView1.DataSource =
                     (From p In _scope.GetItems(Of Product)().AsFiltered(Function(p As Product) p.CategoryID.Value = categoryID)
                      Select New With
                      {
                          p.ProductID,
                          p.ProductName,
                          p.CategoryID,
                          p.Category.CategoryName,
                          p.SupplierID,
                          .Supplier = p.Supplier.CompanyName,
                          p.UnitPrice,
                          p.QuantityPerUnit,
                          p.UnitsInStock,
                          p.UnitsOnOrder
                      }).AsDynamic()
         End Sub
    
         Private Sub btnSaveChanges_Click(sender As System.Object, e As System.EventArgs) Handles btnSaveChanges.Click
             Program.ClientCache.SaveChanges()
         End Sub
         Private Sub comboBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles comboBox1.SelectedIndexChanged
             If comboBox1.SelectedValue IsNot Nothing Then
                 BindGrid(CType(comboBox1.SelectedValue, Integer))
             End If
         End Sub
     End Class
    
    コードのコピー
    namespaceTutorialsWinForms
     {
         using C1.Data.Entities;
         using C1.Data;
         public partial class DataSourcesInCode : Form
         {
             private EntityClientScope _scope;
             public DataSourcesInCode()
             {
                 InitializeComponent();
                 _scope = Program.ClientCache.CreateScope();
                 ClientView<Category> viewCategories =_scope.GetItems<Category>();
                 comboBox1.DisplayMember = "CategoryName";
                 comboBox1.ValueMember = "CategoryID";
                 comboBox1.DataSource = viewCategories;
                 BindGrid(viewCategories.First().CategoryID);
             }
             private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
             {
                 if (comboBox1.SelectedValue != null)
                     BindGrid((int)comboBox1.SelectedValue);
             }
             private void BindGrid(int categoryID)
             {
                 dataGridView1.DataSource =
                     (from p in _scope.GetItems<Product>().AsFiltered(
                          p => p.CategoryID == categoryID)
                      select new
                      {
                          p.ProductID,
                          p.ProductName,
                          p.CategoryID,
                          CategoryName = p.Category.CategoryName,
                          p.SupplierID,
                          Supplier = p.Supplier.CompanyName,
                          p.UnitPrice,
                          p.QuantityPerUnit,
                          p.UnitsInStock,
                          p.UnitsOnOrder
                      }).AsDynamic();
             }
             private void btnSaveChanges_Click(object sender, EventArgs e)
             {
                 Program.ClientCache.SaveChanges();
             }
         }
     }
    
  4. アプリケーションを保存、ビルド、および実行します。「サーバー側のフィルタ処理」の例と同じ結果が得られますが、今回は、すべてをコードで実装した点が異なります。

それでは、先ほど記述したコードを見ていきます。

プライベートフィールド _scope は、フォームからグローバルデータキャッシュへのゲートウェイになります。C1DataSource コンポーネントを直接使用する場合はこれが自動的に行われるため、直接使用しない場合はこのパターンに準拠することが推奨されます。これにより、フォームが有効な間、フォームが必要とするエンティティはキャッシュに留まり、それらのエンティティを保持するすべてのフォーム(スコープ)が解放されると、エンティティも自動的に解放されます。

コンボボックスにすべてのカテゴリを表示するビューを作成することは簡単です。

コードのコピー
Dim viewCategories As ClientView(Of Category) = _scope.GetItems(Of Category)()
コードのコピー
ClientView<Category> viewCategories = _scope.GetItems<Category>();

ビューをグリッドに連結し、コンボボックスで選択されたカテゴリに関連する製品だけを提供するには、追加の演算子 AsFiltered(<predicate>) が必要です。

コードのコピー
From p In _scope.GetItems(Of Product)().AsFiltered(Function(p As Product) p.CategoryID.Value = categoryID)
コードのコピー
from p in _scope.GetItems<Product>().AsFiltered(p => p.CategoryID == categoryID)
   

このクエリーが実行されても、要求された製品を取得するために必ずしもサーバーへのラウンドトリップが必要にならないことに注意してください。この要求データがこのフォームまたはアプリケーション内の別のフォームから以前に要求されたことがあるために、既にキャッシュに含まれていないかどうかが最初に調べられます。または、アプリケーション内のどこかで完全に別のクエリーが実行されて、すべての製品を返すように要求されたため、すべての製品データがキャッシュに既に含まれている場合もあります。これも、C1DataSource の基本的な長所の1つです。アプリケーションにデータのグローバルキャッシュを提供することで、アプリケーションの全ライフタイムを通してパフォーマンスが継続的に改善されます。

ここでは、ユーザーがコンボボックスで新しいカテゴリを選択するたびに(コンボボックスの SelectedValueChanged イベントを参照)、新しいビューを作成し、そこにグリッドを連結することにしました。ただし、いつも新しいビューを作成するのではなく、特別な BindFilterKey を使用してビューを1つ作成するだけで済ますこともできます。これについては、「MVVM の簡略化」で詳しく説明します。

要するに、ここでは、「サーバー側のフィルタ処理」で C1DataSource を使用してデザインサーフェス上で行った作業をコードでも繰り返したということです。さらに、少しばかり手を加えて、「ビューのカスタマイズ」で LiveLinq ステートメントに Select を追加して行ったように、グリッド列に表示されるフィールドをカスタマイズしました。

コードのコピー
Select New With
 {
     p.ProductID,
     p.ProductName,
     p.CategoryID,
     p.Category.CategoryName,
     p.SupplierID,
     Supplier = p.Supplier.CompanyName,
     p.UnitPrice,
     p.QuantityPerUnit,
     p.UnitsInStock,
     p.UnitsOnOrder
 }
コードのコピー
select new
{
     p.ProductID,
     p.ProductName,
     p.CategoryID,
     CategoryName = p.Category.CategoryName,
     p.SupplierID,
    Supplier = p.Supplier.CompanyName,
    p.UnitPrice,
     p.QuantityPerUnit,
     p.UnitsInStock,
     p.UnitsOnOrder
 };
    特別な書式設定なしで、生の製品データをテーブルから返すだけなら、次のように記述できます。

        select p;