ここまでは、ほとんどコードを記述せずに、デザインサーフェスで直接データソースを設定してきました。これらの設定は DataSource for Entity Framework によってたいへん簡単になりましたが、すべてをコードで行いたい場合もあります。C1DataSource はこれも可能にします。これまでに行ったことはすべて、実行時にコードで行うことができます。
その取りかかりとしてわかりやすい方法は、C1DataSource の ViewSourceCollection の要素として実質的にデザイナで設定してきた ClientViewSource オブジェクトを C1DataSource なしで独自に作成できるなら、これを使用することです。ただし、一歩下がって、下位レベルのクラス ClientView<T> を使用することもできます。これにより、サーバーからのデータのロードを完全に制御できます。また、このクラスは C1.LiveLinq.LiveViews.View<T> から派生しているため、任意の LiveLinq 演算子を適用できます。データソースを View<T> に設定できる任意の GUI コントロールに対してこのクラスを連結できるということは、完全に編集可能なデータビューが手に入るということでもあります。
サーバー側のフィルタ処理は、おそらく最もよく使用されている操作です。普通、データベーステーブル全体を無制限のままクライアントに提供しようとは誰も考えないからです。先ほどは、C1DataSource を使用してこれをコードなしで簡単に実行できることを確認しましたが、ここでは実行時コードを使用します。
C1DataSource なしの実行時コードでクライアント側のデータキャッシュを使用するには、プロジェクトのメインクラスに数行を追加して、グローバルなクライアント側データキャッシュを作成します。C1DataSource を使用する場合、これはバックグラウンドで作成されました。今回は、次のコードを使用して明示的に作成します。
| VisualBasic |
コードのコピー
|
|---|---|
Imports C1.Data.Entities
Class Application
Public Shared ClientCache As EntityClientCache
Private Sub Application_Startup(sender As Object, e As
System.Windows.StartupEventArgs) Handles Me.Startup
ClientCache = EntityClientCache.GetDefault(GetType(NORTHWNDEntities))
End Sub
End Class
|
|
| C# |
コードのコピー
|
|---|---|
using C1.Data.Entities; public partial class App : Application { public static EntityClientCache ClientCache; public static NORTHWNDEntities ObjectContext; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); ObjectContext = new NORTHWNDEntities(); ClientCache = new EntityClientCache(ObjectContext); } } |
|
このコードは、アプリケーションレベルの(静的な)ObjectContext を1つ作成し、それを EntityClientCache に関連付けます。先に「クライアントデータキャッシュの能力」トピックで説明したように、アプリケーション全体で1つのコンテキスト(およびキャッシュ)を保持できることは、C1DataSource によって可能になった大幅な簡略化です。
実行時コードでサーバー側のフィルタ処理を実行するには、次の手順に従います。
| VisualBasic |
コードのコピー
|
|---|---|
Imports C1.Data.Entities
Imports C1.Data
Public Class DataSourcesInCode
Private _scope As EntityClientScope
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_scope = Application.ClientCache.CreateScope()
Dim viewCategories As ClientView(Of Category) = _scope.GetItems(Of Category)()
comboBox1.DisplayMemberPath = "CategoryName"
comboBox1.ItemsSource = viewCategories
BindGrid(viewCategories.First().CategoryID)
End Sub
Private Sub BindGrid(categoryID As Integer)
dataGrid1.ItemsSource =
(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 comboBox1_SelectionChanged(sender As System.Object, e
As System.Windows.Controls.SelectionChangedEventArgs) Handles comboBox1.SelectionChanged
If comboBox1.SelectedValue IsNot Nothing Then
BindGrid(CType(comboBox1.SelectedValue, Category).CategoryID)
End If
End Sub
Private Sub btnSaveChanges_Click(sender As System.Object, e As
System.Windows.RoutedEventArgs) Handles btnSaveChanges.Click
Application.ClientCache.SaveChanges()
End Sub
End Class
|
|
| C# |
コードのコピー
|
|---|---|
using C1.Data.Entities; using C1.Data; public partial class DataSourcesInCode : Window { private EntityClientScope _scope; public DataSourcesInCode() { InitializeComponent(); _scope = App.ClientCache.CreateScope(); ClientView<Category> viewCategories = _scope.GetItems<Category>(); comboBox1.DisplayMemberPath = "CategoryName"; comboBox1.ItemsSource = viewCategories; BindGrid(viewCategories.First().CategoryID); } private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (comboBox1.SelectedValue != null) BindGrid(((Category)comboBox1.SelectedValue).CategoryID); } private void BindGrid(int categoryID) { dataGrid1.ItemsSource = (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, RoutedEventArgs e) { App.ClientCache.SaveChanges(); } } |
|
それでは、先ほど記述したコードを見ていきます。
プライベートフィールド _scope は、フォームからグローバルデータキャッシュへのゲートウェイになります。C1DataSource コンポーネントを直接使用する場合はこれが自動的に行われるため、直接使用しない場合はこのパターンに準拠することが推奨されます。これにより、フォームが有効な間、フォームが必要とするエンティティはキャッシュに留まり、それらのエンティティを保持するすべてのフォーム(スコープ)が解放されると、エンティティも自動的に解放されます。
コンボボックスにすべてのカテゴリを表示するビューを作成することは簡単です。
| VisualBasic |
コードのコピー
|
|---|---|
Dim viewCategories As ClientView(Of Category) = _scope.GetItems(Of Category)() |
|
| C# |
コードのコピー
|
|---|---|
ClientView<Category> viewCategories = _scope.GetItems<Category>(); |
|
ビューをグリッドに連結し、コンボボックスで選択されたカテゴリに関連する製品だけを提供するには、追加の演算子 AsFiltered(<predicate>) が必要です。
| VisualBasic |
コードのコピー
|
|---|---|
From p In _scope.GetItems(Of Product)().AsFiltered(Function(p As Product) p.CategoryID.Value = categoryID) |
|
| C# |
コードのコピー
|
|---|---|
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;