原典:Web Forms Model Binding Part 1: Selecting Data (ASP.NET vNext Series)
ASP.NET vNextのブログ投稿シリーズで、今回は3回目になります。
vNextリリースの.NETおよびVisual Studioには、新機能が非常に多く含まれています。ASP.NET vNextでは、WebフォームとMVC、またそのどちらもの基盤となっているASP.NETコアも含め、非常に素晴らしい改善点が多く見られます。
Webフォームの新しいモデルバインディングサポートについて、次週に渡り3つの投稿を行いますが、本日の投稿はその1回目になります。モデルバインディングは、ASP.NET Webフォームにある既存のデータバインディングシステムの拡張で、コードにフォーカスしたデータアクセスの枠組みを提供します。ASP.NET MVCで最初に導入したモデルバインディング概念の大部分を利用し、Webフォームのサーバーコントロールモデルと上手く統合できます。
現在のデータバインディングの背景
Webフォームでは、数多くのデータソースコントロール (SqlDataSource、EntityDataSource、LinqDataSourceなど)が含まれており、直接データソースにサーバーサイドコントロールを宣言的に接続できるようになっています。しかし、多くの開発者は、データアクセスのロジックを完全に制御したり、コードを使用してロジックを書いたりすることを好みます。
こういったことを、以前のWebフォームのバージョンでは、コントロールのDataSourceプロパティを直接設定して、そのDataBindメソッドをページのコードビハインドから呼び出して行っていました。これは多くのシナリオで上手くいきますが、ソート、ページング、編集などの自動処理をサポートしている、よりリッチなデータコントロール(GridViewなど)では上手く動作しません。
現在利用できる別のオプションは、ObjectDataSourceコントロールの使用です。このコントロールは、データアクセス層から、UIコードをよりクリーンに分別し、データコントロールがページングやソートなどの自動機能を提供できるようにします。しかし、データの選択では上手くいっても、双方向のデータバインディングを実施した時はまだ面倒で、簡単な(複雑な型の”深い”バインディングのない)プロパティだけしかサポートしなかったり、(エラー検証なども含む)多くのシナリオを処理するために、多くの複雑なコードを書かなければいけなかったりします。
モデルバインディングの導入
ASP.NET vNextには、Webフォーム内で新たに”モデルバインディング”サポートが含まれます。
モデルバインディングは、リッチな双方向データバインディングフレームワークの利点も残しつつ、コードにフォーカスしたデータアクセスロジックの使用を簡略化しようとするものです。ASP.NET MVCで最初に導入されたモデルバインディングパターンが組みこまれており、またWebフォームのサーバーコントロールモデルとも上手く統合できます。Webフォームで共通のCRUDスタイルのシナリオを簡単に実施でき、またすべてのデータアクセス技術(EF、Linq to SQL、NHibernate、DataSet、生のADO.NETなど)が使用できるようになります。
新しいモデルバインディング機能の利用方法を示したウォークスルーを、今週いくつか投稿します。今回の投稿では、データ取得のためにモデルバインディングを使用する方法をお見せします。
SelectMethodを使用したデータ取得
モデルバインディングは、コードにフォーカスしたデータバインディングの方法です。ページのコードビハインドファイル内に、CRUDヘルパーメソッドを書くと、ページ内のサーバーコントロールと簡単に接続できます。サーバーコントロールは、そのページのライフサイクル内の適切な時にそのメソッドを呼び出し、データをバインドします。
簡単な例を見るために、<asp:gridview>を使いましょう。GridViewには4つの列があります。3つが標準のBoundFieldsで、4つ目がTemplateFieldです。Categoryオブジェクトにするために、GridViewのModelTypeプロパティをどのように設定しているかを確認してください。
これにより、(Evalメソッドの代わりにItem.Products.Countなど)強く型付けされたデータバインディングを、TemplateField内で有効化できます。
<asp:GridView ID="categoriesGrid" runat="server" ModelType="WebApplication1.Model.Category" SelectMethod="GetCategories" AutoGenerateColumns="false"> <Columns> <asp:BoundField DataField="CategoryID" HeaderText="ID" /> <asp:BoundField DataField="CategoryName" HeaderText="Name" /> <asp:BoundField DataField="Description" HeaderText="Description" /> <asp:TemplateField HeaderText="# of Products"> <ItemTemplate><%# Item.Products.Count %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
GridViewのSelectMethodプロパティで、ページのコードビハインドファイルにあるGetCategoriesメソッドを設定し、モデルバインディングを使用してデータを取得するように、GridViewを構成しました。このGetCategoriesメソッドは以下のようになります。
public IQueryable<Category> GetCategories() { var northwind = new Northwind(); return northwind.Categories.Include(c => c.Products); }
上記では、Northwindサンプルデータベースから、カテゴリの一覧を返すLINQクエリを実施するEFコードファーストを使用しています。コードビハインドファイル内で、データベースクエリを実施する必要はありません。私はこれを、リポジトリやデータアクセス層内でも実施できましたし、またGetCategoriesヘルパーメソッドを使用するだけでそのコントロールに接続することもできました。
ページを実行すると、GridViewは上記のメソッドを呼び出して、自動的にデータを取得し、以下のようにページを描画していきます。
N+1選択の回避
上記のコードで1つお分かりになるかと思いますが、LINQクエリで.Include(c=>c.Products)ヘルパー式を使用しています。これは、EFに対して、その式を修正するように伝えることで、Category情報を取得するだけでなく、Products関係も含めるようにしています(各行が返る度にこの情報を取得する別の呼び出しが行われないようにするためです)。