ページ

2012年10月29日月曜日

◆RIAサービスを使ってデータ表示2

前回MTG Blog: ◆RIAサービスを使ってデータ表示(VS2012)の続き。

<データの表示をカスタマイズする>

カスタムクエリを追加するには

  1. RIA1.Webプロジェクトの「MyService.cs」を開き、以下の「GetEmployeesInLongon」メソッドを追加する。
    1. public IQueryable<Employees> GetEmployeesInLondon()
    2. {
    3.     return this.ObjectContext.Employees.Where(e => e.City == "London");
    4. }
  2. ビルド
  3. RIA1プロジェクトの「EmployeeList.xaml.cs」を開き「GetEmployeesQuery」メソッドを今作成した「GetEmployeesInLondonQuery」メソッドに置き換える。
    1. public EmployeeList()
    2. {
    3.     InitializeComponent();
    4.     this.dataGrid1.ItemsSource = _MyContext.Employees;
    5.     //_MyContext.Load(_MyContext.GetEmployeesQuery());
    6.     _MyContext.Load(_MyContext.GetEmployeesInLondonQuery());
    7.  
    8. }
  4. これで実行して「EmployeeList」を開くと以下のように「London」のみのデータが表示される。
    image

「DomainDataSource」コントロールを使ってデータをロードする

これまではコードビハインドへのコーディング追加によって、「Employee」テーブルの内容をロードしていたが、「DomainDataSource」なるコントロールを使ってXAMLのみでデータをロードする方法が用意されている。(GUIでクエリーデータソースをドラッグアンドドロップなどして自動生成させるとVisualStudioはこいつを使うようである)

「DomainDataSource」を使ったデータ読み込みの方法を試してみる。

  1. RIA1プロジェクトの「EmployeeList.xaml.cs」を開きデータロードのロジックをコメントアウト。
    1. public EmployeeList()
    2. {
    3.     InitializeComponent();
    4.     //this.dataGrid1.ItemsSource = _MyContext.Employees;
    5.     //_MyContext.Load(_MyContext.GetEmployeesQuery());
    6.     //_MyContext.Load(_MyContext.GetEmployeesInLondonQuery());
    7.  
    8. }
  2. RIA1プロジェクトの「EmployeeList.xaml」を開き、ツールボックスからデザインビューの「DataGrid」のすぐ上に「DomainDataSource」コントロールをドラッグする。
    (「DomainDataSource」自体はユーザーに何かしらを表示するコントロールではないのでデザイナー画面には表示されない。その昔、コンポーネントなどと呼ばれていた類のコントロール)
  3. 「DomainDataSource」のプロパティを以下のように設定し、「GetEmployeesQuery」によってデータを自動的に取得させる。
    1. <riaControls:DomainDataSource Name="employeeDataSource" QueryName="GetEmployeesQuery" AutoLoad="True" >
    2.     <riaControls:DomainDataSource.DomainContext>
    3.         <ds:MyContext/>
    4.     </riaControls:DomainDataSource.DomainContext>
  4. 「DataGrid」の「ItemsSource」に「DomainDataSource」コントロールの「Data」プロパティをバインディングする。
    1. <sdk:DataGrid AutoGenerateColumns="True" IsReadOnly="True" x:Name="dataGrid1" MinHeight="100" ItemsSource="{Binding Data, ElementName=employeeDataSource}" />

「DomainDataSource」に手動で読み込む

「DomainDataSource」には「AutoLoad」を「False」にして手動で読み込む方法も用意されているので、それを使ってみる。

  1. 「DomainDataSource」の「AutoLoad」を「False」にする。
  2. 「TextBlock」の次にデータロード用のボタンを追加する。
    「DomainDataSource」コントロールには予め「LoadCommand」が用意されているようなので、それを「Command」にバインドしてあげれば良さそうだ。
    1. <Button Width="100" HorizontalAlignment="Left" Content="Load" Command="{Binding LoadCommand, ElementName=employeeDataSource}"/>

「DomainDataSource」に抽出条件を指定する

「DomainDataSource」に画面から抽出条件を指定して表示できるようにする。

  1. 「RIA1.Web」プロジェクトの「MyService.cs」に以下の抽出メソッドを追加する。
    1. public IQueryable<Employees> GetEmployeesByCity(string cityname)
    2. {
    3.     return this.ObjectContext.Employees.Where(e => e.City == cityname);
    4. }
  2. 「RIA1」プロジェクトの「EmployeeList.xaml」のLoadボタンをStackPanelに入れ、抽出条件指定用のTextBlockを追加する。
    image
    1. <StackPanel Orientation="Horizontal">
    2.     <TextBlock Text="CityName : " />
    3.     <TextBox Name="txtCity" Width="100" Height="20"/>
    4.     <Button Width="100" HorizontalAlignment="Left" Content="Load" Command="{Binding LoadCommand, ElementName=employeeDataSource}"/>
    5. </StackPanel>

  3. 「DomainDataSource」コントロールに「QueryParameters」プロパティを追加し、上記の抽出条件指定用TextBlockとバインドする。
    1. <riaControls:DomainDataSource Name="employeeDataSource" QueryName="GetEmployeesByCityQuery" AutoLoad="False" >
    2.     <riaControls:DomainDataSource.DomainContext>
    3.         <ds:MyContext/>
    4.     </riaControls:DomainDataSource.DomainContext>
    5.     <riaControls:DomainDataSource.QueryParameters>
    6.         <riaControls:Parameter ParameterName="cityname" Value="{Binding Text, ElementName=txtCity}"/>
    7.     </riaControls:DomainDataSource.QueryParameters>
    8. </riaControls:DomainDataSource>
  4. これで実行し、抽出条件に「London」と入力して「Load」ボタンを押すと「London」のデータだけが表示される。
    image

「DomainDataSource」にソート条件を指定する

  1. 「DomainDataSource」には「SortDescriptors」属性を指定することによって簡単にソート条件を追加することができる。
    1. <riaControls:DomainDataSource Name="employeeDataSource" QueryName="GetEmployeesByCityQuery" AutoLoad="False" >
    2.     <riaControls:DomainDataSource.DomainContext>
    3.         <ds:MyContext/>
    4.     </riaControls:DomainDataSource.DomainContext>
    5.     <riaControls:DomainDataSource.QueryParameters>
    6.         <riaControls:Parameter ParameterName="cityname" Value="{Binding Text, ElementName=txtCity}"/>
    7.     </riaControls:DomainDataSource.QueryParameters>
    8.     <riaControls:DomainDataSource.SortDescriptors>
    9.         <riaControls:SortDescriptor PropertyPath="LastName" Direction="Ascending" />
    10.     </riaControls:DomainDataSource.SortDescriptors>
    11. </riaControls:DomainDataSource>
  2. ソート指定した項目は先頭カラムに表示される。
    image

「DomainDataSource」にページング処理を追加する

  1. 「DomainDataSource」のクエリーを「GetEmployeesQuery」に戻して、追加した「QueryParameters」を削除する。
  2. ツールボックスから「DataGrid」の下に「DataPager」コントロールをドロップし、XAMLを以下のように指定する。
    1. <sdk:DataPager Source="{Binding Data, ElementName=employeeDataSource}" PageSize="4" HorizontalAlignment="Left"/>
  3. これで実行して「EmployeeList」を表示させると4行ごとにページングされて表示されるのが判る。
    image

「DomainDataSource」にフィルターを追加する

「DomainDataSource」に抽出条件を指定する、ではサーバーに対して抽出処理をやり直したが、今度はクライアント側だけでフィルター処理を行う。

  1. 「DomainDataSource」に以下のように「FilterDescriptors」属性を追加する。
    1. <riaControls:DomainDataSource.FilterDescriptors>
    2.     <riaControls:FilterDescriptor
    3.          PropertyPath="City"
    4.          Operator="IsEqualTo"
    5.          IgnoredValue=""
    6.          Value="{Binding ElementName=txtCity, Path=Text}" >
    7.     </riaControls:FilterDescriptor>
    8. </riaControls:DomainDataSource.FilterDescriptors>
  2. 実行して「EmployeeList」を表示させる。
    image
  3. 「CityName」テキストボックスにフィルター対象の「City」を指定して「Load」ボタンを押すとフィルターされたデータが表示される。
    image

 

<マスタ詳細ビューを作る>

「EmployeeList」の一覧表示ができたので、次は一覧で選択したデータを詳細表示してみる。詳細表示には「Silverlith Toolkit」に含まれている「DataForm」コントロールを使用する。

「DataForm」コントロールの使い方は以下に簡単に纏めている。
MTG Blog: ◆DataFormコントロールを使ってみる

DataFormを追加するには

  1. 「RIA1」プロジェクトから「EmployeeList.xaml」を開く
  2. 「DataForm」用の名前空間を追加する
    1. xmlns:dataForm="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
  3. 「DataPager」コントロールの下に以下のXAMLを追加して「DataForm」コントロールを追加する。
       1: <dataForm:DataForm x:Name="dataForm1" Header="Employee Infomation" 
       2:                    AutoGenerateFields="True" HorizontalAlignment="Left" 
       3:                    AutoEdit="False" AutoCommit="True" Height="400" Width="800"
       4:                    CurrentItem="{Binding SelectedItem,ElementName=dataGrid1}" 
       5:                    Margin="0,12,0,0" />

  4. アプリケーションを実行し「EmployeeList」を表示したのちに一覧でデータを選択すると、その下の「DataForm」に詳細が表示される。
    image

<関連データを表示する>


OrderとOrder_Detailsテーブルを使って親子関係(集約)のあるデータを表示してみる。


関連テーブルのデータを表示するには



  1. 「RIA1」プロジェクトの「Views」フォルダーを右クリックして「追加」「新しい項目」をクリックする。
  2. 「Orders」という名前で「Silverlight」ページを追加する。
  3. 「Orders.xaml」を開く。
  4. <Grid>タグの間に以下のXAMLを追加する。





    1. <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}" >

    2.     <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">

    3.         <TextBlock Text="Orders" Style="{StaticResource HeaderTextStyle}"/>

    4.     </StackPanel>

    5. </ScrollViewer>





  5. 「MainPage.xaml」を開く。
  6. 「EmployeeList」のリンクの次に「Orders」ページへのリンクを追加する。





    1. <HyperlinkButton x:Name="Link5" Style="{StaticResource LinkStyle}" NavigateUri="/Orders" TargetName="ContentFrame" Content="Orders"/>

    2. <Rectangle x:Name="Divider4" Style="{StaticResource DividerStyle}"/>





  7. 「RIA1.Web」プロジェクトの「MyService.metadata.cs」を開く。
  8. 「OrdersMetadata」クラスの「Order_Details」プロパティに「Include」属性と「Composition」属性を追加する。
    (この属性により、Order_Detailsのコレクションもインスタンシングされるのかな)
  9. 「RIA1.Web」プロジェクトの「MyService.cs」を開く。
  10. 「GetOrders」メソッドを修正して、項番8のコレクションを作る元ネタ用に、関連する「Order_Details」も読み込む指定に変更する。





    1. public IQueryable<Orders> GetOrders()

    2. {

    3.     //return this.ObjectContext.Orders;

    4.     return this.ObjectContext.Orders.Include("Order_Details").OrderBy(p => p.OrderID);

    5. }





  11. 「Orders.xaml」を開く。
  12. 「データ」メニューの「データソースの表示」をクリックして「データソースウィンドウ」を開く。
  13. 「Orders」をデザイン画面にドラッグする。
    image
    「Orders」の列を含む「DataGrid」が表示される。
  14. 同様に、「Orders」のノードの中に含まれる「Order_Details」を「Orders」の「DataGrid」の下にドラッグする。
    image
    「Order_Details」の列を含む「DataGrid」が表示される。
  15. 追加された「DataGrid」の「Width」プロパティを削除し、画面の幅いっぱいに表示されるようにする。
  16. それぞれの「DataGrid」の前に「TextBlock」コントロールを追加して以下のようにラベルが表示されるようにする。


       1: <TextBlock Text="Order見出し" />

       1: <TextBlock Text="Order明細" />

    image


  17. 「DomainDataSource」にフィルターを追加して取得されるデータ数を制限しておく。


       1: <riaControls:DomainDataSource.FilterDescriptors>
       2:     <riaControls:FilterDescriptor PropertyPath="EmployeeID" Operator="IsLessThan" Value="5" />
       3: </riaControls:DomainDataSource.FilterDescriptors>

     



  18. アプリケーションを実行し、「Orders」を開く。

  19. 「Order見出し」の行を選択すると対応する明細データが「Order明細」に表示される。
    image


関連テーブルのデータを表示するには2


上記では「データソース」をドロップすることによって半自動的に画面デザインを行ったが、実装を確認する意味で中身を確認しながら手動で設定してみる。



  1. 「RIA1」プロジェクトの「Views」フォルダーを右クリックして「追加」「新しい項目」をクリックする。
  2. 「注文」という名前で「Silverlight」ページを追加する。
  3. 「注文.xaml」を開く。
  4. <Grid>タグの間に以下のXAMLを追加する。


       1: <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
       2:  
       3:     <TextBlock Text="注文" Style="{StaticResource HeaderTextStyle}"/>
       4: </StackPanel>

  5. 「MainPage.xaml」を開く。
  6. 「EmployeeList」のリンクの次に「注文」ページへのリンクを追加する。


       1: <HyperlinkButton x:Name="Link4" Style="{StaticResource LinkStyle}" NavigateUri="/注文" TargetName="ContentFrame" Content="注文"/>
       2:  
       3:  <Rectangle x:Name="Divider3" Style="{StaticResource DividerStyle}"/>

  7. 「注文.xaml」を開き「注文」タイトルの下に「注文タイトル」の「TextBlock」を追加


       1: <TextBlock Text="注文タイトル" />

  8. その下に「DtaGrid」を追加し、以下のように設定。


       1: <sdk:DataGrid x:Name="注文DataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Data,ElementName=注文DomainDataSource}"  Height="200" IsReadOnly="True"/>

  9. その下に「注文明細」の「TextBlock」を追加


       1: <TextBlock Text="注文明細" />

  10. その下に「DataGrid」を追加し、以下のように設定。


       1: <sdk:DataGrid x:Name="注文明細DataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Data.Order_Details, ElementName=注文DomainDataSource}"  Height="200" />

  11. これで実行して、「注文」ページを表示させると「Orders」ページとほぼ同様の表示となる。
    image


2012年10月26日金曜日

◆RIAサービスを使ってデータ表示(VS2012)


RIAサービスに対する情報(リソース)はあまり多くはない。
探してみるとだいたい以下の記事くらいしかヒットしない。

WCF RIAServicesで業務アプリケーションをよりシンプルに(1/5):CodeZine
WCF RIA Servicesの仕組み(1/4):CodeZine

この記事を参考にしつつ、本家MSDNのチュートリアルを試してみる。(自分なりの適当な解釈で纏めている)
http://msdn.microsoft.com/ja-jp/library/ff713719(v=vs.91).aspx
(この記事自体はSilveright4,VS2010前提のようだ)

このチュートリアルでは「AdventureWorks」を使っているが、こいつはデカくて使いづらいので定番の「Northwnd」を使う。
(予めサーバーエクスプローラで接続の追加を行っておく。)

ちなみに、VS2012ではSQLServer(Express)無しにデータベースファイル(mdf)を使えるようになっているが、「Northwnd」を使うには以下の手順でデータベースファイルのアップグレードが必要になる。
MTG Blog: ◆VisualStudio2012でNorthwindを使う

<ソリューションの作成及び設定>

新しい WCF RIA Services アプリケーションを作成するには

  1. 「Silverlight」カテゴリから「Silverlightビジネスアプリケーション」テンプレートをクリックし「RIA1」と名前を付ける
    image
  2. ソリューションエクスプローラに2つのプロジェクト(RIA1がクライアント用プロジェクト、RIA1.Webがサーバー用プロジェクト)が作られるのを確認する。
    image
  3. F5キーを押して雛形のアプリケーションが実行されるのを確認する。
    image

アプリケーションを設定するには

  1. ソリューションエクスプローラで、クライアントプロジェクトのMainPage.xamlを開く
  2. デザイン画面で「アプリケーション名」と表示されているTextBlockコントロールをクリック
    image
  3. XAMLウインドウのスクロールバーをポイントすると、カレントコントロール(TextBlock)が反転して表示される。
    image
    この定義を見ると、「ApplicationResources」クラスの「Strings.ApplicationName」プロパティにバインドしているようだ。
    (プロパティ値の実体は「ApplicationStrings.resx」から持ってきているっぽい。)
    image
    ソリューションエクスプローラで「ApplicationStrings.resx」をダブルクリックすると以下のリソース設定画面が表示されるので、ここで「ApplicationName」を「RIA1アプリケーション」に変更する。
    image
  4. ソリューションエクスプローラでViewsフォルダーを右クリックし、「追加」「新しい項目」をクリック。
  5. 「Silverlight」カテゴリから「Silverlightページ」テンプレートをクリックし、「EmployeeList.xaml」と名前を付けて「追加」ボタンをクリックする。
  6. 表示されたXAMLソースの<Grid>タグの間に以下の記述を追加する。
    1. <Grid x:Name="LayoutRoot">
    2.     <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}" >
    3.         <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
    4.             <TextBlock Text="Employee List" Style="{StaticResource HeaderTextStyle}"/>
    5.         </StackPanel>
    6.     </ScrollViewer>
    7. </Grid>
  7. 「MainPage.xaml」を開き、右上に表示されている「ホーム」と「バージョン情報」の間に以下のXAMLを追加する。
    1. <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/EmployeeList" TargetName="ContentFrame" Content="Employee List"/>
    2.  
    3. <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
  8. アプリケーションを実行し、メニューの「Employee List」をクリックして、「Employee List」画面が表示されればここまではOK。
    image

 

<データの表示>

データソースを追加するには

  1. ソリューションエクスプローラでRIA1.Webプロジェクトの右クリックから、「追加」「新しい項目」をクリック
    image
  2. 「データ」カテゴリーで「ADO.NET Entity Data Model」テンプレートを選択し、名前にNorthwnd.edmxを指定して「追加」ボタンをクリック
  3. 「Entity Data Model ウィザード」画面で「データベースから生成」を選択して「次へ」
    image
  4. 「データ接続」に「Northwnd」を指定して「次へ」
    (予めデータ接続は作ってある前提)
    image
  5. 「データベースオブジェクトの選択」画面でテーブルを選択して「完了」
    (Employees,Orders,Order Details)
    image
  6. 追加されたテーブルがダイアグラムで表示されるので、空白部分を一旦クリックして選択してから、プロパティウインドウで「コード生成方法」に「規定」を指定する。
    image
    image
  7. ソリューションエクスプローラにて「Northwnd.Context.tt」と「Northwnd.tt」を削除
    image
  8. F6キーを押してビルド

ドメインサービスオブジェクトとデータに対するクエリを追加するには

ここで、ドメインサービスとは今作ったEntityデータモデルにアクセスしてクライアント側へWCFで公開してくれるようなサービスといったイメージと理解している。

  1. ソリューションエクスプローラでRIA1.Webプロジェクトの右クリックから、「追加」「新しい項目」をクリック
  2. 「Web」カテゴリで「DomainServiceクラス」を選択し名前に「MyService」をつけて「追加」ボタンをクリック
  3. 「新しいドメインサービスクラスの追加」画面で表示された「エンティティ」をチェックし、「編集の有効化」もチェックして「OK」ボタンをクリック。
    image
    一旦ビルドしていなかったりすると「エンティティ」が表示されなかったりするので注意。
  4. 作成された「Myservice.cs」を見ると、それぞれのエンティティに対するCRUDメソッドが自動生成されているのが判る。
  5. ここで一旦ビルドして、クライアントプロジェクトの自動生成コードを見てみる。
    (ソリューションエクスプローラでクライアントプロジェクトを選択し、「すべてのファイルを表示」をクリック)
    image
    ドメインサービスに対応するContextクラス(ここにクライアントが使うCRUDが生成される)とエンティティクラスが作成される。
    この自動生成によりクライアント側はサーバーサービスを意識することなくクライアント側のプログラミングに専念できることになる。
    先のダイアログで「クライアントアクセスを有効にする」をチェックして事によってドメインサービスクラス(MyServiceクラス)に「EnableClientAceess」属性が付与され、「EnableClientAceess」属性が付与されたクラスのメソッドはクライアント側にコピーされるといった仕掛けだろうか。
  6. EmployeeList.xamlを開き、「TextBlock」コントロールの下に「DataGrid」コントロールをドラッグして追加し、プロパティを以下の様に指定する。
    1. <sdk:DataGrid AutoGenerateColumns="True" IsReadOnly="True" Name="dataGrid1" MinHeight="100" />
  7. コードビハインド(EmployeeList.xaml.cs)を開きusing句に以下を追加。
    1. using RIA1.Web;
    2. using System.ServiceModel.DomainServices.Client;
  8. データをロードする様に以下のコーディングを追加する
    1. namespace RIA1.Views
    2. {
    3.     public partial class EmployeeList : Page
    4.     {
    5.         MyContext _MyContext = new MyContext();
    6.         public EmployeeList()
    7.         {
    8.             InitializeComponent();
    9.             this.dataGrid1.ItemsSource = _MyContext.Employees;
    10.             _MyContext.Load(_MyContext.GetEmployeesQuery());
    11.         }

    以前のサンプル等を見るとデータ取得後のコールバックでデータソースを設定したりして若干煩雑なコーディングだったが、非常にすっきりした形になっている。(最近変わったのかどうかは判らないが)
  9. F5キーで実行し、右上のメニューから「EmployeeList」をクリックすると「Employee」テーブルの内容が表示される。
    image

2012年10月10日水曜日

◆Visual Studio 2012 で Silverlight RIA Service を試してみる

最近ではMicrosoftは旧称メトロアプリ一色。
Silverlight はどうやら開発ツールの主流からははずれたようだ。
これでようやく機能追加が止まって実用期に入るのではと期待している。

はっきり言って、これまでは安定もする前からどんどんバージョンが上がっていき、開発環境を維持するだけで一苦労。

各種資料、解説記事もバージョンが入り乱れて混乱しがち。
とてもじゃないが、安心して業務アプリケーションなど作っていられない。

テクノロジーが進化してもなぜか我々の仕事は一向に楽にならない。(逆に面倒になるばかり)

最近やっと、社内向けツールなどはSilverlightも良いかもねなんて思ったりして少し触り始める。

とりあえずは以下のチュートリアルから。(環境はVS2012)
http://msdn.microsoft.com/ja-jp/library/ee707376(v=vs.91).aspx

説明通り進めていけばテーブルの内容を簡単にブラウザで照会できた。
RIAサービスって便利!!

ただ、ちょっと気になったのが「Entity Data Model」を追加したときに出た以下のエラー。
無視して進めても動作的には問題なさそうだったが・・・・。

image

良くは判らないが、NuGetを入れれば良いのだろうかと思い、とりあえず入れてみる。

「ツール」「拡張機能と更新プログラム・・・」から「NuGet Package Manager」を検索してインストール。
image

運良くこれでエラーは消えてくれた。

が、その後が良くない。

「Entity Data Model」を追加した後、ドメインサービスを追加する手順になっているのだが、Nugetを入れる前はこんな感じに公開するテーブル(エンティティ)を認識してくれていたのに、

image

Nuget追加後は何も選択肢に表示されなくなってしまった。

image

表示されていたリンクを辿ってみると、どうやら「Entity Data Model」とドメインサービスのバージョンが整合しないようだ。(Nugetでバージョンアップされた?)
http://support.microsoft.com/kb/2745294

なんともいけてない・・・。

気を取り直して、指示通りに修正する。

  1. Entityモデルのデザイナー画面を開く
  2. プロパティウインドウにて「コード生成方法」を「なし」から「規定」に変更
    image
  3. ソリューションエクスプローラで「edmx」を展開し2つの「.tt」ファイルを削除する。
    image

私が最近チェックした記事