Quantcast
Channel: とあるコンサルタントのつぶやき
Viewing all 44 articles
Browse latest View live

Hello World, Silverlight 2 !!

$
0
0

というわけで、まずしょっぱなは VS2008 と Expression Blend 2.5 を使った Silverlight 2 のアプリケーション開発の基本について説明してみたいと思います。実は今年の TechEd 2008 では Silverlight 2 の説明をやりたかったんですが、技術調査が間に合わずに断念……してしまったので、ここで解説してみようと思います。(TechEd の Agenda 締め切りってめっちゃ早いんですよ……とまあそれはともかく。)

さて、Silverlight 2(以下 SL2)は WPF のサブセットのプログラミングモデルを持っています。この WPF は、もともと以下の 3 つの異なるプログラミングモデルを統合するために開発されたフレームワークでした。

  • ドキュメントタイプのコンテンツ(HTML のようなワープロタイプのコンテンツ)
  • 固定座標レイアウトタイプのフォーム(Windows フォームのようなもの)
  • 3D ベクターグラフィックス(DirectX のようなもの)

従来、全く異なるプログラミングモデルで開発されていたこれらのアプリケーションを、XAML を中心とした一つのプログラミングモデルで開発できるようにしたのが WPF であり、サブセットである SL2 も同じ特徴を持ちます。しかしこの WPF/SL2 のプログラミングモデルでは、レイアウトシステムやコンテンツ合成モデルなどの新しい UI 構築概念が導入されており、これらの特有の概念を理解しないと、WPF や SL2 を「全く」使いこなせないと思います。特に、Expression Blend はこの WPF, SL2 の UI 概念に基づいて作られたツールなので、起動したはいいけどなにしていいのやら、という状態にもなるかと思います。

ここでは、簡単なサンプルを Step by Step で通しで開発してみて、それを通して、SL2 の特有概念について解説していきたいと思います。

説明するトピックは以下の通りです。

  • Part 1. 最も簡単な Silverlight 2 アプリ開発
    • StackPanel による画面の組み立て
    • Hello World SL2 アプリの開発
    • XML Web サービスの呼び出し
  • Part 2. Expression Blend との連携
    • Blend による画面デザイン方法
    • リキッドデザイン画面の作成
  • Part 3. 高度な Silverlight 2 アプリ開発
    • ListBox への一覧表示
    • コンテンツモデルによるボタンの変更
    • レンダリング変形機能の活用

なお、今回のサンプルはすべてβ2ベースで書かれています。(製品版だと動かないところがあるかもしれませんが、そこは頑張ってください、ということで……;) また、前提知識として、ASP.NET や LINQ に関する基本的な知識はあるものとして解説を進めます。不明な点がある場合には、関連する資料を調べてみてください。

……では今から原稿書き始めます。:-)
というかもろもろ初めての挑戦なので、画像のアップとかどうやるんだろう……状態です(笑)。


Part 1. 最も簡単な Silverlight 2 アプリ開発

$
0
0

というわけで今日は TechEd 2008 Yokohama の会場に来てます。今回は MCS がコンサルティングサービス体験コーナーを出展していて、そのヘルプで来てるんですが、なにげにお昼までは出番がない……

20080828100818

というわけで、昨日書きかけた原稿を完成させるべく控え室で Live Writer と格闘中。そんなわけで、早速 SL2 アプリの開発を見て行きます。

まず Part 1. では、以下の項目について解説したいと思います。

  • Visual Studio 2008 を使った Silverlight 2 アプリ開発の基本
  • Hello World Silverlight 2 アプリの開発
  • XML Web サービス連携の基本

なお、本資料を書くにあたっては、Silverlight 2 beta 2 chained installer の SDK キットと、Expression Blend 2.5 June Preview (日本語版) を利用しています。こちらのセットアップ方法については解説しませんので、必要な方はネットを検索してみてください。

[Step 1] Silverlight 2 プロジェクトの新規作成

まず、VS2008 を起動して、新規に Silverlight 2 のプロジェクトを作成します。

  • SL2 の SDK をインストールすると、VS2008 に SL2 のプロジェクトテンプレートがインストールされます。こちらから新規に SL2 アプリを作成します。
  • 作成時に、SL2 をホストするためのサンプル Web サイトも同時に作成するかを問われるので、こちらも同時に作成してください。

20080828a

20080828b

SL2 のプロジェクトはクラスライブラリプロジェクト(SilverlightApplication1)として作成されます。

  • このプロジェクトがコンパイルされると、.xap ファイル(SL2 アプリがパッケージングされた zip ファイル)が作成されます。
  • .xap ファイルは、テスト用サンプル Web サイト(SilverlightApplication1Web)にコピーされ(ClientBin フォルダ下)、これがテストページから呼び出される形で動作します。

20080828c

[Step 2] StackPanel を利用したコントロールの表示

まず、クラスライブラリプロジェクト(SilverlightApplication1)内の Page.xaml を開きます。

  • 画面上側にプレビュー、下側に XAML エディタが現れます。
  • 下側のウィンドウから、直接 XAML コードを書きます。

20080828d

簡単のため、レイアウトシステムを Grid から StackPanel に切り替えます。

  • Page.xaml 内には、<Grid> タグがありますが、これはタグ内に書かれた UI 部品をグリッド(表)に割り当てて表示するという特殊なレイアウトシステムです。
  • Grid レイアウトは使い方が複雑なので(Part 2 で説明します)、ここでは簡単な StackPanel を使うことにし、コードを以下のように書き換えます。

[修正前]

   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300">
   5:<Grid x:Name="LayoutRoot" Background="White">
   6:  
   7:</Grid>
   8:</UserControl>

[修正後]

   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300">
   5:<StackPanel>
   6:
   7:</StackPanel>
   8:</UserControl>

次に、StackPanel 内に、タグを使って UI 部品を配置していきます。

  • ここではまず、テキストボックス、ラベル、ボタンの 3 つを配置してみます。(ツールボックスをダブルクリックしてコードを自動生成することもできますが、IntelliSense を使ってコードを書いてもよいと思います。)
  • 部品群は、StackPanel により自動的に「縦に並べて」表示されます。(コードを書くと、preview 画面に反映が行われます。)
  • 各オブジェクトに名前をつける場合には、x:Name という属性を利用してください。
   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300">
   5:<StackPanel> 
   6:<TextBox x:Name="textBox1" Text="Nobuyuki" />
   7:<TextBlock x:Name="textBlock1" Text="あああ" />
   8:<Button x:Name="button1" Content="ボタン" />
   9:</StackPanel> 
  10:</UserControl>

20080828e

上記のコードには、非常に興味深いポイントがいくつかあります。これは SL2/WPF 特有の UI 概念を反映しているので、ぜひ注目してください。

  1. SL2 には Label コントロールがありません。かわりに TextBlock コントロールを利用します。
  2. TextBox と TextBlock コントロールでは、表示内容の指定に .Text プロパティを使います。 しかし、Button コントロールでは、表示内容の指定に .Content プロパティを使います。

これは、SL2/WPF では「コンテンツ合成」と呼ばれる概念がサポートされていることに起因するものです。例えば 1. についていうと、

  • ラベルは必ずしも「文字」とは限りません。場合によってはイラストだったりすることもあります。
  • しかし、従来の Label コントロールは、中身を「文字」に暗黙的に決めてしまっていました。
  • この問題を解決するため、「文字」を表示するためには、Label ではなく TextBlock を使います。

また、2. については、

  • ボタンの中身は必ずしも「文字」とは限りません。もしかしたら「絵」かもしれませんし、あるいは複合コンテンツかもしれません。
  • こうしたケースをサポートできるように、ボタンの中身に「複合コンテンツ」を指定できるように設計されています。
  • 例えば、ボタンの中に画像を埋め込みたい場合には、以下のようにします。(詳細なコードは理解しなくても OK です。)
   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300"> 
   5:<StackPanel> 
   6:<TextBox x:Name="textBox1" Text="Nobuyuki" /> 
   7:<TextBlock x:Name="textBlock1" Text="あああ" /> 
   8:<Button x:Name="button1"> 
   9:<Button.Content>
  10:<Image Height="80.75" Width="56.791" Source="pic1.jpg" Stretch="Fill"/> 
  11:</Button.Content>
  12:</Button> 
  13:</StackPanel> 
  14:</UserControl> 

以上を整理すると、以下の通りになります。

  • SL2 には、Label コントロールはありません。かわりに TextBlock コントロールを使います。
  • 各コントロールの表示内容に文字列を指定する場合、.Text プロパティを使う場合と .Content プロパティを使う場合があります。
  • .Content プロパティを使うコントロールの場合、表示コンテンツとして画像や複合コンテンツを使うことができるようになっています。

[Step 3] Hello World メッセージの表示

引き続き、ボタンをクリックした際に Hello World メッセージが表示されるようにします。 まずはボタンのイベントハンドラを作成します。

  • Preview 画面からダブルクリックでイベントハンドラを作る機能は今のところありません。
  • XAML コードにて、Click イベントハンドラを追加し、コードビハインドに処理を記述します。
   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300"> 
   5:<StackPanel> 
   6:<TextBox x:Name="textBox1" Text="Nobuyuki" /> 
   7:<TextBlock x:Name="textBlock1" Text="あああ" /> 
   8:<Button x:Name="button1" Content="ボタン"Click="button1_Click" /> 
   9:</StackPanel> 
  10:</UserControl>
   1:privatevoid button1_Click(object sender, RoutedEventArgs e) 
   2: { 
   3:     textBlock1.Text = "Hello World SL2, " + textBox1.Text; 
   4: } 

20080828f

これを Ctrl+F5 キーで実行すると、Hello World メッセージが表示できます。

20080828g

  • コードを修正してアプリケーションを再実行する場合には、いったんブラウザを終了させてください。(ブラウザ内にキャッシュが残るため)
  • SL2 では、いわゆるモーダルダイアログがサポートされていません。もしモーダルダイアログを表示したい場合には、以下のようなコードでブラウザの警告ポップアップを使う必要があります。
   1:privatevoid button1_Click(object sender, RoutedEventArgs e) 
   2: { 
   3:     System.Windows.Browser.HtmlPage.Window.Alert("Hello World SL2, " + textBox1.Text); 
   4: }

[Step 4] XML Web サービスの呼び出し

SL2 では、XML Web サービスを呼び出すこともできます(クロスドメインアクセスも可能)。以下の手順で、XML Web サービスの作成と呼び出しを行ってみます。 ここでは簡単のため、WCF ではなく ASP.NET XML Web サービスを使うことにします。

まずは Web サイト側に WebService.asmx ファイルを追加します。

  • コードビハインドを使わない開発モデルを使います。
  • 以下のような GetMessage Web サービスを開発します。
  • 開発したら、この WebService.asmx ファイルを呼び出してみて、動作確認を行ってください。

20080828h

   1:<%@ WebService Language="C#" Class="WebService" %> 
   2:  
   3:using System; 
   4:using System.Web; 
   5:using System.Web.Services; 
   6:using System.Web.Services.Protocols; 
   7:  
   8: [WebService(Namespace = "http://tempuri.org/")] 
   9: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
  10:publicclass WebService  : System.Web.Services.WebService { 
  11:  
  12:     [WebMethod] 
  13:publicstring GetMessage(string name) {
  14:return"Hello World, " + name;
  15:}
  16: } 

20080828j

次に、Silverlight 2 アプリケーション側に、サービス参照を追加します。

  • サービス参照では、URL 欄に WebService.asmx ファイルの URL を入力します。ポート番号はランダムで決定されるので、各自の環境に合わせて設定してください。(探索ボタンを押すと簡単に追加できますのでこちらがオススメ。)
  • これにより、Web サービスを呼び出すためのプロキシクラスが作成されます。

20080828k

20080828l

作成されるプロキシクラスに関しては、以下の 2 つの大きな特徴がありますので注意してください。 まず一つ目は、WCF(Windows Communication Foundation)のプロキシクラスが作成される、という点です。

  • .NET Framework 2.0 で利用していた、ASP.NET XML Web サービスのプロキシクラスではなく、WCF のプロキシクラスが作成されます。
  • ただし、SL2 では WCF のサブセット���みのサポートです。このため、WS-* を使ったサイトの呼び出しなどはできません。(BasicHttpBinding のみがサポートされています。)

そしてもう一つは、非同期呼び出しパターンのみがサポートされている、という点です。

  • プロキシクラスを使った XML Web サービス呼び出しは、多くの場合、同期型(=メソッドを呼び出すと、すぐに結果が取り出せる)になります。
  • しかし SL2 で作成される XML Web サービスのプロキシクラスは、非同期型(=Web サービス呼び出しを行うメソッドと、処理結果を受け取るメソッドが別になる)になります。

具体的には、Page.xaml.cs ファイルの button1_Click メソッドに以下のようなコードを書きます。

   1:privatevoid button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:string name = textBox1.Text;
   4:// プロキシクラスのインスタンス化
   5:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
   6:// GetMessage Web サービス呼び出し終了時に呼び出されるメソッドを指定
   7:     proxy.GetMessageCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs>(proxy_GetMessageCompleted);
   8:// 非同期呼び出しを開始
   9:     proxy.GetMessageAsync(name);
  10: }
  11:  
  12:// 非同期呼び出し終了時に呼び出されるメソッド
  13:void proxy_GetMessageCompleted(object sender, SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs e)
  14: {
  15:// 呼び出し結果取り出し(呼び出しに失敗している場合には、e.Result を取り出そうとすると例外発生)
  16:string result = e.Result; 
  17:     textBlock1.Text = result;
  18: }

これを実行すると、(見た目は変わりませんが) XML Web サービスが呼び出されるような Silverlight 2 アプリケーションになります。(再実行時はブラウザを一度終了させることをお忘れなく^^)

20080828m

なお、現在の実装のままだと、ボタンが二重押しされる可能性があります。このため、以下のようなコードを追加して、XML Web サービス呼び出し中はボタンを Disable 化するとよいでしょう。

   1:privatevoid button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:button1.IsEnabled = false;
   4:string name = textBox1.Text;
   5:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
   6:     proxy.GetMessageCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs>(proxy_GetMessageCompleted);
   7:     proxy.GetMessageAsync(name);
   8: }
   9:  
  10:void proxy_GetMessageCompleted(object sender, SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs e)
  11: {
  12:button1.IsEnabled = true;
  13:string result = e.Result; 
  14:     textBlock1.Text = result;
  15: }
  16:

以上で XML Web サービスと連携する基本的な Silverlight 2 アプリケーションの開発方法についての説明はおわりです。キーポイントをまとめると次のようになります。

  • Silverlight 2 では、XAML と呼ばれるファイルを使って画面を作る。
  • Silverlight 2 では、Grid や StackPanel などの中に、UI 部品を並べていって画面を作る。
  • Silverlight 2 には Label コントロールはない。かわりに TextBlock コントロールを使う。
  • コントロールに文字列を設定する場合には、.Text プロパティを使う場合と、.Content プロパティを使う場合がある。.Content プロパティが使われている場合には、中身のコンテンツとして複合コンテンツを指定できるようになっている。
  • Silverlight 2 からはサービス参照機能を使うことで、簡単に XML Web サービスが呼び出せるようになっている。

引き続き、Part 2. では Expression Blend との連携方法などについて説明していきたいと思います。

というわけで初めて Windows Live Writer 使ってみましたがめちゃめちゃ使いやすいですね。こんな便利ツールがあったとは……^^

Part 2. Expression Blend との連携

$
0
0

さて、引き続き Part 2 では、Expression Blend との連携方法と、このツールの使い方について解説していきます。

Expression Blend は、WPF や Silverlight 2 で用いられている新しい UI 概念に基づいて作られた UI デザイナツールであり、特にコントロールの配置(レイアウト)の手法に関する考え方が、従来とは全くといっていいほど異なっています。このため、このツールに触れた直後は「どう使えばいいのかさっっっっぱりわからない」になると思いますが、WPF/SL2 の UI 概念とリンクさせながら理解すると、簡単に使いこなせるツールになっています。

というわけで、以下に Expression Blend による画面デザイン手法について解説していきます。ここでは、最終ゴールとして以下のような画面を作るという前提で、この画面を Expression Blend でデザインしていく手法を解説します。

20080829k

[Step 5] Expression Blend の起動

Expression Blend は、Visual Studio と同じソリューションファイルを利用するため、Visual Studio で開発したプロジェクトをそのまま開くことができます。連携のためのショートカットも用意されており、Visual Studio の開発画面から右クリックを押すことで、Expression Blend を起動することができます。

20080829

Expression Blend を起動すると、セキュリティ警告が出ますが、ここではそのまま無視して進んで構いません。完了すると、Expression Blend 上で Visual Studio 2008 の Silverlight 2 プロジェクトが開かれます。

20080829b

Expression Blend は非常にとっつきにくいツールだと思いますが、いくつか肝になる部分を解説します。

  • 画面左側の「オブジェクトとタイムライン」ウィンドウは、XAML ファイルにおけるコントロールのツリー構造を表現したものです。基本的には、このウィンドウを利用して画面を組み立てます。
  • 画面中央部の上側は XAML のプレビュー画面、下側は XAML のコードです。XAML のプレビュー画面は VB のフォームのようにいじる(コントロールの位置を動かしたりする)ことができますが、絶対に安易にいじってはいけません。後述するレイアウトコントロールの概念を理解してからいじるようにしてください。
  • 画面右側には、「プロジェクト」ウィンドウと、「プロパティ」ウィンドウがあります。
  • これらのウィンドウは、ツール→オプション→ワークスペース→ワークスペースのズームの項目から、縮小・拡大表示することができます。特に画面が狭い PC を使っている場合には、この拡大率を下げると使いやすくなりますので活用してください。

20080829c

さて、ここから Expression Blend での画面デザイン方法について解説していきますが、その前に、Silverlight 2 では大別して 2 つの基本的なレイアウト方式がサポートされている点を理解しておく必要があります。それが、固定座標レイアウトと、リキッドデザインレイアウトです。

  • 固定座標レイアウトとは...
    いわゆる従来の VB 6 や Windows フォームのアプリケーションに代表されるような、「固定サイズ画面の固定ピクセル位置に固定サイズのコントロールが貼り付けられている」画面。
  • リキッドデザインレイアウトとは...
    Office や Windows Messenger のように、画面サイズを変えると「液体のように」その画面サイズに追随するようにレイアウトがきれいに変わっていくタイプの画面。

VB6 をはじめとしたフォーム系アプリケーション開発技術の多くは、固定座標レイアウトをベースとしていましたが、昨今のアプリケーションでは、リキッドデザインに対応することが求められるようになってきました。たとえば、今この文章を書くのに使っている Live Writer も、ウィンドウサイズ変更時に動的に入力欄のサイズが変更されるようになっています。

20080829d

リキッドデザインに対応することは、ウィンドウサイズの観点でだけでなく、多言語対応の観点でも重要です。たとえば利用する OS の言語によって

  • 「こんにちは」
  • 「Hello World」

と表示を切り替えるようなボタンを用意しようと思ったとき、(コンテンツの最大サイズに合わせてボタンの Width プロパティを設定しておくのではなく)、コンテンツのサイズに合わせて、柔軟にコントロールのサイズやレイアウトが変わってくれることが望ましいといえます。

Silverlight 2 では、UI コントロールを並べる際に、代表的に以下の 3 通りの並べ方がサポートされています。

  • Canvas : 固定座標レイアウト
  • Grid : リキッドデザインレイアウト
  • StackPanel : 横または縦に並べるだけの単純なレイアウト

StackPanel の使い方についてはすでに解説済みなので、引き続き、Canvas レイアウトと Grid レイアウトについて解説します。

[Step 6] レイアウトコントロール① Canvas コントロール

ここまでのサンプルでは、StackPanel レイアウトコントロールの中に TextBox, TextBlock, Button を並べましたが、これらを固定座標レイアウト(いわゆるフォームに対して任意の位置に貼り付けられるレイアウト)にしてみます。

まず、Expression Blend のオブジェクトとタイムラインウィンドウから、StackPanel コントロールを Canvas コントロールに差し替えます。(右クリック→レイアウトの種類の変更→Canvas を選択)

20080829e

これにより、以下の点が変化します。

  • オブジェクトとタイムラインウィンドウの StackPanel が Canvas にかわります。
  • XAML コード内の StackPanel タグが Canvas タグにかわります。

20080829f

この状態になると、画面上の UI コントロールを任意の場所に動かしたり、サイズを自由に変更したりすることができます。(レイアウト情報は XAML コードに属性情報として保存されます。)

20080829g

   1:<UserControl x:Class="SilverlightApplication1.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Width="400" Height="300">
   5:<Canvas> 
   6:<TextBox x:Name="textBox1" Text="Nobuyuki"Height="20.7724609375" Width="148" Canvas.Top="119.309" Canvas.Left="132" /> 
   7:<TextBlock x:Name="textBlock1" Text="あああ"Height="22.537109375" Width="158" Canvas.Top="92.772" Canvas.Left="44" /> 
   8:<Button x:Name="button1" Content="ボタン" Click="button1_Click"Height="25.90283203125" Width="136" Canvas.Top="41.31" Canvas.Left="144" /> 
   9:</Canvas> 
  10:</UserControl>

さて、Silverlight 2 では、UI コントロールの入れ子関係が非常に重要な意味を持ちます。例えば、Canvas (いわゆるパネル)の中に Canvas が貼り込まれている場合、新たに画面に貼り付ける部品(Button や TextBox コントロール)が、どちらの Canvas に所属するのかは非常に重要になります。

 20080829i

上図の場合、Button コントロールがどちらの Canvas に所属しているかは、XAML コントロールツリー(オブジェクトとタイムラインウィンドウ)を見るとすぐにわかります。

このような理由から、コントロールを追加する場合は、安易にドラッグ&ドロップで画面上に追加しないようにしてください。一番確実なのは、下図のように、コンテナとなるコントロールをダブルクリックして選択したのちに、左側のツールボックス上のアイテムをダブルクリックしてアイテムを追加する方法です。ツールに慣れるまではこの手法を使っていただいたほうがよいでしょう。

20080829h

[Step 7] レイアウトコントロール② Grid コントロール

さて、Canvas レイアウトコントロールは固定座標レイアウトを行うために使うものでしたが、リキッドデザイン画面を実現したい場合には、Grid レイアウトコントロールをコンテナとして利用します。

Grid レイアウトコントロールの基本概念は、「パネルを(Excel のような)複数のセルに切り、そのセルにコントロールを割り当てていく」というものです。(概念をわかりやすく説明するためにあえて Excel のスナップショットを示しますが^^)、下図のように、Excel と同様、複数の列や行を連結させてひとつのセルとして使うことができるようになっています。

20080829j

では実際に、リキッドデザインに対応した下図のような画面を Expression Blend で作成してみることにします。

20080829k

Page.xaml ファイルにはすでにかなりの修正が加えられてしまっていますので、いったんクリーンアップすることにしましょう。

  • Expression Blend 上で Page.xaml ファイルを削除します。(「プロジェクトから削除」ではなく、通常の「削除」を行い、ファイルを完全に消去します。)
  • 新しいユーザコントロールとして Page.xaml ファイルを追加してください。(プロジェクトに Silverlight 2 ユーザコントロールを Page.xaml という名称で追加します。)

20080829n

20080829o

20080829p

新規に追加した Page.xaml ファイルは Grid レイアウトコントロール(既定のデザインサイズは 640x480)になっていますので、こちらを使って、リキッドデザインの画面を設計していきます。

  • 既定のレイアウトコントロールのサイズではやや大きすぎるという場合には、XAML コード中の x:DesignWidth と x:DesignHeight 属性を変更しておいてください。
  • なおこれらの属性は、Expression Blend 上でのデザイン時のサイズであり、実際の実行時の描画サイズではありません。実際の描画時のサイズを直接指定したい場合には、Width, Hieght 属性を使います。
   1:<UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     x:Class="SilverlightApplication1.Page"
   8:d:DesignWidth="400" d:DesignHeight="300">
   9:  
  10:<Grid x:Name="LayoutRoot" Background="White" />
  11:</UserControl>

では、リキッドデザイン画面を作成していきます。まず、画面を 3x2 のセルに切ります。

  • パネルの上・左部分のところでマウスをクリックすると、行や列を切ることができます。
  • 幅についてはとりあえず適当で OK です。(あとから調整します。)

20080829q

次に、画面上に UI コントロールを配置していきます。

  • Grid レイアウトコントロール上に、TextBlock, Button, ListBox コントロールを追加します。
  • 追加の際は、デザイン画面上にドラッグ&ドロップするのではなく、① 「オブジェクトとタイムライン」ウィンドウ上の Grid コントロール(オブジェクト名 LayoutRoot )をまずダブルクリックして選択し、② ツールボックス上のコントロールをダブルクリックします。これにより、Grid レイアウトコントロール下に、TextBlock, Button, ListBox コントロールが追加されます。
  • この時点では、レイアウトを設定していないので、グリッドの左上に 3 つのコントロールが貼りついている形になっています。

20080829r

次に、これらの貼り付けたコントロールを、本来の正しいセルの位置に移していきます。最終的には、各コントロールを下図のように配置させます。

20080829t

例えば ListBox コントロールを、下側の行の 3 つのセルにまたがって配置させるためには、以下のように作業します。

  • まず、オブジェクトとタイムラインウィンドウで、ListBox を選択します。
  • ツールボックス内のポインタ(左上のもの)を選択し、ListBox を動かします。
  • ガイドが表示されるので、これに割り当てます。(割り当て時は、隙間(マージン)を取っても OK ですし、マージンを取らずにぴったり割り当てても OK です。お好みでどうぞ。)

20080829s

これらを繰り返して、TextBlock, Button, ListBox を正しい位置に配置します。

20080829u

完成した XAML コードは以下の通りです。(RowDefinition, ColumnDefinition の項目は、セルの切り方によって若干異なるはずです)

   1:<UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     x:Class="SilverlightApplication1.Page"
   8:     d:DesignWidth="400" d:DesignHeight="300">
   9:  
  10:<Grid x:Name="LayoutRoot" Background="White">
  11:<Grid.RowDefinitions>
  12:<RowDefinition Height="0.2*"/>
  13:<RowDefinition Height="0.8*"/>
  14:</Grid.RowDefinitions>
  15:<Grid.ColumnDefinitions>
  16:<ColumnDefinition Width="0.238*"/>
  17:<ColumnDefinition Width="0.537*"/>
  18:<ColumnDefinition Width="0.225*"/>
  19:</Grid.ColumnDefinitions>
  20:<TextBlock HorizontalAlignment="Left" VerticalAlignment="Stretch" Text="TextBlock" TextWrapping="Wrap" d:LayoutOverrides="Width" Margin="8,8,0,8" Width="79.2"/>
  21:<Button Margin="8,8,8,8" Content="Button" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
  22:<ListBox Margin="8,8,8,8" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3"/>
  23:</Grid>
  24:</UserControl>

この中で注目したいコードは以下の 3 行です。

   1:<TextBlock HorizontalAlignment="Left" VerticalAlignment="Stretch" Text="TextBlock" TextWrapping="Wrap" d:LayoutOverrides="Width" Margin="8,8,0,8" Width="79.2"/>
   2:<Button Margin="8,8,8,8" Content="Button" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
   3:<ListBox Margin="8,8,8,8" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3"/>
  • Grid.Column(列), Grid.Row(行) 属性により、当該コントロールが占有するセルが指定されます。
  • Grid.ColumnSpan(そこから横に何セルを結合して使うか)、Grid.RowSpan(そこから下に何セルを結合して使うか)により、セルを結合して使うことが指定されます。
  • HotizontalAlignment, VerticalAlignment が Stretch になっているため、各コントロールは「与えられたセルをめいいっぱい使って」描画します。

ここまでの状態で、Visual Studio 2008 に戻って実行してみると(SilverlightApplication1TestPage.aspx を表示してみると)、以下のようになります。

[ウィンドウ拡大時]

20080829v

[ウィンドウ縮小時]

20080829w

確かにリキッドデザインにはなっているものの、画面が崩れてしまうことが確認できます。これを防ぐため、Grid レイアウトコントロールに、サイズに関する制約条件を追加して、適切に動作するようにします。

[Step 8] サイズ制約条件の追加

再び Expression Blend の画面に戻って作業します。

  • オブジェクトとタイムラインのウィンドウから、Grid レイアウトコントロール(オブジェクト名は LayoutRoot)をダブルクリックして選択します。
  • 選択すると、黄色いボーダーラインが付与され、コンテナとしての各種の情報がエディットできるようになります。

20080829x

各列・各行に、鍵のアイコンが付与されていますが、この鍵のアイコンをクリックすると、以下の 3 つの状態をトグルするようになっています。

  • スターサイズ設定済み : 余っている幅を使う
  • ピクセルサイズ設定済み : 固定的なサイズに設定する
  • 自動サイズ設定済み : 内容を表示するために必要な最低限のサイズを利用する

リキッドデザイン画面を構成する際に、この中で最も重要なのが「自動サイズ設定済み」です。この設定を行うと、コンテンツのサイズに合わせて幅が自動調整されます。そこで、1 行目、1 列目、3 列目に対して「自動サイズ」を設定します。(鍵のアイコンの一部が変化していることに注目してください。)

20080829y

この設定だけだと見た目が変わりません。これは、各列・各行に最低割り当てピクセル(MinWidth, MinHeight)が設定されてしまっているためです。

   1:<Grid.RowDefinitions>
   2:<RowDefinition Height="Auto"MinHeight="60"/>
   3:<RowDefinition Height="*"/>
   4:</Grid.RowDefinitions>
   5:<Grid.ColumnDefinitions>
   6:<ColumnDefinition Width="Auto"MinWidth="95.199996948242188"/>
   7:<ColumnDefinition Width="*"/>
   8:<ColumnDefinition Width="Auto"MinWidth="89.916000366210938"/>
   9:</Grid.ColumnDefinitions>

これらの設定を削ると、コンテンツに合わせたサイズでセルの幅や高さが設定されるようになります。やり方はいくつかありますので、お好きな方法を使ってください。

  • XAML のコードエディタから、これらの設定を削ってしまう。(てっとり早いです^^)
  • Grid コントロールのプロパティを開き、「レイアウト」の項目の中の ColumnDifinitions, RowDifinitions コレクションの設定を変更する。(※ 既定では折りたたんで隠されているので、開いて使ってください)
  • デザイン画面上で列幅や行幅を変更し、なるべく小さな幅にする。(MinWidth, MinHeight を小さな値にする) (GUI 上からできるのでこれも便利です。)

20080829z

以上の結果、どのような動作をすることになるかというと、

  • 横方向について
    1 列目と 3 列目について、「コンテンツのサイズに合わせた最小幅」がまず割り当てられる。
    2 列目については、残った幅が割り当てられる。
  • 縦方向について
    1 行目について、「コンテンツのサイズに合わせた最小幅」がまず割り当てられる。
    2 行目については、残った幅が割り当てられる。

以上の作業で、リキッドデザインの画面が出来上がります。

image

[Step 9] 最後の仕上げ

最後に、TextBlock や Button への変数名の設定や、文字列の変更を Expression Blend から行っておきます。

  • TextBlock コントロールの Text プロパティを「著者データ一覧」に変更する。
  • Button コントロールの Content プロパティを「一覧表示」に変更する。

私の端末上で作業してみたら、下図のように TextBlock 内のテキストが折り返されてしまいましたが、これは TextBlock に Width プロパティがついていたためでした;。

image

というわけで Width プロパティを削除(Auto に変更)。ちなみに、リキッドデザイン画面を作成する場合には、極力 Width プロパティや Height プロパティを使わないようにすることをお勧めします。(リキッドデザイン画面のレイアウトの考え方は、レイアウトの制約条件を設定する、という考え方であるため。なので、MinWidth や MaxWidth といったプロパティを中心に設定していきます。)

image

最後に変数名を設定しておきます。(プロパティウィンドウの名前のところから設定)

  • TextBlock コントロール → textBlock1
  • Button コントロール → button1
  • ListBox コントロール → listBox1

完成した画面と XAML コードを以下に示します。

image

   1:<UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     x:Class="SilverlightApplication1.Page"
   8:     d:DesignWidth="400" d:DesignHeight="300">
   9:  
  10:<Grid x:Name="LayoutRoot" Background="White">
  11:<Grid.RowDefinitions>
  12:<RowDefinition Height="Auto"/>
  13:<RowDefinition Height="*"/>
  14:</Grid.RowDefinitions>
  15:<Grid.ColumnDefinitions>
  16:<ColumnDefinition Width="Auto"/>
  17:<ColumnDefinition Width="*"/>
  18:<ColumnDefinition Width="Auto"/>
  19:</Grid.ColumnDefinitions>
  20:<TextBlock HorizontalAlignment="Left" VerticalAlignment="Stretch" Text="著者一覧表示" TextWrapping="Wrap" Margin="8,8,8,8" Width="Auto" x:Name="textBlock1"/>
  21:<Button Margin="8,8,8,8" Content="一覧表示" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" x:Name="button1"/>
  22:<ListBox Margin="8,8,8,8" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3" x:Name="listBox1"/>
  23:</Grid>
  24:</UserControl>

以上で Expression Blend を使った画面の作成方法についての説明はおわりです……というかめっちゃ長すぎる;。もっと短いはずだったんですが、文字で書くと長くなっちゃいますね。すみません;;。キーポイントをまとめると、以下のようになります。

  • Silverlight 2 では、大別して 3 種類のレイアウトが利用できる。
    ① Canvas : 固定座標レイアウト
    ② Grid : リキッドデザインレイアウト
    ③ StackPanel : 横または縦に並べるだけの単純なレイアウト
  • UI コントロールを貼り付ける場合には、入れ子関係に注意する。
  • Grid を使う場合には、まずおおざっぱにセルを切った後、セル幅に「自動サイズ設定」を指定していく。

引き続き、Part 3. では ListBox コントロールへのデータの一覧表示機能の作り込みなどについて説明していきたいと思います。

Part 3. 高度な Silverlight 2 アプリ開発

$
0
0

というわけで、Silverlight 2 & Expression Blend 2.5 の記事の最終回 Part 3. では、ここまでの知識を総括して、XML Web サービス経由でデータベースからデータを取り出して ListBox に表示するアプリケーションを開発してみることにします。

なお、以降の作業は Part 2. で完成させたソリューションファイルに対して行います。Expression Blend から Visual Studio の方に戻り、以下の作業を続けてください。

※ Part 2. 終了時点のファイルはこちらになります。

[Step 10] データ取得のための XML Web サービスの準備

まず、サーバ側にデータベースからデータを取得して送り返す XML Web サービスを準備します。出版社サンプルデータベースである pubs.mdf ファイルを準備し、以下の作業を行います。(準備できない方は、後ろに書いてある回避策を使ってください。)

  • Web サイトの App_Data フォルダ下に pubs.mdf ファイルを貼り付ける。
  • App_Code フォルダ下に、Pubs.dbml ファイル(LINQ to SQL の O/R マッピングファイル)を新規作成する。
  • Pubs.dbml ファイルを開き、サーバエクスプローラからすべてのテーブルをドラッグ&ドロップで追加する。(ドラッグ&ドロップできたらファイルはセーブしておきます。)

image

  • WebService.asmx ファイルを開き、LINQ to SQL でデータを取得して返すコードを記述します。

image

ここでは LINQ to SQL の詳細は解説しませんが、実装上のポイントは以下の通りです。

  • using キーワードにより、System.Linq と System.Data.Linq 名前空間の利用宣言を行います。(LINQ to SQL クエリを使うために必要)
  • データを返すための構造体として、AuthorList クラスを定義します。ここでは簡単のため、au_id (著者 ID)と au_name (著者名)の 2 つのフィールドを持つクラスを定義します。
  • AuthorList クラスの配列を返す GetData() メソッドを XML Web サービスとして定義します。LINQ to SQL のクエリを記述した後、.ToArray() メソッドを呼び出すことにより、LINQ クエリの実行結果を静的配列に変換することができます。

実装が終わった WebService.asmx ファイルは以下の通りです。完成したら、WebService.asmx ファイルをブラウザから呼び出して、動作確認を行ってください。

   1:<%@ WebService Language="C#" Class="WebService" %>
   2:  
   3:using System;
   4:using System.Web;
   5:using System.Web.Services;
   6:using System.Web.Services.Protocols;
   7:  
   8:using System.Linq;
   9:using System.Data.Linq;
  10:  
  11: [WebService(Namespace = "http://tempuri.org/")]
  12: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  13:publicclass WebService  : System.Web.Services.WebService {
  14:  
  15:     [WebMethod]
  16:publicstring GetMessage(string name)
  17:     {
  18:return"Hello World, " + name;
  19:     }
  20:  
  21:     [WebMethod]
  22:public AuthorList[] GetData()
  23:     {
  24:using (PubsDataContext pubs = new PubsDataContext())
  25:         {
  26:             var authors = from a in pubs.authors
  27:where a.state == "CA"
  28:                           select new AuthorList
  29:                           {
  30:                               au_id = a.au_id,
  31:                               au_name = a.au_fname + " " + a.au_lname
  32:                           };
  33:return authors.ToArray();
  34:         }
  35:     }        
  36: }
  37:  
  38:publicclass AuthorList
  39: {
  40:publicstring au_id { get; set; }
  41:publicstring au_name { get; set; }
  42: }

image

※ pubs.mdf データベースファイルを用意できない場合について

この場合には、ダミーデータを返す XML Web サービスを開発してください。具体的には、WebService.asmx ファイル上に以下のようなコードを実装します。

   1: [WebMethod]
   2:public AuthorList[] GetData()
   3: {
   4:     AuthorList a1 = new AuthorList { au_id = "172-32-1176", au_name = "Johnson White" };
   5:     AuthorList a2 = new AuthorList { au_id = "213-46-8915", au_name = "Marjorie Green" };
   6:     AuthorList a3 = new AuthorList { au_id = "238-95-7766", au_name = "Cheryl Carson" };
   7:     AuthorList a4 = new AuthorList { au_id = "267-41-2394", au_name = "Michael O'Leary" };
   8:     AuthorList a5 = new AuthorList { au_id = "274-80-9391", au_name = "Dean Straight" };
   9:     AuthorList a6 = new AuthorList { au_id = "409-56-7008", au_name = "Abraham Bennet" };
  10:     AuthorList a7 = new AuthorList { au_id = "427-17-2319", au_name = "Ann Dull" };
  11:returnnew AuthorList[] { a1, a2, a3, a4, a5, a6, a7 };
  12: }        

[Step 11] XML Web サービスの呼び出しと ListBox へのデータバインド

引き続き、XML Web サービスの呼び出しと ListBox へのデータバインドを行います。まず、Silverlight 2 アプリケーション側のサービス参照を更新し、新しく追加した、GetData() メソッドの情報をプロキシクラスに取り込みます。

image

次に一覧表示ボタンのイベントハンドラとして、XML Web サービス呼び出しとデータバインドを実装します。

  • Page.xaml ファイルを開きます。
  • XAML コードエディタを開き、Button オブジェクトに Click イベントハンドラを追加します。
  • コードビハインドファイルを開き、イベントハンドラコードを追加します。

image

   1:privatevoid button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:     button1.IsEnabled = false;
   4:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
   5:     proxy.GetDataCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs>(proxy_GetDataCompleted);
   6:     proxy.GetDataAsync();
   7: }
   8:  
   9:void proxy_GetDataCompleted(object sender, SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs e)
  10: {
  11:     button1.IsEnabled = true;
  12:     listBox1.ItemsSource = e.Result;
  13: }

実装が終了したら、アプリケーションを実行してみます。ボタンを押下すると、XML Web サービスが呼び出され、データバインドが実行されます。実行結果は以下のようになります。

image

ListBox の各行に「SilverlightApplication1.ServiceReference1.AuthorList」という文字列が表示されていることに着目してください。前述のコードでは、ListBox の ItemsSource プロパティに AuthorList オブジェクトのコレクションを割り当てていますが、ListBox コントロールはこのオブジェクトの表示方法を知りません。このため、既定の状態では、下図に示すように、各項目について .ToString() メソッドを呼び出して ListBox に表示する、という挙動を行います。

image

AuthorList オブジェクトの中身(つまり著者 ID や著者名)を取り出して ListBox 内に表示させるためには、ListBox コントロールにレンダリングテンプレート(各行を描画する際に利用するテンプレート)を指定しなければなりません。

[Step 12] ListBox コントロールに対する ItemTemplate の指定

ListBox コントロールに対するレンダリングテンプレートの指定には、Expression Blend を利用します。Expression Blend を使い、以下の作業を行います。

  • Page.xaml ファイルを開きます。
  • 「オブジェクトとタイムライン」ウィンドウの listBox1 を右クリックして、コンテキストメニューを開きます。
  • 「他のテンプレートの編集」→「ItemTemplate の編集」→「空アイテムの作成」を選択します。
  • DataTemplate リソースの作成ウィンドウが開くので、OK ボタンを押してデータテンプレートの編集画面を開きます。

image

image

上記作業を行う際に、「コントロールパーツ(テンプレート)の編集」ではなく「他のテンプレートの編集→ItemTemplate の編集」の方を選択することに注意してください。この二つは全く違う機能です。

  • 「コントロールパーツ(テンプレート)の編集」とは、ListBox コントロールそのものの描画方法(外観)を変えるための機能です。
  • 「ItemTemplate の編集」とは、ListBox 内の各行のレンダリング方法を変えるための機能です。

ItemTemplate の編集モードに入ると、デザイナが listBox1→DataTemplate の編集モードに入ります。

※ (参考) ItemTemplate の編集に入っているのに DataTemplate と表示されるのは、「ListBox コントロールの ItemTemplate プロパティに、データ表示のためのレンダリングテンプレートである DataTemplate オブジェクトを割り当てる」ためです。つまり、"ItemTemplate" は ListBox のプロパティ名、"DataTemplate" はそこに割り当てるオブジェクトのクラス名です。このことは、最後に生成される XAML コードを見るとよくわかるので、興味がある方は見てみてください。

image

このテンプレートの編集画面では、AuthorList オブジェクトを、どのように 1 行としてレンダリングするのかを設定します。著者 ID と著者名を表示したい場合には、下図のようなテンプレートを設定することになります。

image

具体的には、以下の作業を行います。

  • 既定で貼り付いている Grid レイアウトコントロールを、StackPanel レイアウトコントロールに差し替えます。(レイアウトの種類の変更 → StackPanel)
  • StackPanel コントロールの下に、TextBlock コントロールを二つ貼り付けます。(ツールボックスからダブルクリックで追加するとラク。間違って TextBox を貼り付けやすいので注意してください。自分は結構ミスします...)

image

image

  • StackPanel レイアウトコントロールの Orientation プロパティを、Vertical (縦に並べる)から Horizontal (横に並べる)に変更します。
  • 各 TextBlock の Margin プロパティを 4 に変更します。(既定だと 0 のため、文字がくっついて表示されてしまう。これを避けるため。なお、β2 版では IME 制御が不完全なためか、たまに Margin 値を打ち込むときに IME が全角になってしまい、うまく入力できないことがありますのでご注意を。)

image

最後に、各 TextBlock に対して、データバインドの指定を行います。

  • 左側の TextBlock を選択し、プロパティウィンドウの Text プロパティの右側にある「・」ボタン(詳細プロパティオプション)をクリックし、データバインドを選択します。
  • 「データバインドの作成」ウィンドウの「明示的なデータコンテキスト」タブを開き、カスタムパスとして "au_id" を設定します。(※ データフィールドタブのカスタムパスの指定は、コンテキストバインドの機能ではないため、ここでは使いません。)
  • 同様に、右側の TextBlock に対しては、カスタムパスとして "au_name" を設定しま��。

image

image

image

以上の作業により、レンダリングのためのテンプレートが設定できます。(見た目では、TextBlock の表示がつぶれます。)

image

ファイルをセーブしたら、Visual Studio に戻り、アプリケーションを実行してみてください(ブラウザを再起動することを忘れずに^^)。正しくアプリケーションが作成できていれば、以下のような一覧表示が行えるはずです。

image

最終的に完成した XAML コードとコードビハインドを以下に示します。

[Page.xaml ファイル]

   1:<UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     x:Class="SilverlightApplication1.Page"
   8:     d:DesignWidth="400" d:DesignHeight="300">
   9:<UserControl.Resources>
  10:<DataTemplate x:Key="DataTemplate1">
  11:<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
  12:<TextBlock Text="{Binding Mode=OneWay, Path=au_id}" TextWrapping="Wrap" Margin="4,4,4,4"/>
  13:<TextBlock Text="{Binding Mode=OneWay, Path=au_name}" TextWrapping="Wrap" Margin="4,4,4,4"/>
  14:</StackPanel>
  15:</DataTemplate>
  16:</UserControl.Resources>
  17:  
  18:<Grid x:Name="LayoutRoot" Background="White">
  19:<Grid.RowDefinitions>
  20:<RowDefinition Height="Auto"/>
  21:<RowDefinition Height="*"/>
  22:</Grid.RowDefinitions>
  23:<Grid.ColumnDefinitions>
  24:<ColumnDefinition Width="Auto"/>
  25:<ColumnDefinition Width="*"/>
  26:<ColumnDefinition Width="Auto"/>
  27:</Grid.ColumnDefinitions>
  28:<TextBlock HorizontalAlignment="Left" VerticalAlignment="Stretch" Text="著者一覧表示" TextWrapping="Wrap" Margin="8,8,8,8" Width="Auto" x:Name="textBlock1"/>
  29:<Button Margin="8,8,8,8" Content="一覧表示" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" x:Name="button1" Click="button1_Click" />
  30:<ListBox Margin="8,8,8,8" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3" x:Name="listBox1" ItemTemplate="{StaticResource DataTemplate1}"/>
  31:</Grid>
  32:</UserControl>

[Page.xaml.cs ファイル]

   1:using System;
   2:using System.Windows;
   3:using System.Windows.Controls;
   4:using System.Windows.Documents;
   5:using System.Windows.Ink;
   6:using System.Windows.Input;
   7:using System.Windows.Media;
   8:using System.Windows.Media.Animation;
   9:using System.Windows.Shapes;
  10:  
  11:namespace SilverlightApplication1
  12: {
  13:publicpartialclass Page : UserControl
  14:     {
  15:public Page()
  16:         {
  17:// Required to initialize variables
  18:             InitializeComponent();
  19:         }
  20:  
  21:privatevoid button1_Click(object sender, RoutedEventArgs e)
  22:         {
  23:             button1.IsEnabled = false;
  24:             ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
  25:             proxy.GetDataCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs>(proxy_GetDataCompleted);
  26:             proxy.GetDataAsync();
  27:         }
  28:  
  29:void proxy_GetDataCompleted(object sender, SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs e)
  30:         {
  31:             button1.IsEnabled = true;
  32:             listBox1.ItemsSource = e.Result;
  33:         }
  34:     }
  35: }

以上で基本的には終わりですが、オマケでちょっと遊びをしてみたいと思います。:-)

[Step 13] レンダリング変形機能の活用

従来の Windows フォームや VB6 などのアプリケーションと異なり、Silverlight 2 では、ベクターグラフィクスベースのレンダリングエンジンを利用した描画を行っています。

  • これは、「TextBox や ListBox などがドット絵として描画されるのではなく、ベクターグラフィクスとして描画される」というものです。
  • このような特性を持つが故に、Expression Blend や Visual Studio では、デザイナ画面で「画面そのものを拡大表示する」ことができるようになっています。

image

例えば、Expression Blend 上で、以下のような作業をしてみてください。

  • ListBox コントロールの四隅をつまんで回転させてみる。
  • Button コントロールを台形変形させてみる。(プロパティウィンドウ内の「変換」タブを開き、その中にある「傾斜」をいじってみてください。)

image

このように変形しても、アプリケーションは全く問題なく動作します。(ボタンも問題なく押せるし、リストボックスも問題なく斜めにスクロールします。)

image

実際の業務アプリケーションでこのような変形加工を行うことはないと思いますが^^、Silverlight 2 がベクターベースの描画エンジンを利用している、というポイントは非常に興味深いポイントであるため、覚えておいていただければと思います。

[Step 14] DataGrid コントロールの利用

ちなみに、ここまで ListBox コントロールを使ってきましたが、DataGrid コントロールを使うと簡単にグリッドにデータ表示することができます。これに関しては、細かいやり方は説明しませんので、ここまでの解説を参考にして実際にいじってみてください。:-) キーポイントは以下の通りです。

  • 既定では、Expression Blend のツールボックス上に DataGrid コントロールは現われません。これは、DataGrid コントロールが拡張コントロールであり、参照設定が必要なためです。プロジェクト→参照設定の追加から、C:\Program Files (x86)\Microsoft SDKs\Silverlight\v2.0\Libraries\Client 内にあるSystem.Windows.Controls.Data.dll ファイルに参照設定を張り、さらにアセットライブラリのカスタムコントロール内から選択することで、DataGrid が使えるようになります。
  • データバインドのやり方は基本的に同じで、ItemsSource プロパティにオブジェクトコレクションを割り当てます。

image

※ レンダリングテンプレートの変更は、Columns プロパティに DataGridCellTemplate を割り当てることによって行う....のですが、現時点では Expression Blend からは設定できないかもしれません。(探してみたけど見当たらず....) XAML コードからであれば指定できるので、興味がある方はやってみてください。具体的なやり方は以下のページが参考になると思います。http://blogs.msdn.com/scmorris/

※ 完成したソリューションはこちらになります。

[本エントリと全体のまとめ]

というわけで、本エントリのキーポイントをまとめると以下のようになります。

  • ListBox コントロールの ItemsSource プロパティにオブジェクトコレクションを割り当てると、データバインドができる。
  • データバインド時のレンダリングテンプレートは、ItemTemplate で指定する。
  • Silverlight 2 はベクターベースのレンダリングエンジンを利用している。

3 つのエントリにわけて Silverlight 2 アプリケーションの基本的な開発方法を解説してきましたが、Silverlight 2 と Expression Blend はとにかく面白いツールだと思います。特に、リッチな Web アプリケーションの UI 画面を C# で開発できるというメリットは極めて大きく、また新しい UI 設計概念も非常によくできている、というのが個人的な感想です。

ぜひ、本エントリなどを活用して Silverlight 2 と Expression Blend による開発を楽しんでください。

……というわけでやっと書き終わりました;。やっぱり結構大変っすねー;。
次は何を書こうかな……

Silverlight 2 開発用 SDK の RTM 版

$
0
0

急に話が飛びますが、せっかくなので^^。昨日~今日にかけて、日本語用のSilverlight 2の開発用SDKがリリースされました。以下のセットでSilverlight 2 の開発環境がそろいます。

ようやくこれでツールセットが揃ったので早速インストール作業。今から Workshop のコンテンツの update 作業(β2→RTM)です。ComboBox、TabControlが追加されたのはかなり大きいポイントですね。

単体入力エラーチェックの実装パターン

$
0
0

さて Part 1. のエントリでは、業務処理の終了パターンの分類と、各アプリケーションタイプにおける基本的な実装パターンを整理しました。要点をまとめると、以下のようになります。

  • 業務処理の終了パターンは、以下のように分類される。
    image
  • 突き合わせエラーについては、バックエンドのモジュール(BC や DAC)との連携によるチェック作業が必要になる。UI 部単体でチェックが可能なのは、単体入力エラーに限られる。

.NET Framework では、UI 開発技術として、ASP.NET, Silverlight, WPF, Windows フォームなど、様々なテクノロジが提供されています。これらの技術には、いずれにも、UI 部において、単体入力エラーチェックを効率よく実装していくための機能が備わっています(これらの機能は、いずれも単体入力チェックを効率よく実装するための機能であり、突き合わせエラーのチェックや、システムエラーに関する対処を実装するための機能ではありません。いや無理矢理使えば使えるかもしれませんが;、それはこれらの機能が用意された目的や意図とはズレた使い方だと考えるべきだと思います。)

  • ① ASP.NET Web フォーム : 入力検証コントロール
  • ② Silverlight 3, WPF 3 : 例外ベースの双方向データバインド
  • ③ Windows フォーム 2.0, WPF 3.5 : IDataErrorInfo ベースの双方向データバインド

さてこれらの機能は、いずれも「単体入力チェックを行う」「フィールド単位のチェックとインスタンス単位のチェックを行う」という点においては違いがありませんしかし、その実装方法や、エラーチェックに対する考え方は、全くといっていいほど違います。この実装方法の特性の違いを理解しておかないと、単体入力エラーチェックをうまく実装できないばかりか、開発生産性をかえって大幅に損なう結果に繋がりかねません。特に、ASP.NET Web アプリケーション開発の入力検証コントロールの使い方に慣れた人が、Windows フォームや WPF などのテクノロジを遣うと、おそらく入力検証のやり方が全くといっていいほど違うため、相当に戸惑うことになるはずです。(というよりも私はむちゃくちゃ戸惑いましたよ....orz)

本エントリの目的は、これらの各テクノロジにおける、実装パターンの違い(実装方法やエラーチェックに対する考え方の違い)を明確化することです。

  • ① ASP.NET Web フォーム : 入力検証コントロール
    検証コントロールを使って、「正しい文字列」を作成する方式
  • ② Silverlight 3, WPF 3 : 例外ベースの双方向データバインド
    双方向データバインドを使うものの、反映に失敗するケースがある方式
  • ③ Windows フォーム 2.0, WPF 3.5 : IDataErrorInfo ベースの双方向データバインド
    双方向データバインドを使うが、反映に失敗するケースがない方式

なお、以下に順番に各テクノロジの実装方式を解説していきますが、基本的にはどのテクノロジであっても、UI 部でやるべきことは以下の 3 つです。

  • UI 上のテキストボックスなどから値を入力してもらう
  • 入力された値を、コードビハインドのデータ変数に取り出す
  • 単体入力チェックが済んだ値を、BC/DAC に送出する

image

実装テクノロジによる差異は、下線部のやり方の部分に出てきます。この点を意識しながら、以降の解説を読んでください。

※ (参考)なお本エントリは、各テクノロジでの単体入力エラーチェックの実装方法について、ある程度知識がある、という前提で解説を進めます。もし、各テクノロジでの単体入力エラーチェックの実装方法をまったく知らないという場合には、以下の情報を併読されることをお勧めします。

※ (注意)また本エントリは、各データ検証方式の考え方の違いを明確化することを狙っていますので、解説をかなり単純化しています。例えば、Silverlight 3 には、①に近いデータ検証を可能とする ValidationRule や、属性ベースでデータ検証を行う DataAnnotation などの機能が備わっていますが、これらについては触れません。詳細にデータ検証をご存じの方は「え゛ー?」とツッコミ入れたいところがたくさんあると思いますが、そこはちょっとだけ目をつぶっていただけるとうれしいです^^。

では、以下に順番に解説していきます。

[① ASP.NET Web フォームの場合:入力検証コントロール]

ASP.NET Web フォームの場合、単体入力チェックは検証コントロールを使って実装します。

  • 4 種類の標準のチェックロジックが用意されています。
    (必須入力チェック、フォーマットチェック、比較チェック、範囲チェック)
  • 上記の 4 種類でカバーできないチェックは、CustomValidator を使って自力で実装します。
    (インスタンス単位の単体入力チェックなどは、CustomValidator で実装します)

image

この場合の、UI 部のコードビハインドの制御コード(ボタン押下のイベントハンドラのコード)は以下のようになります。

image

このコードについて、改めてじっくり考えてみると、以下のような特徴があることがわかります。

  • ASP.NET Web フォームの検証コントロールは、「テキストボックスに、適切な値を作る」ように動作します。
  • 検証コントロールによるチェックを通過できていれば(IsValid = true なら)、データ変数への取り出しや型変換などで失敗したりすることは絶対にありません。つまり、コードビハインド内で値をテキストボックスから取り出す際には、すでに単体入力チェックが終わっている状態になっている、ということになります。
  • ただし、UI からコードビハインド内へのデータ取り出し作業自体は、自力で記述する必要があります

image

上記のような特性は、Silverlight や WPF、Windows フォームなどとは全く異なります。

まず、一般的に、Silverlight, WPF, Windows フォームといった、リッチクライアント系のアプリケーション開発技術では、通常、双方向データバインドと呼ばれるテクニックを用いて、データ検証とデータ取り出しを同時に行います

image

Silverlight, WPF, Windows フォームそれぞれで、双方向データバインドの実装方法は少しずつ異なりますが、根本にある基本的な考え方は、「UI コントロールの表示と、データソースオブジェクト間の値を、双方向にリアルタイムに同期させる」というものです。このため、双方向データバインドを利用すると、UI コントロールからのデータ取り出し作業(例:string customerName = tbxCustomerName.Text; などといった取り出し作業や、decimal price = decimal.Parse(tbxPrice.Text); といったパース処理)が不要となり、バインドされているオブジェクトを、UI から入力されたデータであるとみなしてそのまま使うことができます。これが、双方向データバインドを用いたデータ入力制御の根底にある、基本的な考え方です。

しかし、双方向データバインドにおける入力データの検証方法(単体入力チェック方法)に関しては、いくつかの方法があります。.NET Framework 内で使われている双方向データバインド時のデータ検証方法は、大別すると以下の 2 つに分類されます。

  • ② Silverlight 3, WPF 3 : 例外ベースの双方向データバインド
  • ③ Windows フォーム 2.0, WPF 3.5 : IDataErrorInfo ベースの双方向データバインド

これらは、単体入力チェックロジックを持たせる場所と持たせる方法に違いがあり、また双方向データバインドの挙動についても多少の違いがあります。このため、以下に順番に解説していきます。

[② Silverlight 3, WPF 3 の場合:例外ベースの双方向データバインド]

まず、Silverlight 3, WPF 3 の場合について解説します。これらの場合には、以下のようにして単体入力チェックロジックを実装します。

image

  • バインドするオブジェクト側に、フィールド単位のデータチェックロジックを持たせる。
    具体的には、下図 A のように、バインドオブジェクトのプロパティ setter に対して、フィールド単位のチェックロジックを持たせる。もし、UI から不適切なデータが投入された(テキストボックスから不適切な値が入力された)場合には、例外(通常は ArgumentException 例外)を throw し、値を受け取らないようにする
  • 双方向データバインドの "ValidatesOnException" 機能を使う。
    具体的には、下図 B のように、UI 部(XAML コード)にて、バインドするオブジェクトの各プロパティと、UI 項目との紐付けを行う。これにより、UI 部から入力された値が、バインドされたオブジェクトに自動反映されるようになる。ここで、ValidatesOnException 機能を有効化しておくと、バインドオブジェクトのプロパティへの反映時に失敗した場合(=例外が throw された場合)、これをエラーメッセージとして赤枠やツールチップにより表示してくれるようになる
    (※ エラーメッセージを赤枠やツールチップ表示するためには適切なスタイル定義が必要ですが、これについてはサンプルコードを参照してください。)

A. 例外ベース双方向データバインドで利用する、バインドオブジェクトの実装例

image

B. 例外ベース双方向データバインドでの、双方向データバインドの実装例(UI 部)

image

さて、一見するとわかりやすそうなこの実装方法ですが、実際には厄介な問題を抱えています。それが、UI 上に実際に表示されている値と、バインドされたオブジェクトが持っている値とのずれです。

例えば上記のアプリケーションに対して、下記のような操作を行った場合(オブジェクトへの反映に成功したり失敗したりするケースが混在する場合)を考えてみてください。

  • 顧客 ID として “3214” を設定する。(→ 反映に成功する)
  • 顧客 ID を “12345” に変更する。(→ 顧客 ID は 4 桁英数大文字のため、反映に失敗する)
  • 顧客名として “Nobuyuki” を設定する。(→ 反映に成功する)
  • 生年月日として “1973/06/07” を設定する。(→ 反映に成功する)
  • 生年月日を “1973/55/41” に変更する。(→ 日付として正しくないため、反映に失敗する)

image

この場合、UI 上に表示されている値と、バインドされたオブジェクトの中に設定されている値とがずれています。このため、業務処理のために UI から入力された値を使おう、と思った場合には、まず、双方向データバインドにエラー(反映失敗)があるか否かを確認する必要があります。バインドされたオブジェクトの中に入っている値をいきなり使うと、実は UI から入力された過去の正しい値を使ってしまうことがある、ということになってしまいます。

また、次のような問題もあります。一般的なデータエントリシートの場合、最初に画面を表示した際には何も記入されていないのが普通でしょう。しかし、そのためには、バインドされたオブジェクト側が空の状態(例えば null や空文字が入っている)でなければなりません。がしかし、このようなオブジェクトは、そもそも値として、本来正しくない値を抱えている状態になっています。

image

また、インスタンス単位の単体入力チェックを行うロジックについては、バインドオブジェクトに持たせることができません(この例だと電話番号と電子メールアドレスの少なくとも片方が入力されている、というチェック)。なぜなら、電話番号と電子メールの入力項目は、UI からずれたタイミングでひとつずつバインドオブジェクトに反映されてくるため、バインドオブジェクト側のフィールドに持たせることが困難だからです。

こうした事情から、例外ベースの双方向データパインドでは、UI 部のボタン押下のイベントハンドラを、以下のように実装することになります。

  • まず、バインドにエラーが発生していないか否かをチェックし、フィールド単位の単体入力エラーがあるか否かをチェックする。
  • 次に、バインドされたオブジェクトを見て、インスタンス単位の単体入力エラーがあるか否かをチェックする。
  • 最後に、バインドされたオブジェクトに含まれるデータを使って、業務処理を行う。

image

つまり、ここまでの解説をまとめると、例外ベースの双方向データバインドの動作イメージは以下の通りになります。

  • バインドエラーがない場合に限り、UI からの入力がすべてバインドオブジェクトに反映されている、という動作になる。このためイベントハンドラ内では、まずバインドエラーのチェックが必要。
  • 仮にバインドエラーがなかったとしても、インスタンス単位のチェックをイベントハンドラ内で行う必要がある

image

例外ベースの双方向データバインドでは、バインドオブジェクト側に、例外を使った検証ロジックを持たせているのですが、これは、バインドオブジェクトが不正な状態になることがないようにする、という考え方に基づいています。この考え方は、それだけ見ると、一般的なオブジェクト指向設計の考え方からして特に間違ってはいません。ところが、双方向データパインドは、UI 表示とバインドオブジェクトの内容との二点間同期を保つ、という考え方に基づいているため、根本的なところで概念的な相反があります。このため、上記のような厄介な実装上の工夫を行わなければならなくなるのだろうと思います。

しかし次に解説する、IDataErrorInfo ベースの双方向データバインドでは、このような概念的な相反は発生しません。

[③ Windows フォーム 2.0, WPF 3.5 : IDataErrorInfo ベースの双方向データバインド]

引き続き、Windows フォーム 2.0 や WPF 3.5 で導入されている、IDataErrorInfo ベースの双方向データバインドについて解説します。

IDataErrorInfo ベースの双方向データバインドでは、バインドオブジェクト側に、IDataErrorInfo というインタフェースを持たせます。このインタフェースは、オブジェクトインスタンス内部にエラーが含まれていることを、文字列情報として返すためのもので、これを使うことにより、前述の問題をきれいに解決することができます。

image

IDataErrorInfo インタフェースを持つバインドオブジェクトの実装例は後述しますので、まず先に概念図を示しましょう。IDataErrorInfo ベースの双方データパインドでは、以下のようにしてデータバインドを行います。

  • 入力値が正しかろうと間違っていようと、とにかくオブジェクトに反映してしまう。
  • オブジェクトインスタンスが不正な状態にある場合には、これを IDataErrorInfo インタフェースから公開する。
  • これにより、常に UI とオブジェクト内の値とが同期される。

image

前述したように、双方向データバインドは、UI とバインドオブジェクトのデータを常に同期させる技術でした。この際、データとして誤りのある内容が UI から入力された場合にオブジェクトに反映させるのかどうか、が問題になったわけですが、IDataErrorInfo ベースの双方向データバインドでは、入力内容を常にオブジェクトに反映させます。すると、バインドオブジェクトが「単体入力エラーを含んだデータを抱える」ことになります。この単体入力エラーに関する情報を IDataErrorInfo インタフェースから公開させ、これを UI コントロールに拾わせて、画面上に表示を行う、ということをするわけです。

IDataErrorInfo インタフェースを持つバインドオブジェクトの実装コード例を以下に示します。

   1:using System;
   2:using System.Collections.Generic;
   3:using System.Text;
   4:using System.ComponentModel;
   5:using System.Text.RegularExpressions;
   6:  
   7:namespace WindowsFormsApplication1
   8: {
   9:publicclass CustomerInput : IDataErrorInfo
  10:     {
  11:private Dictionary<string, string> _errors = new Dictionary<string, string>();
  12:  
  13:privatestring _id;
  14:publicstring ID
  15:         {
  16:             get { return _id; }
  17:             set
  18:             {
  19:                 _id = value;
  20:if (value == null)
  21:                 {
  22:                     _errors["ID"] = "ID は必須入力項目です。";
  23:                 }
  24:elseif (Regex.IsMatch(value, @"^[0-9A-Z]{4}$") == false)
  25:                 {
  26:                     _errors["ID"] = "ID は半角英数大文字 4 文字です。";
  27:                 }
  28:else
  29:                 {
  30:                     _errors.Remove("ID");
  31:                 }
  32:             }
  33:         }
  34:  
  35:privatestring _name;
  36:publicstring Name
  37:         {
  38:             get { return _name; }
  39:             set
  40:             {
  41:                 _name = value;
  42:if (value == null || value == "")
  43:                 {
  44:                     _errors["Name"] = "名前は必須入力項目です。";
  45:                 }
  46:elseif (Regex.IsMatch(value, @"^[\u0020-\u007e]{1,40}$") == false)
  47:                 {
  48:                     _errors["ID"] = "名前は半角英数文字 40 字以内で入力してください。";
  49:                 }
  50:else
  51:                 {
  52:                     _errors.Remove("Name");
  53:                 }
  54:             }
  55:         }
  56:  
  57:privatestring _email;
  58:publicstring Email
  59:         {
  60:             get { return _email; }
  61:             set
  62:             {
  63:                 _email = value;
  64:if (value == null || Regex.IsMatch(value, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"))
  65:                 {
  66:                     _errors.Remove("Email");
  67:                 }
  68:else
  69:                 {
  70:                     _errors["Email"] = "電子メールアドレスとして有効な値を入力してください。";
  71:                 }
  72:             }
  73:         }
  74:  
  75:privatestring _phone;
  76:publicstring Phone
  77:         {
  78:             get { return _phone; }
  79:             set
  80:             {
  81:                 _phone = value;
  82:if (value == null || Regex.IsMatch(value, @"(0\d{1,4}-|\(0\d{1,4}\) ?)?\d{1,4}-\d{4}"))
  83:                 {
  84:                     _errors.Remove("Phone");
  85:                 }
  86:else
  87:                 {
  88:                     _errors["Phone"] = "電話番号は (03)1234-5678 のように入力してください。";
  89:                 }
  90:             }
  91:         }
  92:  
  93:public DateTime? Birthday { get; set; }
  94:  
  95:// 全体整合チェック
  96:publicstring Error
  97:         {
  98:             get
  99:             {
 100:if (_email == null&& _phone == null)
 101:                 {
 102:return"電子メールアドレスか電話番号かのいずれか一方は必須入力です。";
 103:                 }
 104:else
 105:                 {
 106:returnnull;
 107:                 }
 108:             }
 109:         }
 110:  
 111:publicbool HasErrors
 112:         {
 113:             get { return (_errors.Count != 0 || Error != null); }
 114:         }
 115:  
 116:publicstringthis[string columnName]
 117:         {
 118:             get
 119:             {
 120:return (_errors.ContainsKey(columnName) ? _errors[columnName] : null);
 121:             }
 122:         }
 123:     }
 124: }

コード中の 95 行目~122 行目が、IDataErrorInfo インタフェースにかかわる部分ですが、コードのポイントをピックアップすると以下のようになります。

  • バインドオブジェクトの各プロパティは、たとえ単体入力エラーがあるデータであったとしても、とりあえずデータを受け取ります。かわりに、内部にエラー情報(エラーメッセージ)を蓄積しておきます。 
    image
  • IDataErrorInfo インタフェースには、Error プロパティ(オブジェクトインスタンス全体にかかわるインスタンス単位の単体入力エラー情報を返すためのもの)と、プロパティ名を使ったインデクサ(フィールド単位の単体入力エラー情報を返すためのもの)があります。これらを使って、単体入力エラー情報を UI 部に対して返します。 
    image

Windows フォーム 2.0 を使う場合には、UI 側に ErrorProvider コントロールを張り付けておきます。このようにしておくと、ErrorProvider コントロールがバインドされたオブジェクトの IDataErrorInfo インタフェースから自動的にエラー情報を取り出し、画面上にエラーメッセージを表示してくれるようになります。(※ 実装方法の詳細は、こちらのエントリを見てください。)

image

また、バインドされたオブジェクトにエラーがあるか否かは、バインドオブジェクトのみを見れば簡単に調べることができます。このため、UI 部のイベントハンドラ(Button_Click イベント)のコードは、以下のように非常に簡単になります。

image

このように、IDataErrorInfo インタフェースベースの双方向データバインドを使うと、綺麗な形での単体入力データチェックが実装できます。全体像を示すと以下の通りになります。

image

スマートクライアントにおける、双方向データバインドと IDataErrorInfo インタフェースを用いた単体入力チェックロジックの実装モデルには、以下のような特徴があります。

  • 単体入力チェック処理を、バインドオブジェクトに固めることができる。
    このため、モジュールの役割分担が明確になる上に、単体入力チェックロジック部分だけを重点的に単体機能テストすることもできます。
  • コードビハインドの記述が簡単になる。
    コードビハインドのイベントハンドラでは、バインドオブジェクトだけを操作すればよく、UI コントロールを触る必要がなくなります。このため、コードビハインドのコードの見通しも非常によくなります。
  • 入力仕掛り状態の維持が簡単にできる。
    バインドオブジェクトをそのままシリアル化して保存すれば、入力しかけのデータをそのまま保存しておくこともできます。

実装モデルが非常に綺麗になるので、ぜひ覚えておくとよいでしょう。

※ (注意) このモデルは Windows アプリケーションなどでは有効ですが、Web アプリケーションでは有効ではありません。なぜなら、Web アプリケーションでは、データが入力される場所(=ブラウザ上)と、データを取り出す場所(=サーバサイド)が分かれており、UI からリアルタイムでデータを取り出すことができないためです。

[3 つの単体入力チェック方式の比較]

さて、ここまでの解説を整理しつつ比較してみると、3 つの単体入力チェック方式には以下のような違いがあることがわかります。

image

ここで重要なのは、単体入力チェックモデルの優劣を議論することではありません。というのも、ぶっちゃけ、どのモデルを使ったところで単体入力チェックは実装できるわけで、好みの違いはあれど、どのモデルがより優れている、といった議論は宗教論争になりかねません;。そうではなくて、自分が業務アプリケーションを実装する際に、どのモデルを使って単体入力チェックを実装しようとしているのかを意識することが重要です。実際、.NET Framework の中に標準で含まれるデータ入力検証フレームワークを見ても 3 通りはあるわけで(実は私が気付いていないもっと別のモデルもあるかもしれません…とつぶやいておく;)、これらをごちゃまぜにしたような実装は避けなければなりません。

アプリケーションを実装する際は、一貫性が非常に重要です。どの方式を選ぶにせよ、ある特定のアプリケーションの中では「このパターンで実装する」といった具合に、モデルを定めて実装するようにしてください。

※ (参考) さらに追加のつぶやきですが、よくこうした単体データ入力���証フレームワークに関して、「○○のタイミングでエラーメッセージを表示できるようにできませんか?」「○○のような方式でエラーメッセージを表示できるようにできませんか?」といったことを聞かれます。こうしたカスタマイズは、できる場合とできない場合とがあります。というのも、もともとフレームワークというものは、「動作モデルに制約を加えるかわりに、開発生産性を大きく向上させよう」というコンセプトで作られているものであって、「どんなふうに動作させるものであっても開発生産性がよくなるもの(万能薬)」ではないからです。もし、.NET Framework などが標準で備える入力検証フレームワークの動作ではお客様要件を満たせない、ということであれば、独自に単体データ入力検証フレームワークを作成するか、または既存の単体データ入力検証フレームワークにカスタマイズを加えるしかありません。一般には、こうした問題が極力発生しないように、UI 設計段階(=業務設計段階)から、ある程度実装効率というものを意識して、フレームワークの想定している動作に併せた形での設計を行うようにします。

[まとめ]

というわけで、ここまで .NET Framework が備えている各種の単体データ入力検証フレームワークに関して、その実装モデルの違いを解説してきましたが、最も重要なポイントをまとめると、以下のようになります。

  • 単体データ入力検証フレームワークを使う上では、そもそも業務エラーとシステムエラーの分類や、単体入力エラーの分類を正しく行うことが必要になる。
  • .NET Framework が備えている各種の単体入力エラーチェック機能は、下図の枠線内の実装(開発効率)を高めるためのものである。
    image

また、単体入力チェックに対するアプローチは、ランタイムによってかなり異なります。

  • ① ASP.NET Web フォーム : 入力検証コントロール
    検証コントロールを使って、「正しい文字列」を作成する方式
  • ② Silverlight 3, WPF 3 : 例外ベースの双方向データバインド
    双方向データバインドを使うものの、反映に失敗するケースがある方式
  • ③ Windows フォーム 2.0, WPF 3.5 : IDataErrorInfo ベースの双方向データバインド
    双方向データバインドを使うが、反映に失敗するケースがない方式

これらはそれぞれに特徴があるので、データ検証に対する考え方をよく理解した上で活用することが重要です。本エントリを参考にして、さらに優れた業務アプリケーション開発を目指していただければ幸いです。

開発系エンジニアのスキルロードマップ Part 1

$
0
0

ここ最近、組織改変などの影響もあって忙しい日々が続いている今日この頃。なかなか  blog エントリ書きも滞ってしまっていて申し訳ないのですが;、最近はコンサルの現場を離れて、少しバックエンド系のお仕事をしていたりします。といっても、プロジェクトの技術レビューや提案活動は以前と変わらず実施しているのですが、そんな中、営業支援でお手伝いをしていた案件のひとつが、全社開発標準の整備・強化プロジェクト。簡単に言えば、SIer のコアコンピテンスのひとつともいえる全社開発標準を整備・強化していくことで、より強い SIer を目指していきたい、という話です。

全社開発標準の整備・強化プロジェクト、確かに SIer の強化のために開発標準のような「モノ」が重要な役割を占めるのは間違いないのですが、けれどもそれだけではダメで、それを作り、回していく「ヒト」の強化、つまり人材育成にもかなりの力を注がないと、なかなかうまくいかないものです。こうした組織強化の中でも特に人材育成の話は、私自身がずっとこだわって携わってきた領域でもあったりします。おそらく現場の開発系エンジニアにとって、ひとつ参考になる考え方になるのではとも思いますので、今回、blog エントリとして取り上げてみることにしたいと思います。

※ なお、このエントリの内容は、ITSS などの標準的なスキルマップを使ったものでもなければ、マイクロソフトとしての標準的なスキルロードマップというものでもありません。私自身が現場の『肌感覚』として、開発系エンジニアがどうやってスキルアップし、生き残っていくべきなのか? というものを考えてきた結果としての、一つの考え方にすぎません。その点についてはあらかじめご了承ください。

[Agenda]

  • システム開発の在り方の変質と、開発系エンジニアへの要求の変質
  • SIer にとってのテクニカルスキルの重要性
  • 開発系エンジニアのスキルアップの難しさ
  • 座学(トレーニング)と OJT のバランス
  • システム開発プロセスの基本とエンジニアの分類
  • システム開発のフェーズとエンジニアの対応関係
  • スキルタイプごとのトレンドと育成ポイント
  • キャリアパスとしての技術コンサルタント

[システム開発の在り方の変質と、開発系エンジニアへの要求の変質]

昨今の開発系エンジニアは、開発現場で過酷な労働を強いられていることが多いと思います。一昔前は花形産業としてもてはやされていた IT 産業は、今や 21 世紀の新しい “3K” (キツい、帰れない、給料が安い)と揶揄されるような状況。中でも SI (システム開発)の仕事の厳しさは、私が改めてここで書くまでもないことと思います。なぜこんなひどいことになってしまったのか? それを総合的に語ることは簡単ではありませんが、私自身が開発現場にコンサルタントとして携わっていて強く感じることのひとつに、「開発技術の理解や習得(テクニカルスキル)が相対的に軽んじられてしまっている」ことがあります。実際、「スキル不足」「スキルの低さ」が開発のトラブルや失敗を招いているケースは後を絶たず、現場のトラブルを見てみると、そもそもどうしてこんなことをしたのか?と言いたくなることもしばしば。今の時代ほど、テクニカルスキルが重要な時代はないとすら私は思うのですが、なぜテクニカルスキルが重要なのか、どうしてそうなったのかについては、あまり理解されていないように思います。

テクニカルスキルが従来以上に重要になったのは、現在のシステム開発が、数十年前に見られたような労働集約型のビジネスとは言いづらくなってきたことに起因しています。システム開発における SIer のゴールは、システムを「早く・安く・上手く」作ることであり、それを実現するために、以下のような考え方とアプローチをとっていました。

  • 開発標準化を行い、プロセスと開発方法を標準化し、「誰でも彼でも作れる」ようにする。
  • これにより、スキルの低いエンジニアや単価の安いエンジニアでも、品質の高いアプリケーションを作れるようにする。

端的に言えば、「エンジニアのスキルの低さ」を「開発標準化」でカバーする、という方法だったと言えますが、この考え方は、残念ながら現在ではなかなか通用しなくなっています。

image

従来の考え方が通用しなくなった最大の理由、それは昨今の開発ツールやフレームワークなどの技術進化です。例えば今から約 10 年前、VB6 の時代に、データベースからデータを取得しようと思った場合には、以下のようなコードを記述する必要がありました。(※ コードを理解する必要はありません、なんとなく見てください)

Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset 
 
Set con = CreateObject("ADODB.Connection")
Set cmd = CreateObject("ADODB.Command")
Set rs = CreateObject("ADODB.Recordset")
 
' データベース接続を開きます。
con.Open "Provider=SQLOLEDB;Data Source=sqlsrv00;Initial Catalog=pubs;Trusted_Connection=yes" 
 
' Commandオブジェクトで利用するコネクションを指定します。
Set cmd.ActiveConnection = con
' コマンドタイプをSQL文実行として指定し、SQL文をセットします。
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT * FROM authors"
' 読み出し専用の切断レコードセットのためのオプションを指定します。
rs.CursorLocation = adUseClient
rs.CursorType = adOpenStatic
rs.LockType = adBatchOptimistic 
 
' レコードセットを取得します。
rs.Open cmd
 
' 切断レコードセットにするため、コネクションを切断します。
' また同時に、不要となったオブジェクトを破棄していきます。
Set cmd.ActiveConnection = Nothing
Set cmd = Nothing
Set rs.ActiveConnection = Nothing
 
' データベース接続を切断し、解放します。
con.Close
Set con = Nothing
 

ところが、今の時代、例えば ADO.NET や LINQ to Entity Framework を使うと、上記の処理は数行で書けてしまいます。

using (pubsEntities pubs = new pubsEntities())
{
    var query = from a in pubs.authors select a;
    List<author> result = query.ToList();
}

ひと昔前であれば、数 10 行~数 100 行のコードを書かなければいけなかった作業が、今や本質的な作業を表す数行のコードを記述するだけで済むようになった、ということなわけですが、この変化は、以下の 2 つを意味します。

  • ブルーカラー的な単純労働作業がなくなった、端的に言えばコピペ作業はなくなった、ということ。
  • 設計と実装の距離が非常に短くなった、端的に言えば設計者がその内容を直接コードとして表現できるようになった、ということ。

つまり、現在の開発技術のトレンドは、手を動かすだけの労働集約的な作業は少なくなり、ホワイトカラー的な設計者が直接かつ素早くモノ作りをしていくことができる方向に進化している、ということを意味します。結果として、現在の技術トレンドは、シニアな開発メンバによる、少数精鋭の SWAT チーム的な開発に適したものに進化してきている、ということになります。

image

これは .NET、Java を問わず、昨今の開発技術全般について言えることですが、現在の開発技術(言語やツール)の怖いところは、ジュニアなコピペエンジニアの仕事をなくしてしまう、ということです。実際、現在の .NET Framework や Visual Studio は極めて高い生産性ツールであるものの、誰もがその高生産性を十分に引き出せるというわけではありません。設計スキルやアーキテクト的な素養があればあるほど、その高開発生産性をうまく引き出すことができる、という類のツールです。その結果、スキルの高い人はますます稼ぎ、スキルの低い人は簡単に淘汰され価格競争に巻き込まれる、という構図ができあがります。こうしたことからも、特にデベロッパーにとってスキルが極めて重要であることがわかるかと思います。

image

[SIer にとってのテクニカルスキルの重要性]

このテクニカルスキルの重要性は、直接、内部設計や実装などの開発作業に携わらない SIer にも当てはまります。なぜ SIer においてもテクニカルスキルが重要なのかは、以下のようなことを考えてみればすぐにわかります。

SI では、たくさんの人がプロジェクトに関与します。こうした中で、開発にかかる総コストを低減し、自社の付加価値を高めていくために SIer が行ったことは、実装作業やテスト作業といった、比較的ブルーカラー色の強い業務を、協力会社やオフショアに移転していくことでした。すなわち、自社のプロパー要員を、極力、リーダーやプロジェクト管理業務に注力させ、自社のコアコンピテンスを、単価の高い高付加価値業務にシフトしていくことで、企業価値を高めていこうと考えたわけです。

image

この考え方自体はごく普通の話ですし、市場原理に基づけば、ごく当たり前の流れともいえます。しかし問題なのは、コアコンピテンスにリソースを集中させすぎると、かえってコアコンピテンスを失う結果につながってしまう、というポイントです。例えば、F1 レーサーを考えてみてください。F1 レーサーのメインの仕事は F1 カーを走らせることであり、その部分がお金を生み出しています。ですが、だからといってそればかりに注力して、日々の筋トレを怠ってしまったらどうなるでしょうか? しばらくの間は高付加価値業務を維持できるかもしれませんが、その後は筋力を失い、結果として F1 カーをドライブできなくなり、仕事を失うことにつながっていきます。筋トレで稼いでいるわけではありませんが、だからといって筋トレをやめたら F1 カーを走らせられなくなる、という側面があるわけです。

この例からも明らかなように、コアコンピテンスというのは、それが単体で存在しているわけではありません。実際には、コアコンピテンスを支えているベースラインスキルというものが存在します。ベースラインスキルというものは、それ単体では価値を生み出すものではなく、またそれ自体を専門にする必要性のあるものではありませんが、それを失ってしまうとコアコンピテンスを維持できなくなる、という類のものです。コアコンピテンスを考える場合には、それがベースラインスキルとは不可分であることを意識することが重要であり、日々、ベースラインスキルを維持すること(簡単に言えば基礎トレ)も同時に考えなければなりません。

imageimage

SIer の場合、プロジェクト管理やチームリードを専門として行うことで付加価値を守っており、自ら実装や内部設計を行うわけではありません。これは今後も同じでしょう。ですが、だからといって技術のことを知らなくてもよいということではないはずです。自ら開発をしなくても、技術の肝を理解していなければ、協力会社やオフショアに依頼したものをレビューすることすらできなくなってしまいます。「自分ができないから外注する」のか、「自分でもできるけれどもコストが安いから外注する」のか、どちらであるのかは天と地ほどの違いがあります。SIer のコアコンピテンスを守るためには、やはりテクニカルスキルがベースラインスキルとして求められるのだと思います。

そういう意味において、SIer の場合、協力会社やオフショアなどの外注に依存しきらないようにすることが大切です。例えば、小規模案件であれば自社のみで開発したり、大規模案件であっても若手のプロパーメンバーを実作業員として参画させて現場経験を積ませたりすることで、実作業の『勘所』を見失わないようにすることが重要です。そうすることで、本当の意味での自分たちのコアコンピテンスを守っていかないと、企業価値が損なわれていってしまいます。

image

プログラマーではなく SE、SE ではなく PM、PM ではなくコンサル……といった具合に、名前を付け替えていったとしても、中身が伴わなければいつかは破綻します。我々エンジニアが自分たちの市場価値を守っていくためには、足腰をきちんと鍛え、地に足の着いた、本当の意味での実力を身に着け、発揮していかなければなりません。そのベースラインとして、テクニカルスキルが極めて重要であることを、今一度改めて認識することが大切なのだと思います。私自身、コンサルタントをしているときは、(別にコーディングを業務として行うわけではないのですが)折に触れてコーディングの訓練をしていましたし、マネージャ業務をやっている現在でも、折に触れて最先端技術を学習するようにしています。勘所がわからなければ、コンサルティング業務もマネージャ業務もきちんとできないからです。

[開発系エンジニアのスキルアップの難しさ]

……などと、とりあえず理屈をこねてみたわけですが、現場にいる開発系エンジニアの実感としては、「理屈は分かるけど実際にはムリでしょ;;;」というのが偽らざる本音だと思います。実際、開発現場のエンジニアのスキルアップや育成を拒む要因はたくさんあります。代表的なものという意味だと、以下のようなものでしょうか。

  • 技術要素が多すぎて、とても追いつけない。
  • 勉強しようにも、どこからどうやって勉強していけばよいのか、ロードマップがない。
  • そもそも物理的に時間が取れない。

image

こうした厳しい状況の中で、羅針盤もなしに「勉強しろ」と命じたところで回るわけがないですし、意欲的なエンジニアもどうやって勉強すればよいのか途方に暮れてしまうと思います。自分で勉強するにせよ、会社として育成するにせよ、しっかりと基本に立ち返った考え方をする必要があります。そのための要点は、以下の 2 つです。

  • 座学(トレーニング)と OJT のバランスを取ること
  • 「開発系エンジニア」を適切にタイプ分類し、それぞれに適した学習ロードマップを持つこと

それぞれについて、説明していきたいと思います。

[座学(トレーニング)と OJT のバランス]

スキル強化を考える際、昨今は OJT (現場で業務をしながら仕事を覚える)が非常に重視されるようになりました。なぜなら、IT 業界では座学では学べないノウハウがたくさんあり、それは現場の実務経験からしか学ぶことができないからだ、だから新入社員のトレーニングは最小限で済ませて早く現場に送り出すのだ……などと言われていますが、私自身の肌感覚からすると、それはトレーニング予算を削減するための、体(てい)の良い言い訳ではないか? と正直思います。というのも、現場の OJT 経験だけでは、確たる実力が身につかない、あるいは身についたとしてもとてつもなく効率が悪くて時間がかかりすぎるからです。

小学生や中学生の頃の勉強を思い返して欲しいのですが、勉強するときに、教科書もろくに読まずにいきなり問題集に取り掛かる方法は、どう考えても学習効率が悪いです。仮にその方法で問題集を丸暗記して直近の期末試験を何とか乗り越えたとしても、ほとんど定着しませんし、ちょっと角度を変えた応用問題を出されたとたんに解けなくなるものです。言うまでもなく、「理論」と「実践」の両方が伴って、初めて応用力のある実力、すなわち現場での実力となるものですが、昨今のシステム開発の現場では、こうした基礎トレーニングが思ったようにできていないのが実際ではないでしょうか?

特にここ最近は、教育に対する投資を後ろ向きに考えがちな風潮があるようにも思います。トレーニングを受講してもらって人を育成しても、すぐに辞めてしまうのではないか? そもそも多忙を極める現場からトレーニングに人を出したら現在のプロジェクトが回らなくなるのではないか? などなど、心配事項は尽きません。確かに、座学で理論ばっかり勉強していても頭でっかちになるばかりですが、かといって OJT だけでは基礎学力は身に付きませんし、基礎学力が身についていないと、場当たり的・その場しのぎのパッチ的な解決策に走りやすくなります。例えば、インターネットから検索したソースコードを、意味も理解せずにそのままコピペしたようなアプリケーションコードを現場で見かけたりしませんか? そうしたアプリケーションコードは、潜在バグも多いものです。こうしたことを防ぐためには、まずトレーニングや座学で幹を作り、そこに OJT で枝葉を補い、応用力をつけていくことが大切です。枝葉だけかき集めても、しっかりとした幹を持った大木にはなりません

image

私自身の肌感覚としては、座学(業務時間外での学習や書籍などでの自習も含む)と OJT とのバランスは、だいたい 1 : 4~5 ぐらいではないかと思います。座学ばっかりやっている必要はないけれども、最低でも時間ベースで 10~15% ぐらいは基礎トレをしていないと、現場で力を発揮できないという印象を持っています。私自身、コンサルタントをやる上では、意識的にこうした学習時間を取るようにしていました。

ただし、座学での学習は、やみくもに時間をかければよいというものではありません。きちんとした学習・成長ロードマップを持ち、「どこを目指すのか?」(=自分の専門性)を意識した学習が必要になります。この部分に関しては、日本の考え方は少し遅れているように感じていますので、少し深掘りしてみたいと思います。

[開発系エンジニアのタイプ分類]

開発系エンジニアがスキルアップを考える際に重要なのは、自分の専門性をどのように捉えるのか?という点です。日本の場合、開発系エンジニアに関しては比較的考え方が古く、今でもゼネラリスト的な考え方を取ることが多いです。もう少し説明すると、下図にあるように、

  • まず新入社員は、情報工学の基礎や、ネットワーク、DB、プログラミングなどの基礎を学習する。
  • 次に、システム開発プロセスや運用管理、セキュリティなどを学習する。
  • それが終わったら、業界業種知識などを伸ばす。
  • さらにプロマネスキルやコンサルティングスキルを伸ばしていく。

のように、ひとつのロードマップで成長していくようなモデルを取っていることが多いと思います。実際、日本の場合、プロジェクトマネージャ、SE、プログラマー、テスターの間には明確な職位的上下関係があり、まるで昔の士農工商制のように扱われていることも多いと思います。新人はまずテスターを経験し、頭角を現した場合にはプログラマーを経験させ、そこでさらに頭角を現した人には SE になってもらい……といった具合です。

しかし、米国などでは、より専門性の強い職種への細分化が進んでいます。新卒エンジニアはまず基礎学力をつける、というところは変わりがないでしょうが、ある程度の経験を積んだあとは、業務 SE やアーキテクト、デベロッパー、テスター、プロジェクトマネージャーなどに職種が分かれ、それぞれの職種の専門スキルを深めていくことになります。

image

もちろん米国などにおいても、小規模な組織や開発においては、一人が複数の職種を兼任することは当然あるでしょう。しかし、少なくともそれぞれの職種には専門性がある、と考えているところが重要なポイントです。簡単に言えば、上級のテスターには上級のテスターとしてのスキルやノウハウや専門知識というものが存在するし、上級のアーキテクトには上級のアーキテクトとしてのスキルやノウハウや専門知識が存在する、ということが認知されているというところがポイントです。このため、特に高度な専門性を要求する会社の場合、中途採用の枠などは、最初から個別に分かれています。例えばこちらはマイクロソフトの採用ページですが、募集されている職種を見てみると、ソフトウェアエンジニアリングにおいて、製品計画、開発、製品テストなどが最初から分かれていることが確認できると思います。

image

開発系エンジニアの職種(=専門性・専門分野)をどのように細分化するのか? に関しては、様々な方法があります。例えば、Visual Studio にも組み込まれている MSF (Microsoft Solution Framework)と呼ばれる方法論でよく使われるロール(職種)としては、① プロダクト管理、② プログラム管理、③ アーキテクチャ、④ 開発、⑤ テスト、⑥ リリース管理、⑦ ユーザーエクスペリエンス、があります。が、開発対象となるシステム規模によってもこの細分化の程度は変わってくるため、私自身はこれをもう少し簡素化した以下の 5 つのロールを、基本的な開発系エンジニアの専門分野と捉えるとよいと思っています。これぐらいの方が覚えやすいし、使いやすいでしょう。

  • 業務 SE(要件定義や業務設計を担当)
  • アーキテクト(方式設計などの設計・実装標準化を担当)
  • デベロッパー(内部設計や実装を担当)
  • テスター(テストと品質評価を担当)
  • プロジェクトマネージャ(プロジェクト管理を担当)

なぜ開発系エンジニアがこのような 5 つの専門職種に分類されるのかは、システム開発のプロセスを理解すれば自ずと理解できます。これについて解説します。(次回に続く……;)

開発系エンジニアのスキルロードマップ Part 2

$
0
0

(このエントリは Part 1 からの続きです。)

[システム開発プロセスの基本とエンジニアの分類]

業務システムの開発には様々な人が関与しますが、どのような規模のシステム開発であったとしても、少なくとも以下のような役割(ロール)のメンバーが必要になります。そして、それぞれのロールには、他のロールとは異なる専門性が求められます。この 5 つのロールを理解することは、開発系エンジニアが自らの専門性を深めていく上で極めて重要な指針となるものですので、これらについて解説します。

image

① 業務 SE

業務 SE とは、お客様から業務要件をヒヤリングし、その情報を元に、実装可能な業務仕様を取りまとめていくエンジニアです。上流寄りの業務 SE はどちらかというと業務コンサルに近く、下流寄りの業務 SE はどちらかというと開発者に近いロールになりますが、いずれにしても、お客様の業務内容を理解し、それを仕様や設計に落とし込んでいくというのがポイントになります。このため、業務 SE の人たちには、業務に関する専門知識(ドメインエキスパート)や、モデリングに関する専門知識(データモデリングなど)が要求されます。

② アーキテクト(アプリケーションアーキテクト)

アーキテクトとは、アプリケーションやシステムインフラに関する方式設計(アーキテクチャ設計)を行うエンジニアです。簡単に言うと、どのような方式(アーキテクチャ)でその業務システムを実現するのかを決定する役割になります。このため、この作業を行うためには、業務に関する知識と理解、そして開発技術に関する、幅広く深い知識と理解が必要になります。また、(後述しますが)業務 SE とデベロッパーの橋渡しをすることも、重要なロールの一つになります。

③ デベロッパー

デベロッパーとは、業務 SE のまとめた業務仕様に基づいて、内部設計と実装作業を行うと共に、テストチームに引き渡し可能な品質のアプリケーションを作り上げる人たちです。ひと昔前であれば、「プログラマー」と呼ばれていた人たちに相当します。ただし、現在のプログラム開発は、前回のエントリに書いた通り、いわゆるブルーカラー的なコピペ作業ではなくなっています。つまり、業務 SE から渡された業務仕様書を元に、プログラムの内部設計を書き起こし、それをコードとして組み立てられるスキルが求められているわけです。このような、内部設計作業からプログラミング(実装作業)、そしてプログラムコードから実際のバイナリファイル(成果物)を作り上げていく人たちを、デベロッパーと呼びます。ですので、デベロッパーの人たちには、プログラミングに関する深い専門知識が求められるだけではなく、業務仕様を理解し、プログラム内部設計を書き起こせるスキルも求められています。

④ テスター

テスターとは、デベロッパーの人たちが作ったアプリケーションを体系的・網羅的にテスト・評価する方法を考え、バグの発見と、アプリケーション品質の定量化を行っていく人たちになります。「テスター」というと、テスト計画やテストケースに基づいて、実際にアプリケーションの操作を行う人、というイメージがありますが、実際にテストを行う場合には、優れたテスト計画やテストケースを作成することの方が圧倒的に重要であり、この部分には極めて高いスキルが要求されます。こうした、優れたテスト計画の立案やテストケースの設計を行い、得られたテスト結果から各種の品質指標数値データを算出していく役割を担うのがテスターです。(このため、テスターは QA (Quality Assurance、品質保証)担当と呼ばれることもあります) ですので、テスターの人たちには、業務仕様や各種テストツールに関する深い知識はもちろんのこと、各種の統計解析・統計分析スキルを持っていることが要求されます。

⑤ プロジェクトマネージャー

プロジェクトマネージャーとは、前述したような各種のロールのメンバーが最大限の能力を発揮できるように各種の調整を行うと共に、プロジェクトの全体進行の進捗管理などを行う人になります。(小規模開発でロールを兼任せざるを得ない場合でなければ)プロジェクトマネージャー自身はシステム開発作業を直接行うことはなく、チームメンバーやステークホルダーとのコミュニケーションに力を注ぐことになります。このため、求められる専門知識やスキルも、技術知識というよりは、コミュニケーション能力、リーダーシップ、交渉力、問題解決力など多岐に渡ることになります。開発系エンジニアという枠組みで捉えるべきか否かは難しいところですが、開発系エンジニアとしてのスキルや知識がないと、システム開発のプロジェクトをうまく回すことは難しいのもまた事実なため、ここで取り上げておくことにしました。

なお、この 5 種類のロールに関して誤解されやすいポイントが 2 つありますので、少し補足説明を加えておきます。

  • アーキテクトとデベロッパーは別物
  • デベロッパーとテスターは別物

アーキテクトとデベロッパーの違い

システム開発において、チーム内にアーキテクト(アプリケーションアーキテクト)を置くのは、アプリケーションの作り方を一定化させて品質を安定させるためです。これについて説明します。

一般に、アプリケーションは、業務 SE が行った業務設計に基づいて、デベロッパーが作成していく、という流れになります。この際、統一的な設計思想のないまま業務要件定義書に基づいて実装が行われると、場所ごとに作り方がまちまちになってしまって保守できなくなったり、性能の出ないアプリケーションができてしまったり、セキュリティの確保の方法が一貫していないアプリケーションになってしまう危険性が高くなります。これを防ぐために、チーム内にアーキテクトを配し、アーキテクトの人が方式設計、すなわちアプリケーションの実現方式に関するプランを固め、これを徹底します。このようにすることで、複数人のデベロッパーが関わっても、同じようなアプリケーションが出来上がり、各種品質が担保されるようになります。

image

「アーキテクト」と聞くと、非常に華々しくカッコいい職種を思い描かれる方も多いかと思いますが、そうした方は、アーキテクトという職種を誤解しています。アーキテクトを設置する『目的』は、たくさんいるデベロッパーの方々に、一貫した設計思想に従った、品質の高いアプリケーションコードを書いてもらうことです。そのためにアーキテクトは、日常的にはかなり地味くさい & 泥臭い仕事をたくさんします。例えば…

  • 業務 SE の人たちに頭を下げて、業務のことを教えてもらう。
  • 日々、開発技術を一生懸命勉強して、その要点をきっちり理解する。
  • そのシステムをどんなふうに作るのがベストなのかを考え、それをアーキテクチャとしてまとめる。
  • 自分が考えたアーキテクチャをデベロッパーの人たちに理解してもらうために、解説書(開発標準書)をまとめる。
  • デベロッパーの人たちにその開発標準書を理解してもらうために、頭を下げてまわって一生懸命説明する。
  • デベロッパーの人たちが作ったアプリケーションコードをレビューして、定着度を確認したり、修正を依頼したりする。

などです。編成された開発チームのデベロッパーのスキルが高くない場合には、「本当はこうするといいんだけど……」というところを断念して、意識的にアーキテクチャを簡単化するようなこともしばしばあります。この話からも分かるように、難しくてかっちょいいアーキテクチャを書いて、それを開発チームに丸投げして放置し、自分の書いたアーキテクチャが理解できないのはデベロッパーのスキルが低いのが悪いんだ! などと思ってしまうような人は、アーキテクトには全く向いていません……というよりむしろ害悪です;。日々鍛錬に励み、他人に頭を下げることを厭わず、最終的に出来上がってくるアプリケーションの品質を高めるために何でもやろう! という気概を持って、プロジェクトの最後まで面倒を見る覚悟のある人だけが、アーキテクトを名乗る資格があるのだと私は思います。

……と、ちょっと話が逸れましたが、アーキテクトのこのような業務内容を意識すると、アーキテクトはプロジェクトメンバ全体に対して 10~15 % 程度必要だろうと思います。もちろん、すべてのアーキテクトがハイスキルである必要はなく、一部のアーキテクトがリードを務め、残りのアーキテクトはサブの位置づけでアーキテクチャの定着に努めるメンバ、といった形にすることも多いと思います。が、いずれにしても開発現場を見てみると、このアーキテクトロールの人員が不足しているケースは多いです。このような場合には、適宜、社内の関連他部署や社外のコンサルティングサービスなどを活用し、アーキテクトロールの不足を補うようにすることをおすすめします。

デベロッパーとテスターの違い

デベロッパーとテスターも、日本では極めて混同されやすい職種ですが、やはりこの二つも、専門性が全く異なります。一言で言えば、デベロッパは「作ることの専門家」、テスターは「品質評価の専門家」です。これは、実際の作業を意識しないとわかりにくいので、こちらの図を使って説明します。

image

例えば、マイクロソフトにおいてあるパッケージ製品(例えば Office 2010)を開発する場合を想定してみます。この場合、デベロッパーチームは、単純にコーディングをするだけではなく、それをビルドし、バイナリパッケージ(簡単に言えば半完成品)を作成します。テストチームはこの半完成品を受け取り、品質検査(テスト)を行い、品質評価を行います。品質に問題がある場合(すなわちバグがたくさん見つかってしまった場合)には、バグ報告票を起票してバグの修正を依頼し、デベロッパーチームに修正を要求する、という形になります。そして再度、デベロッパーチームから完成品を渡してもらい、これを再度、品質評価する、という流れを繰り返します。

この一連の流れにおいて重要なのは、テストチームのメンバーは、ソースコードを見たり触ったりせず、またバグの原因追究も行わない、という点です。あくまで完成品をエンドユーザと同様に触ったり使ってみたりして、問題がないかどうか、品質を検査します。そして問題があった場合には、その問題を的確に報告する(=バグの再現手順をデベロッパーチームに報告する)ことを行いますが、そのバグの原因追及や修正作業はテストチームは一切行いません。バグの原因を追究し、それを修正するのはデベロッパーの責任だからです。というか、バグの修正方法などに中途半端にテスターが首を突っ込むと、「アプリの作りもよく知らないのに、適当な修正方法を言ってくれるな!」とケンカになります;。デベロッパーは料理人、テスターは料理評論家のようなものです。料理評論家は「おいしくないから修正すべき」ということだけを伝えるべきで、料理法やら素材やらを論じてうっかり踏み込むと、ケンカになってしまいます;。

このように、デベロッパーとテスターの間には、明確な役割の違いがあります。まとめると、以下のようになります。

  デベロッパー テスター
主な役割 完成品を作る 完成品の品質を評価する
ソースコード 触ることが可能 触ってはいけない(出来上がったパッケージ品だけを触る)
作業場所・作業環境 開発環境 エンドユーザと同等の環境(テスト環境)
バグ出しの方法 場当たり的なデバッグ作業でバグ出しを行う 事前に網羅的に設計したテストケースに基づいてバグ出しを行う
バグ出しのやり方・考え方 ホワイトボックステスト(ソースコードを見ながら考えて作業) ブラックボックステスト(ソースコードを見ずに作業)
バグを発見した場合 その場ですぐにソースコードを直してよい
(※ テストフェーズより前の場合)
バグ報告票(バグの修正依頼票)を起票し、デベロッパーチームに直してもらう
バグの修正 行う 行わない(バグの再現手順を報告するのみ)

日本の場合、テストとデバッグ作業が区別されていないことが多く、また、「完成品」を通して、デベロッパーチームとテストチームが連携する、という考え方が取られていないことも多いです(いやそれどころか、テストチームが独立していないことの方が多いでしょう)。しかし、『作ること』と『それを評価すること』とは全く異なるスキルセットが要求されますし、どちらが偉いという性質のものでもありません。実際、米国などでは、テスターとデベロッパーとの間に職位的上下関係はありません。

image

なお、稀に、バグ出しはテストチームの責任だと考えている人がいますが、これも誤りです。なぜなら、そもそもテストチームがテスト作業を始める前(すなわちテストフェーズに入る前)に、デベロッパーチーム側で十分な単体機能テストとデバッグを行い、ふつうに使ったぐらいではバグが出たりすることはない程度まで品質を高めておくことが必要だからです。というのも、テストフェーズに入った後、テストチームがバグを見つけた場合には、バグ報告票を起票してきちんとバグ管理をしなければならなくなりますが、バグの数があまりにも多い場合には、バグ管理そのものがまともに機能しなくなります。つまり、テストフェーズでバグ管理を適切に行うためには、管理できる程度までバグの数が減っていることが必要です。このためには、テストフェーズに入るまでに、デベロッパーチームが十二分にデバッグを行い、バグを摘出しておくことが必要です。

ちなみに、マイクロソフトの製品開発の場合には、テストフェーズに入ったのち、テストチームが一定期間以内に一定数以上のバグを発見した場合には、テスト不能として、テストチームがデベロッパーチームに対してフェーズの差し戻しを要求することができるルールになっています。

デベロッパーチームがきちんとデバッグしたものを、エンドユーザ視点で品質検査して、わずかに残っているバグを徹底的に摘出し、さらに品質を評価することが、テストチームのテスターには求められているわけです。かなりの高いスキルが要求されること、またデベロッパーとは全く違う視点やスキルが求められることが、容易に想像できるかと思います。

[システム開発のフェーズとエンジニアの対応関係]

さて、ここまで、システム開発に必要となる基本的な 5 つのロールを説明しました。

  • 業務 SE
  • アーキテクト
  • デベロッパー
  • テスター
  • プロジェクトマネージャ

この 5 つのロールの人たちが、システム開発の各フェーズにおいて様々な作業を行い、システムを開発・リリースしていきます。プロジェクトのサイズや状況によって実施すべきタスクや期間はかなり変わってきますし、大型のプロジェクトでは構成管理チームやインフラチーム、運用チームなども編成される形になってきますが、おおざっぱには以下のような形になります。

image

……とまあ、こんな感じのイラストをよく見かけると思います。が、正直なところこうした作業図はとてもじゃないけれども覚えられない、というのが実際のところだと思います。私としてはこの図を暗記するよりも、以下のような形で勘所を覚えておくことをおすすめしたいです。

一般に、システムは「要求されたものを作る」わけですが、要件は以下の 2 つに大別されます。

  • 機能要件:「どんな機能が必要なのか?」「何を作るのか?」
  • 非機能要件:「各機能をどの程度の品質で作るのか?」

この二つの要件を明らかにし、それを満たすようにシステムを開発していくのがシステム開発です。このように考えると、システム開発の流れと大まかなタスクは、以下のようにまとめることができます。

image

この 2 つの流れにおける各タスクを実践していく際には、当然、得手不得手に応じたスペシャリストを割り当てることが必要です。先ほどの 5 つのロールとのマッピングを考えると、以下のようになります。

image

もちろん、実際の作業では、ここに示した人たちだけが各タスクを実施するわけではありません。例えば、結合機能テストを実施している最中は、当然、デベロッパーの人たちがバグの修正作業を行っているはずです。また、業務設計が終わった後、業務 SE の人たちはマニュアルを書いたり、場合によってはテスターロールとなってテスト計画やテストケース設計をしていることもあるでしょう。ですので、ここに書いたのはあくまで開発プロセスにおける骨組みで、ここに枝葉を補って、最終的には WBS やスケジュールを作成していく必要がありますが、こうした骨組みの部分を理解しておくことは極めて重要です。このような形で開発プロセスを理解しておくと、なぜ前述の 5 つのロールが開発において極めて重要な意味を持つのかがお分かりいただけるかと思います。

では、こうした作業を実践していくために、各ロールがどのようなスキルを持っているべきなのかについて考えていきたいと思います。(次エントリに続く。)


開発系エンジニアのスキルロードマップ Part 3

$
0
0

(このエントリは Part 2 からの続きです。)

さて、Part 2 のエントリでは、開発系エンジニアの 5 つの分類を示しました。この 5 つのロールに関して、昨今のトレンド及び育成ロードマップがどのようなものであるべきか、自身のスキルを高め、市場価値を高めるためにはどうならなければいけないのかについて考えてみたいと思います。

image

[① 業務 SE (要件定義・業務設計)について]

私がこの IT 業界に飛び込んだ頃は、まだ要件定義や業務設計に関してはあまり情報が整理されていませんでした。しかし、最近は要件定義手法に関する研究が進んできており、書店にも多数の書籍が並ぶようになりました。「要求開発」といった手法や考え方が出てきたり、また特に品質特性(非機能要件)の領域に関する研究の深化などにより、従来に比べてかなり方法論が整ってきた感があります。また、パッケージ製品や SaaS が適用できる領域も増えつつあり、メールなどの IT インフラ領域だけでなく、販売管理や顧客管理などの領域でも、パッケージ製品や SaaS 利用ができるようになってきています。こうした中で、「業務」のことだけを見て、技術のことを知らずに業務設計を行うと、「実現不可能な」システムになってしまったり、「コストがかかりすぎる」システムになってしまう危険性もあります。

こうしたことを踏まえると、業務 SE のスキルロードマップとしては以下のようなものが考えられます。すなわち、まず入社直後は一般的なモデリング技術や業務分析手法を習得して基本的なスキルを身に着け、その後、技術スキルをベースとして、業界・業種知識を伸ばしていきます。最後には、業界・業種に関する深い造詣を元に業務コンサルティング領域へと踏み出していき、業務の To-Be モデルの策定や、実現可能な業務システムの提案・設計ができるようになっていくことが一つのゴールとなってくるでしょう。

image

[② アーキテクト(方式設計)について]

前述したように、アーキテクトはシステムの実現方式やアプリケーションの作り方を決定し、それを開発チームで実践する役割を担っています。このため、アーキテクトロールは開発チームの中で非常に重要な役割を担っていますが、アーキテクトを取り巻く環境は厳しくなる一方です。特に厳しいのは、昨今の開発技術の複雑化です。UI の多様化(Web, RIA, スマクラ, モバイル)、クラウドコンピューティングなど、システムのプラットフォームが一気に複雑化してきており、こうした多種多彩な技術に深い造詣を持つエンジニアの希少性が一段と高くなってきています。このため、どこの会社もアーキテクト不足に悩んでいる、というのが実情でしょう。

また、アーキテクトの重要性が頻繁に語られる割には、「アーキテクチャ」の詳細に関する業界標準がないのも大きな課題です。例えば、何を以て「アーキテクチャ」とするのか? また標準化としては何をすべきか? に関しては、業界内で統一的な見解がなく、例えば SIer 各社がノウハウを持っていたとしても、社外に対して公開されたり share されたりすることがなく、ノウハウを外部から入手することが困難、という難しさもあります。また、方式設計(アーキテクチャ設計)は「机上の空論」ではなく「実践可能」でなければなりませんので、高度なテクニカルスキルも求められます。簡単に言えば、多種多彩な開発技術に対して深い造詣を持つと共に、そこからアーキテクチャに関して自分なりの見解を組み立てるスキルが求められるのが、アーキテクトというロールだと言えます。

ただし、最終的なゴールは前述したように、自分でアーキテクチャを考えるだけではなく、それを流布させ定着させていくことです。ここまで含めて考えると、アーキテクトとして目指すべき最終的なゴールは、教育的視点まで含んだ開発標準化の実践、というところになっていくでしょう。

image

[③ デベロッパー(内部設計・実装)について]

Part 1 にて解説したように、昨今のアプリケーション開発の特徴は、内部設計と実装の距離が大きく縮まっていることです。すなわち、実装の手間が軽減されることで、内部設計がダイレクトに実装に直結するスタイルに近づいてきており、内部設計者がそのまま実装することが可能になってきています。このことは、裏を返せば、内部設計スキルのないエンジニアがツールやフレームワークを誤用すると、品質に大きな難のあるアプリケーションが出来やすいということでもあります。このため、デベロッパーの人たちは、コーディングのスキルだけでなく、内部設計のスキルを高めていくことが非常に重要になっていると言えます。

ところが現場の実情を見ると、特に若手デベロッパーに、基礎スキルの不足の傾向がみられます。これは、ウィザードやツールを多用した開発の練習ばかりしているためで、内部の動きや作業の意味を理解しないまま、技術の勉強を進めているためでしょう。ひどいケースになると、変数定義やインスタンス生成などの基本的な意味がわかっていないことすらありましたが;、こうしたことを避けるためには、単に表面的なツールの使い方やコーディング方法を勉強するのではなく、裏側の動きまで含めた、一歩踏み込んだ学習をしていかなければなりません。そうしたことを怠ると、その場しのぎの作業がどんどん増えていき、最後にはインターネットからコードをコピペしまくる「Copy & Paste」デベロッパーになってしまいます。でも、こうしたデベロッパーは確実に淘汰されます。オフショアなどとの価格競争に巻き込まれないようにするには、デベロッパーとしての生産性や付加価値をどのように守っていくのか、どのようにそれらを向上させていくのかを、真剣に考える必要があります。なぜなら、今でもフレームワークや共通部品群、アプリケーションの中核部分は開発が難しく、スキルの高いデベロッパーにしか作れないからです。基本的には、デベロッパーが自身の市場価値を維持・向上する方法は以下の 2 つのいずれかだと思います。

  • ツールを最大限に生かし、開発生産性を大幅に高めること
  • 他の人には作れないような高度なコードを開発できるようになること

要するに、「早く・安く・上手く作れる」ことが、デベロッパーの目指すゴールとなります。

image

[④ テスター(結合機能テスト・システムテスト)について]

システム開発の世界におけるテスターというのは、以前は肩身の狭い職種だったように思いますが、最近になって、テストはデバッグとは違うということ、すなわちテストとは品質評価であることが、ようやく認知され始めてきました。米国では、ここ 5~10 年ほどで現場での研究ノウハウがようやく結実し始め、書籍などの形で入手しやすくなってきています。実際、マイクロソフトも Visual Studio 2010 でようやく本格的なテストツール Microsoft Test & Lab Manager (略称 MTLM)をリリースし、科学的なアプローチに基づく「品質評価」を実践しやすい環境が、ツール面でも整ってきました。

しかし残念なことに、日本の開発現場の多くは旧態依然としたテストが実践されており、KKD (勘と経験と度胸)でテストが行われてしまっていることが多いと思います。システム開発を生業としている SIer ですら、製造業のような高度な品質管理はほとんど実践されておらず、テストの専門性が認知されていないことすらあるのではないでしょうか? 先進的な企業ではこうした部分にメスが入りつつありますが、米国に比べるとまだまだ遅れは大きい状況ではないかと思います。実際、日本ではテスターの人数や品質評価作業の工数が削られがちで、例えばテスト工数の削減となると、すぐさま「ツールによる自動化」に飛びついて解決を図ろうとする安直な考え方が目立ちます。テスト工数を削減する際には、まず テスト実施計画の最適化やテストケース設計の見直しを行うべきなのですが、このような考え方では、せっかくツールがあってもそれを使いこなしたり生かしたりすることは難しいでしょう。

こうしたことから、今のシステム開発の世界においてテスターの専門家を目指そうとするのは、日本の場合、かなり茨の道になる可能性もあります。しかしその一方で、今後さらに重要性を帯びてくる領域でもあるため、この分野のスキルを伸ばしておくことは非常に重要になってくると思います(実際、この領域に関する問い合わせはここ最近非常に増えてきています)。この分野のスキルを伸ばそうとする場合には、まず正しいテストの考え方を理解すること、そしてテストケース設計やテスト実施に関するノウハウを身に着けていくことが大切です。テストツールを使った自動化などは最後に学ぶべきことで、確たる知識ベースラインに基づいて学習しないと、ツールに振り回されることになるため注意が必要です。最終的には、テスト実施計画、テストケース設計、テスト結果分析を通して、システムの品質を可視化・定量化できるようになることがテスターとしてのゴールです。

image

[⑤ プロジェクトマネージャー(プロジェクトマネジメント)について]

プロジェクトマネジメントを取り巻く環境については、ここ 10 年で大きく変化しました。特に大きいのは、PMBOK のようなプロジェクトマネジメントに対する知識体系の整理や、Team Foundation Server (TFS)に代表されるような、高度なプロジェクト管理システムが安価・容易に入手できるようになったことだと思います。後者は非常に重要で、従来、「プロジェクト管理のため」に行われていた手作業の報告作業や集計作業が、一部とはいえ自動化できるようになったところは非常に大きいと思います。Agile 型(XP, RUP, Scrum など)のような開発プロセスであっても数値ベースでプロジェクトを管理できるようになったのは、こうした開発管理システムがあったからこそ、ではないかと思います。

しかしその一方で、プロジェクト管理においてプロジェクトを数値ベースで管理できるようにすること、すなわちプロジェクトの「見える化」をするためには、それ相応の「仕掛け」が必要になります。つまり、プロジェクトメンバに、「データ収集を意識させない」仕組みを作り、日常作業を普通にこなしているだけでデータが収集されるような仕組みを組み立てておく必要があります。そのために、プロジェクトマネージャは以下のような仕組みを組み立て上げておかなければなりません。

image

プロジェクトマネジメントの世界では、コミュニケーションスキルに代表されるようなスキルが重要視されます。こうした部分のスキルロードマップの考え方については、PMBOK やそれに関連する部分で十分に体系化されていると思いますのでここでは述べませんが、実務レベルで見た場合、上記のような「プロジェクトマネジメントのために必要なプロジェクトの計測手法」を考え出し、「プロジェクト状況の計測システム」を作り込むことも重要です。実際のシステム構築は、アーキテクトやデベロッパーの手を借りながら進めることになりますが、こうした作業が必要であることも、ぜひ意識していただければと思います。

[T 字型スキルの必要性と重要性]

なお、ここまで話を簡単にするために、5 つの開発系エンジニアのスキルを完全に個別にものとして紹介してきましたが、実際にはこの 5 つのスキルエリアは排他なものではなく、どちらかというと、どこかに高い専門性を持ちつつも、他のスキルエリアについてもある程度は広く知っているようにするべきものだと思います(いわゆる「T 字型スキル」と呼ばれるもの)。私自身は、②のアーキテクトとしてのスキルをメインとしながら、③ デベロッパースキルや④ テスタースキルもある程度掛け持ちしている、という感じですが、例えば SIer の人であれば、①④⑤あたりの掛け持ちが必要になってくるのではないかと思います。どんなスキルがどの程度求められるのかは、プロジェクト規模や企業規模などによって随分状況が異なると思いますが、こうした考え方のフレームワークを持っていると、何かと考えやすいのではないかと思います。

[キャリアパスとしての技術系コンサルタント]

さて、ここまで開発系エンジニアのタイプとそれぞれのスキルロードマップについて解説してきましたが、それとは別にひとつ、悩ましい課題について考えておく必要があります。それは、企業の中でのキャリアパスの問題です。

Part 1. のエントリの中でも触れたことですが、日本企業の場合、開発系エンジニアには、ひとつのロードマップ(=ひとつのキャリアパス)しか与えられていないことがしばしばあります。専門職としてのキャリアはある程度のところで頭打ちとなり、それ以上に昇級していきたければ、管理職(マネジメント)になるしかない……といったような企業は、残念ながら日本の場合は少なくないと思います。これに対して外資系の企業、例えばマイクロソフトなどでは、(製品を作る会社だからという理由も大きいのでしょうが)エンジニアとして高いレベルまで昇級していくことが可能になっており、例えば私自身、自分のマネージャよりも自分の方が(年齢も若いのに)職位が高いという逆転現象を起こしたこともありました。要するに、専門職には専門職のキャリアパスが、管理職(ピープルマネージャ)には管理職のキャリアパスが用意されているということであり、管理職の方が専門職よりも必ず偉いという仕組みにはなっていません。無論、そうした上級専門職の人には、給料に見合った高いアウトプットを出すことが求められるので非常に大変ではあるのですが;、少なくとも制度として、専門職の職位に関して上限(キャップ)が低く設定されているようなことはないわけです。日本企業でも、ハイスキルな人材の流出を防ぎ、社内にキープできるようにするよう、専門職でありながら管理職と同等、あるいはそれ以上の職位を設けるといった動きが出てきてはいるものの、こうした動きはまだまだ全体としては少ない状況だと思います。日本だと、現実にはもっと基本的なこととして、年功序列制度が色濃く残っているようなケースの方が多いのではないでしょうか?

年功序列制度や、専門職・管理職の上下関係などについては、当然、良い面・悪い面があり、是非や功罪を一側面的に論じられるものではないのでここでは立ち入りませんが、ある個人として会社を見た場合、人によってはこうした仕組みが足かせのように感じられるケースはあると思います。開発系エンジニアとしてのスキルを思いっきり伸ばしたいとか、あるいはまだまだ専門職で自分の力を伸ばしていきたいとか、そういった人たちにとって、こうした仕組みはもしかしたら邪魔に感じられるかもしれません。このような場合には、状況によっては、社外へ転職する(技術系コンサルタントへの転職)というのも、キャリアパスのひとつの選択肢として考えるべきだと私は思います。社外に出ることによって得られる経験や知見は、それはそれで高い価値があるからです。

その一方で、安易な考え方(例えば今の会社がイヤだとか上司とそりが合わないとか;)で社外への転職を考えることは、あまりお奨めできません。私自身、マイクロソフトのコンサルタントになるにはどうすればいいんですか? といった相談を受けることがあります。これは別にマイクロソフトの技術コンサルタントに限った話ではないのだと思いますが、外資系企業の技術系コンサルタントを目指そうとする場合には、① 技術力がちゃんとあって、② その技術力を現場で駆使して、③ それを他の人に伝えていける、という高いベースラインスキルを持っているだけではなく、さらに厳しい環境に身を置く覚悟が必要とされるからです。後ろ向きな気持ちではなく、前向きな気持ちで「もっと上を目指したい!」と意欲的に考えられるときに、ひとつの候補として挙げられるものではないかと思います。

実は私が所属しているコンサルティングサービス統括本部では、現在、開発系コンサルタントの募集を行っていて、採用枠もあるのですが、こういうものを見ても、なかなか怖くて食指が動かなかったり、あるいは逆に安易な気持ちや後ろ向きな気持ちで応募したりすることが現実的には多いのではないかと思います。しかしどちらのケースも、応募する側・される側両方にとって不幸なことだと思います。いきなり応募!の前に、もうちょっとカジュアルに、自分のキャリアのことを考えたり、仕事の実情を知ることはとても大切なことです。今回、そんな話を人事の担当者と話したら、だったら小さめのカジュアルなセミナーを開いてみては? という話をもらいました。マイクロソフトのコンサルタントという職種に興味はあるんだけど、自分が応募すべきかどうかが分からない、そもそもコンサルタントってどんな仕事をしているんだろう? といった興味を持っている方向けにセミナーを開いてみたいと思いますので、よかったらこちらのページから応募してみてください。(万が一好評だったらリピート開催してみようかと思います。すでにセミナーが終わっていたら、私にメールで個別にコンタクトしてみてください^^。)

# ちなみに同様の話として、女性のエンジニアとしてのキャリアについて悩んでいる方もいるかと思います。
# そういった方には、女性向けのセミナーもあるそうなので、よかったら参加してみてください。何かヒントが得られるかもしれません。

実際に社外に応募する・しないにかかわらず、そうした職種の選択肢の存在を知っておくことも大切ではないかと思います。エンジニアとしてのキャリアに迷われている方や、技術系コンサルタントという職種に興味のある方は、参加してみていただければと思います。

[まとめ]

というわけでいろいろと説明してきましたが、要点をまとめると以下のようになります。

  • 昨今の開発技術の深化により、ブルーカラー的な単純労働は非常に少なくなってきている。
    • スキルの高い人でないと、Visual Studio などの高い開発生産性ツールの良さを十分に引き出すことができない。
    • このため、デベロッパーは自分たちのスキルを維持・向上させることが極めて重要。
  • 直接、内部設計や実装などを行わない SIer においても、やはりテクニカルスキルは重要である。
    • プロジェクト管理やチームリードなどの「単価の高い」業務へのリソース集中は重要だが、やりすぎるとコアコンピテンスを失う。
    • コアコンピテンスを維持するためにも、ベースラインスキルを維持すること(日々の基礎トレ)は欠かしてはならない。
  • スキル強化を考える場合には、座学(トレーニング)と OJT のバランスを意識する。
    • 基礎学力を応用問題(OJT)だけで身に着けるのは効率が悪すぎる。
    • 基礎学力なしで応用問題(OJT)を学ぼうとすると、場当たり的・その場しのぎのパッチ的な解決策に走りやすくなる。
  • 開発系エンジニアのスキルを考える場合には、5 つの専門分野に分けて捉えると分かりやすい。
    • 業務 SE(要件定義や業務設計を担当)
    • アーキテクト(方式設計などの設計・実装標準化を担当)
    • デベロッパー(内部設計や実装を担当)
    • テスター(テストと品質評価を担当)
    • プロジェクトマネージャ(プロジェクト管理を担当)
  • スキルアップを考える場合には、自分がどの専門分野を特に得意とするのか、どこを特に伸ばしたいのかを意識するとよい。

会社にとって、社員の市場価値を守ることは会社の価値を守るために必要ですが、だからといって個々人が自分の市場価値を守ることを会社にまかせっきりにしてはいけない、と私は思います。自分の市場価値を維持し向上していくことは、基本的には自分の責任、ではないでしょうか? この話は正解や結論のない話ではありますが、本エントリが、ご自身のスキルや市場価値、キャリアなどに悩まれている方の、何らかのヒントになることを願っています。

Community Open Day 2012 に登壇します!

$
0
0

めっちゃ忙しくてまるっきり blog の更新も滞っている今日このごろ;。最近はすっかり Manager 業務ばっかりなのでなかなか外部で講演することも少なくなってしまったのですが、久しぶりに外部向けの講演を行うことになりましたのでちょっとだけ宣伝させてください^^。

こちらのイベントは、6/9(土) に全国一斉(!)で行われるコミュニティイベントで、北は北海道から南は沖縄まで、Developer & IT Pro 向けの勉強会を同日開催しよう! というものになっていたりします。全国 10 会場同時開催な上に、各地方ごとにまったく違うトラック構成になっているというびっくりイベントなのですが、私は北海道札幌に乗り込んで、CLR/H さん主催の Community Open Day でスピーカーをさせていただくことになりました。

[セッションタイトル]
「今どきエンジニアの生き残り戦略」(セッション#5、17:00~18:00)

[セッション概要]
昨今、エンジニアを取り巻く環境は厳しさを増すばかり。日々どうしたものかと悩まれているエンジニアの方は多いと思います。仕事における「質」や「量」、スキルレベルなどをどう考えるのか? エンジニアが生き残っていくための、基本的な心構えや考え方についてお話しさせていただこうと思います。

[セッションアジェンダ]
・ 今どきエンジニアの苦境
・ 仕事の「質」と「量」
・ 仕事の「質」を高めるための 4 つの心がけ
・ エンジニアのスキルの考え方
・ どこを目指すのか? - キャリアの考え方

先日、この blog に「開発系エンジニアのスキルロードマップ」というエントリをアップしたところ、これが恐ろしく好評でびっくりしたのですが;、もしかしたらこういう話もニーズがあるのかな?と思い、今回は敢えて、あんまり技術っぽくない話に挑戦してみようかと思います。コードが一行も出てこないプレゼン(!)を予定していますので、もしご興味のある方はぜひご参加ください。

# 開催場所は北海道ですが、UStream での中継もあるみたいです。
# 札幌にお住いの方にはぜひ直接参加していただきつつ、それ以外にお住まいの方も、よかったらご覧いただければ幸いです。

Windows Azure Web サイトサービス

$
0
0

さてさて、あいかわらず blog をほったらかしにしてかなりの時間が開いてしまったのですが;、最近はというと、Azure いじったり TFS いじったりと、技術系のことをいろいろと調べていたりします。昨年一年間、開発系コンサルタントのマネージャをやっていたのですが、今年の 7 月に再び現場に復帰して、最新の技術をいろいろと研究中。テーマ的には Windows Azure、TFS(ALM)、Windows 8 あたりが大きかったりします。やっぱり技術は楽しい! と思いながらいろいろと調べているのですが、その中でもひときわシビれたテクノロジーが、今回エントリで取り上げようとしている “Windows Azure Web サイトサービス” だったりします。

ご存じの通り、Windows Azure は今年の 6 月に “Spring Update” と呼ばれる大規模 Update が行われ、IaaS 仮想マシンサービスを始めとする大型機能拡張が実施されました。この機能拡張の中では、とにかく IaaS 仮想マシンサービスと仮想ネットワーク機能がお客様の目を引いていて、確かにこれらの機能も非常によくできてはいるのですが、エンジニア目線で一番シビれたのはどちらかというと、こちらの Web サイトサービスの方でした。

この Web サイトサービスは、ほとんど Windows Azure の知識なしで、クラウド環境に ASP.NET のアプリケーションをアップロードして展開できるレンタルサーバサービスで、小規模な Web アプリケーションであればほとんどのものがこれでカバーできてしまうであろう、トンデモサービスだったりします。しかもマシン共有型サービスなので、他のレンタルサーバサービスに比べて圧倒的に安い、という特徴もあります。が、このサービスにあまり着目している人がいないせいか、まとまった情報があまりない様子。なので、今回軽くエントリで取り上げてみようと思った次第です。

目安としては、だいたい Web サーバ数台程度で動かせるような Web アプリを作っているような方々であれば、この Azure Web サイトサービスは一考の余地があります。何ができて何ができないのか、そうした制約事項などについても、本エントリでざくっとまとめてみたいと思います。

Part 1. Web サイトサービス概要
Part 2. Web サイトサービスの内部動作アーキテクチャ
Part 3. Web サイトサービスアプリケーション開発上の注意点

では、まったりエントリを書いていきたいと思います。^^

Part 1. Web サイトサービス概要

$
0
0

まず Part 1. のエントリでは、軽量ホスティングサービスである Web サイトサービスの概要について解説したいと思います。

  • Web サイトサービスとは何か
  • 基本的な使い方
  • Web サイトサービスの特徴
  • 実際の利用モデル

[Web サイトサービスとは何か]

簡単に言うと、Web サイトサービスは、ASP.NET や PHP で作成された Web アプリケーションを簡単にアップロードして実行することができる、レンタルサーバサービスです。おおざっぱな動きを下図に示します(※ 図をわかりやすくするため、Web サーバがあたかも物理マシンであるように描いていますが、実際には仮想マシンが利用されています)。

image

Web サイトサービスでは、予め、Web サーバがプールされて用意されています。ここに、ユーザがアプリケーションをアップロードすると、自動的に空いている Web サーバに配置され、これが実行される、という形になります。アップロードしたアプリケーションには、http://○○○.azurewebsites.net/ というアドレスが付与され、これで Web アプリケーションにアクセスすることができるようになります。

この Web サイトサービスは、よくある Linux のレンタルサーバサービスと同様に、1 つの仮想マシンを複数のユーザ(アプリケーション)で共有して使います(もちろん、各アプリケーションは論理的に隔離されているため、互いにお互いを見ることはできません)。このため、使い勝手としては Linux のレンタルサーバサービスに近い手軽さがあるのですが、非常に面白いのは、そうでありながら、複数のサーバを使ってスケールアウトすることができる、という特徴があります。

[基本的な使い方]

Web サイトサービスの基本的な使い方は、以下の通りです。(ざっと書きますが、簡単なのでやってみればすぐにわかるはず。)

① Windows Azure 管理サイトにて、空の Web サイトを作成します。(https://manage.windowsazure.com/

image

② Windows Azure 管理サイトから、発行プロファイルを取得します。(“Download publish profile” をクリックして入手)

image

③ Visual Studio 上で Web アプリケーションを開発します。( .NET Framework 2.0~4.0 を使って Web アプリケーションを開発。現状では 4.5 は非サポートですが、いずれサポートされるでしょう。)

④ ②を Visual Studio に読み込ませ、アプリを Azure に発行します。( VS 2012 であればそのまま読み込ませることが可能、 VS 2010 であればファイル内の情報を手作業で転記する必要があります。詳細は後述。)

image

⑤ ブラウザから Azure 上に展開されたアプリを呼び出します。(http://〇〇〇.azurewebsites.net/ にアクセスし、アプリを呼び出す)

image

たったこれだけの作業で、Azure 上(=インターネット上)にアプリケーションをアップロードできてしまいます。実際にやってみるとわかるのですが、Visual Studio から Azure Web サイトへのアプリケーションのアップロードはせいぜい数十秒程度で完了するので、非常に手軽にアプリケーションを展開できてしまいます。Windows Azure の他のレンタルサーバサービスである、クラウドサービス(コンピュートサービス)や IaaS 仮想マシンサービスでは、1 台のサーバをセットアップするのに軽く 10 分ぐらいはかかるのですが、これと比べると予備知識も不要で、非常に手軽に Azure が利用できることがわかります。(というかマジで一度使ってみてください。こんな単純でいいのか?!と言いたくなります;。)

[Web サイトサービスの特徴]

この Web サイトサービスの特徴をまとめると、以下のようになります。

  • Web アプリケーションの配置が簡単かつ高速
    Web Deploy と呼ばれる仕組みにより、Visual Studio から数クリックで容易にアップロードができます。
    さらに FTP, TFS, Git などによるアプリケーションの配置が可能。
  • 容易なスケール調整が可能
    利用する Web サーバ台数を、1~3 台まで調整することができます。
    また、 他ユーザとの共用型配置がイヤなら、サーバ占有型での配置もできます(ただしその分コストはかかります)。
  • オープンソース系のランタイムやツールも幅広くサポート
    PHP や Node.js での開発もサポートしており、 無償 Web 開発環境 WebMatrix での開発もサポートしています。
  • Web ギャラリー機能
    Web サイトに展開するアプリケーションが多数用意されてます。 これにより、Blog サイトを簡単に立ち上げたりすることも可能です。

もちろん、デメリットがないわけではありません。たとえば、Windows Azure のレンタルサーバサービスである Web サイトサービスと、クラウドサービス(コンピュートサービス)を比較してみると、次のような違いがあります。

image

クラウドサービス(コンピュートサービス)については、以前こちらのエントリに詳しく書いたのでそちらをご覧いただければよいかと思いますが、例えばクラウドサービスでは可能だった、追加のソフトウェアのインストール、ステージング環境を利用した VIP スワップ、バッチアプリケーションを動作させるためのワーカーロールサーバなどの利用はできません。このため、エンタープライズアプリケーションであればやはりクラウドサービスを利用する、ということになるのでしょうが、実際のクラウドの利用を見ると、

  • 短期間だけちょろっと動作させなければならない簡単なアプリを、素早く作って素早くアップロードして素早く立ち上げる。

というニーズは非常に根強くあります。このようなケースだと、アプリケーションは比較的簡単なものであることが多く、かつ安価に素早くやりたいはず。このような場合にはまさにうってつけのサービスである、と言えます。

[実際の利用モデル]

実際に Web サイトサービスを利用する場合には、多くのケースで、SQL データベースサービス(SQL Azure)と組み合わせる形になるでしょう。これを使うと、Web サイトサービスが背後で利用する SQL Server データベースを簡単に用意することができます。(SQL データベースサービス(SQL Azure)に関しても、以前まとめたこちらのエントリがあるので、そちらをご参照ください。)

image

この Web サイトサービスと SQL データベースを組み合わせれば、かなり広範なアプリケーションをインターネット上に立ち上げることが可能です。下図は代表的な展開パターンですが、Web アプリ、RIA アプリ、スマートクライアントなどはもちろん可能ですし、Windows 8 やスマートフォンなどをクライアントとした際の、サーバ側サービスをこの Web サイトサービスで開発することも当然可能です。

 

image

(参考&注意) 実際に上図のような Web サイトサービス+SQL データベースサービスの組み合わせを利用する場合には、これらのサービスは必ず同一のデータセンタを利用してください。別々のデータセンタに配置してしまうと、Web サイトと SQL データベースの間の通信に対して課金が発生する上に、パフォーマンスも悪くなります。

というわけで、Web サイトサービスは非常に手軽に利用できる、インターネット上のレンタルサーバサービスであることを解説したわけですが、これだけですと利用するのには少し不安、という方も多いと思います。次のエントリでは、Web サイトの内部動作アーキテクチャをもう少し掘り下げてみたいと思います。

Part 2. Web サイトサービスの内部動作アーキテクチャ

$
0
0

Part 1. のエントリでは、Web サイトサービスがどのようなサービスであるかについて解説しました。引き続き本エントリでは、この Web サイトサービスの内部動作アーキテクチャについて解説したいと思います。

  • 内部動作アーキテクチャ
  • 2 種類の配置モデルと 3 種類の課金モデル

[内部動作アーキテクチャ]

Web サイトサービスを理解する上で最も重要なのは、どのような形でアプリケーションが物理インフラ上に配置されるのか、という点です。要点としては以下の 3 つがあります。

  • 複数のユーザが、複数のアプリケーションを配置できるが、各アプリケーションは、同一仮想マシン内で論理的に隔離される。
  • インターネットからの HTTP リクエストは、セッションアフィニティつきで自動的に負荷分散される。
  • アプリケーションが複数マシンに展開されていても、実際のコンテンツは物理的に共有されている。

これらについて順に解説していきます。

① 複数のユーザが、複数のアプリケーションを配置できるが、各アプリケーションは、同一仮想マシン内で論理的に隔離される。

Windows Azure では、Web サイトサービス用にサーバクラスタを保有しており、ここに、複数のユーザが、複数のアプリケーションをアップロードすることができます(※ 共有型の場合、占有型については後述)。各アプリケーションは URL で識別され、フロントンエンドにあるロードバランサが、実際にアプリケーションの乗っている Web サーバにリクエストを転送する形で処理を行います。

image

この際、Web サイトサービスでは、アプリケーション隔離のための仮想マシン分離は行われていません。下図は、Windows Azure のレンタルサーバサービスである、Web サイトサービスと、クラウドサービス(コンピュートサービス)とを比較したイラストです。後者のクラウドサービスでは、各ユーザのアプリケーションは別々の仮想マシンを利用することになるため、分離性は高くなりますが、その一方で、アプリケーションの集積度を高められないため、コストがどうしても割高になります(1CPU あたりざっくり \8,000/月 ぐらい)。しかし、Web サイトサービスは、ひとつの仮想マシンを複数ユーザが共用するため、より安価なサービス提供が可能になります(最も安価なものはフリー、その次がざっくり \1,500/月ぐらい)。

image

(参考) なお、Web サイトサービスでは仮想マシン分離は行われていませんが、だからといって、自分のアプリが隣のアプリから丸見えになってしまう、なんていうことはありません。仮想マシン分離は行われていませんが、かわりに Drawbridge と呼ばれる技術によりアプリケーション分離が行われています。この Drawbridge という技術は、Microsoft Research で開発されているアプリケーションのサンドボックス化による仮想化技術で、仮想マシンよりも小さいプロセスの単位での隔離機能を提供します。アプリケーションが Full Trust 権限を持ちながらも、他のアプリと隔離された状態で動作しているのはこの仕組みによるもので、これにより、より高い集積度でサービスを動作させることができ、結果的により安価なサービス提供ができるようになっています。

② インターネットからの HTTP リクエストは、セッションアフィニティつきで自動的に負荷分散される。

Web サイトサービスの特徴のひとつに、自分が開発したアプリケーションを最大 3 台の Web サーバまでスケールアウトできる、という点があります。上図で示したように、クライアントからのリクエストは自動的に複数の Web サーバに負荷分散してくれるようになっているのですが、Web サイトのこの負荷分散の仕組みにはもう一つ特徴があります。それは、セッションアフィニティ(同一ユーザからのリクエストを常に同一のサーバにルーティングし続ける機能)つきでの負荷分散を行ってくれる、という点です。

セッションアフィニティつきで負荷分散が行えるのは、Web サイトでは、Azure の負荷分散の仕組みをそのまま使っているのではなく、中間にもう一層、リバースプロキシ層を設けているためです。下図が内部アーキテクチャです。

image

ご存じの方も多いかと思いますが、Windows Azure のクラウドサービス(コンピュートサービス)で利用している Azure のロードバランサは、セッションアフィニティを持ちません。しかし、Azure の外部向けロードバランサと、Web サイトサービスの間に ARR リバースプロキシ層が設けられています。この ARR 層は普通に Web サイトサービスを使っている限り意識することはありませんが(ユーザが触ることはできません)、この層があるおかげで、セッションアフィニティが実現されています。

(参考) ARR (Application Request Routing )とは、IIS の機能拡張モジュールのひとつです。様々な機能を持っていますが、代表的な利用方法のひとつとして、こちらで解説されているようなリバースプロキシの構築、というものがあります。私も以前、震災対応の際にリバースプロキシ構築作業を行ったことがあり、その際にこちらの ARR を利用しました。

なお、ARR によるセッションアフィニティは、ARR によるクッキー発行により実現されています。Web サイトにアクセスしている際の HTTP リクエストをツールで解析してみるとすぐにわかりますが、ARRAffinity と呼ばれるクッキーが発行されており、これにより、ルーティング先となるサーバが固定化されるようになっています。

image

③ アプリケーションが複数マシンに展開されていても、実際のコンテンツは物理的に共有されている。

さて、Web サイトサービスでは複数のマシンによるスケールアウトが可能であることをここまで説明してきたわけですが、スケールアウトの際に問題になるのが、実際の物理的なファイルの配置方法です。例えば、3 台のサーバでスケールアウトする場合、通常、Web アプリケーションは各サーバにコピー配置する必要があります。しかし、Web サイトサービスでは、実際の物理的なコンテンツファイルは、下図のように同一のストレージにより共有されています。

image

これにより、Web サイトでは以下のような動作が実現できるようになっています。

  • コンテンツはあたかもローカルディスク上にあるかのように見えるが、どのマシンからローカルディスクに書き込んでも、他のマシンから同じ内容が読み出せる。
  • FTP で Web サイトに接続すると、あたかもサーバが 1 台だけしかないように見える。(が、実際には複数のサーバで負荷分散処理される)

このように、Web サイトサービスには、プロセス隔離やセッションアフィニティつき負荷分散、ストレージ共通化などの機能が備わっています。この結果として、我々は非常に簡単に、Web アプリケーションをアップロードし、負荷分散しながら処理することができるようになっている、というわけです。

とはいえ、やはり他のユーザと同一サーバ上にアプリを共存させたくない、というケースもあるでしょう。そのようなケースに応えるために、別の配置モデルも用意されています。それが、次に述べる占有型配置モデルです。

[2 種類の配置モデルと 3 種類の課金モデル]

Web サイトサービスでは、2 種類の配置モデル(インスタンスモデル)を利用することができます。ひとつが共有型、もうひとつが占有型です。下図に比較を示します。

image

共有型モデルでは、事前にマイクロソフト側が用意しているサーバ群を、複数のユーザが共用する形で利用します。このため、サービスの利用料金は相対的に安価で、(利用量に関する上限設定は比較的厳しいですが)フリーで利用可能なサービスも提供されています。これに対して、占有型モデルでは、あるユーザ(サブスクリプション)専用のサーバ群を用意することができ、そこに自分のアプリケーションを展開することが可能です。この場合には、占有するサーバ分の料金を支払う必要がありますが、かわりに他のユーザのアプリケーションとサーバを共有することがなくなります。

2 つの配置モデルの違いをまとめると、以下のようになります。

image

共有型(Shared)に関しては、あるユーザのアプリケーションがサーバリソースを使い切らないようにするため、リソース利用量に関して上限設定(クォータ設定)がなされています。共有型はさらに Free, Paid の 2 種類に分類されており、それぞれでクォータ設定が異なります。2012/10/19 時点では、クォータ設定および利用料金に関しては、以下のようになっています。見ればわかるように、フリー共有型(Free Shared インスタンスモデル)は比較的厳しいクォータ制限がかかっています。(が、ちょっと遊んでみる程度の用途であれば十二分すぎるほどの量ですね。) (※ フリー共有型を利用する場合、Web サイトはフリーになりますが、SQL データベースの方については課金が行われることには注意してください。100MB までであれば約 \500 /月、~1GB であれば約 \900 /月です。)

image

上記のクォータを超えたアプリケーションに対しては、一時的にアプリが利用停止状態になります。現時点でどの程度のリソースを利用しているのかに関しては、Windows Azure のポータルサイト(管理画面)から簡単に確認することができるようになっています。

image

また、これらの配置モデルについては、アプリケーションを配置した後でも切り替えることができます。このため、Free Shared でまずはサイトを立ち上げたのち、キャパシティが不足するようであれば Paid Shared、さらに必要に応じて Reserved に変えていくとよいでしょう。

image

なお、一点注意すべきこととして、同一サブスクリプション内では同一の配置モデルを使う必要があります。同一サブスクリプション内において、アプリ A は Free Shared、アプリ B は Paid Shared、アプリ C は Reserved、といった使い分けはできませんので、ご注意ください。

というわけで、以上が Web サイトサービスの内部動作なわけですが、引き続き、この Web サイトサービスの上で動作するアプリケーションを開発する際の注意点について、少し解説していきたいと思います。

Part 3. Web サイトサービスアプリケーション開発上の注意点

$
0
0

さて、ここまで Web サイトサービスの概要やその内部動作アーキテクチャについて解説してきたわけですが、引き続き今度は、このサービス上に乗せるアプリケーションを作成する場合の注意点についていくつか解説したいと思います。

  • ロードバランサとセッションプロバイダ
  • ワーカープロセスの特徴
  • 利用できない代表的な機能
  • 国際化対応
  • アプリケーションの配置方法
  • アプリケーションの運用監視方法

[ロードバランサとセッションプロバイダ]

先に述べたように、Web サイトサービスでは、ARR リバースプロキシ層のおかげで、セッションアフィニティ機能つきの負荷分散が行われます。結果として、セッション情報をメモリ上に保有しても、通常は問題なく動作するのですが、Web サーバが何らかの理由でダウンすると、セッション情報が失われてしまいます。このため、Web サーバをクラスタリングする場合には、仮にセッションアフィニティがあったとしても、下図のようにセッション情報をバックエンドストレージに保存することが望ましいです。

image

具体的には、以下の 3 つの作業を実施します。

  • ① セッション情報保存用の SQL データベースを作成する
  • ② ASP.NET Universal Provider を入手する
  • ③ web.config に構成設定を行う

① セッション情報保存用の SQL データベース(SQL Azure)を作成する

まずは管理ポータルサイトから、新規に SQL データベースを作成します。(Web サイトサービスからリンクリソースとして結びつけておくと管理がラクになりますが、必須ではないので、単独でふつーに SQL データベースを作成していただいて構いません。) 作成する場合には Web サイトサービスで利用しているのと同一のデータセンタ(Region)を指定してください。作成後、Show Connection Strings を押下し、ADO.NET 用の接続文字列を入手しておきます。

image

② ASP.NET Universal Provider を入手する

.NET Framework 2.0~4.0 に標準で含まれるセッションプロバイダでは、SQL データベースを利用することができません。このため、NuGet パッケージとして配布されている ASP.NET Universal Provider を入手し、これを利用します。具体的な入手手順は以下の通りです。

  • Web Platform Installer をインストールする。(VS2012 では不要)
    http://www.microsoft.com/web/downloads/platform.aspx
  • NuGet パッケージマネージャをインストールする。(VS2012 では不要)
    http://nuget.codeplex.com/
  • Visual Studio を立ち上げ、NuGet コンソールを起動。
    ツール → ライブラリパッケージマネージャ → ソリューションの NuGet バッケージの管理 を選択
  • NuGet コンソールから ASP.NET Universal Provider をインストールする。
    ソリューションを開いた状態で、コンソールから以下のコマンドを実行
    Install-Package System.Web.Providers

以上で、ASP.NET Universal Provider が、Web アプリケーションに組み込まれます。

image

③ web.config に構成設定を行う

最後に、web.config ファイルを修正します。パッケージマネージャで ASP.NET Universal Provider をインストールすると、web.config ファイルにいくつかの設定が追加されていますので、これを修正すれば OK です。具体的には、下図の赤字の部分を修正します。

image

なお実際には、開発中はファイルアタッチデータベースを利用し、Azure 環境へのアップロード直前に構成設定ファイルを書き換える形になると思いますが、いちいち開発環境と Azure 環境の設定を書き換えるのは面倒です。このような場合には、下図のように web.Debug.config や web.Release.config ファイルを利用し、Azure 環境へ発行するアプリケーションのビルド条件(Debug ビルド or Release ビルド)に併せて web.config を置換するとよいと思います。

image

image

なお、事前に セッション情報を格納するためのテーブルを SQL データベース上に作っておく必要はありません。以上の作業を行ってアプリケーションを実行すると、下図のように自動的にセッション格納用のテーブル(Sessions テーブル)が作成され、これが利用されます。

 

image

セッションプロバイダに関する注意点は以上です。引き続き、Web サイトのワーカープロセスに関する注意点について解説します。

[ワーカープロセスの特徴]

Web サイトサービスのワーカープロセスには、以下のような特徴があります。

  • 統合パイプラインモードの w3wp.exe として動作
  • 64 ビットプロセスではなく、32 ビットプロセスとして動作
  • ユーザごとに異なるローカルアカウントで動作
  • .NET Framework は 2.0, 4.0 どちらも利用可能(どちらか片方のみ、管理ページから変更可能)
  • カルチャは en-us、タイムゾーンは UTC で動作

このうちちょっと注意すべきなのが、ワーカープロセスの動作ビット数です。通常だとワーカープロセスの既定動作は 64 ビットですが、Web サイトでは 32 ビットプロセスが利用され、これについては変更できません。ASP.NET で Web アプリケーションを作成しているのであれば問題はないと思いますが、念のためご注意ください。

なお、Web サイトの実行環境の詳細を知りたければ、*.aspx 上から各種のデータを拾ってみるとよいと思います。下記に、Web アプリケーション内から拾った、マシン名情報や動作アカウント情報を記載します。(常にこの通りになるとは限りませんが、環境を理解する手助けにはなると思います。)

image

[利用できない代表的な機能]

前述のように、ワーカープロセスは特殊な制限アカウントで動作していること、また Web サイト環境が共用環境であることから、必然的に様々な制約が発生してきます。特にまず真っ先に気を付けるべきポイントを整理すると、以下のようになります。

  • ファイルの自由な読み書きについて
    ほとんどのフォルダについて、自由な読み書きができません。ただし、アプリケーションが展開されているフォルダについては読み書きができます。どうしてもアップロードされたファイルをいったん保存しておきたければ、Server.MapPath(“~”) で取得されるフォルダ下への読み書きができますが、うかつなフォルダに書き込みを行うと、リモートにファイルが公開されてしまいます。基本的には、App_Data フォルダ下に書き込むなどの工夫を行うとよいでしょう。
  • イベントログやパフォーマンスカウンタへの書き込みについて
    共用環境ですので、事実上利用できません。書き込みを行おうとすると処理がブロックされ、エラーが出るようになっています。このため、アプリケーションの例外発生時のログをイベントログに記録しておくことができません。通常、global.asax ファイル内で行っているイベントログへの例外ログ書き出し機能については、下に示すように、App_Data フォルダ下へのファイル書き込みという形に変更していただくとよいでしょう。
  • COM コンポーネントや 3rd party 製ソフトのインストール
    当たり前のことですが、これらもできませんので注意してください。

image

なお、注意すべき点として、これらの制約事項は共有型サーバだけでなく、占有型サーバについてもあてはまります。サーバを占有しているのだからこれらが自由にできる、ということにはならないので注意してください。(これらを行いたい場合には、Web サイトサービスではなく、クラウドサービスや IaaS 仮想マシンサービスを利用します。)

[国際化対応について]

Web サイトサービスに限った話ではありませんが、Windows Azure は World Wide でのサービスですので、基本的に言語中立、UTC 標準時刻での動作になっています。このため、国際化対応を意識していないアプリケーションについては、正しく動作しないことがありますので注意してください。よくある実装ミスとしては、以下のようなものがあげられます。

image

本エントリは国際化対応について解説するためのエントリではないので詳細には立ち入りませんが、特に、時刻の取扱いなどについては DateTimeOffset クラスを使って正しく書くなどの工夫が必要になることに気を付けてください。

image

……というわけで、Web サイトで利用するアプリケーションの作り方について注意点をいくつか挙げてきたわけですが、クラウドサービス(コンピュートサービス)に比べて簡易な機能であるために、覚えるべき & 注意すべきポイントもさほど多くはありません。単純なアプリであれば、簡単にそのまま載せてしまうことができるでしょうから、まずはぜひ、ご自身の Web アプリケーションを Web サイトサービスに載せて動かしてみていただければと思います。

引き続き、このサービスにアプリを配置する方法と、その運用監視方法について簡単に解説します。

[アプリケーションの配置方法]

Web サイトサービスに対して、開発したアプリケーションを配置する(アップロードする)方法は、大別して 4 通りあります。

  • ① Web Deploy
    Visual Studio からアプリケーションの発行機能を利用して展開する(Visual Studio 2010 以降でのみ利用可能)
  • ② クラウド版 TFS (Team Foundation Service)
    自動ビルドシステムの中の発行機能を使って展開する
  • ③ Git
    オープンソースコードなどの開発で利用されている分散バージョン管理システムの Git から直接発行して展開する
  • ④ FTP
    Web サイトサービスに直接つなぎ、ファイルをアップロードする

ASP.NET Web アプリケーションの開発で最も便利なのは、①の方法でしょう。これについて解説します。

Visual Studio から Web サイトサービスにアプリケーションをアップロードするためには、管理ポータルサイトから発行プロファイルと呼ばれる情報を入手する必要があります。具体的には、管理ポータルサイトの ”Download publish profile” というボタンを押下すると、XML 形式で、発行に必要な各種の情報を入手することができます。あとはこれを Visual Studio 2012 に読み込ませるだけです。以上の作業で、Visual Studio の「発行」機能を使うだけで、Azure 環境への直接アップロードができます。

image_thumb[4]

ただし、ダウンロードされる XML ファイルは Visual Studio 2012 用ですので、Visual Studio 2010 の場合には、XML ファイル内の情報を手作業で Visual Studio に設定する必要があります。具体的には、まず発行プロファイル情報をメモ帳で開き、下記のタグを探します。そこに書かれて情報を、Web の発行画面に転記してから発行ボタンを押せば OK です。(ちょっと面倒ですが、一度だけやれば OK です。)

image_thumb[9]

[アプリケーションの運用監視方法]

最後に、Web サイトアプリケーションの運用監視方法について解説します。

クラウドサービス(コンピュートサービス)では、自力で監視エージェントなどを組み込むことにより高度な運用監視が可能でしたが、Web サイトサービスではそのようなことはできません。しかし、簡易なアプリケーションを手軽に運用監視できる機能群が備わっているため、特に事前準備することもなく、すぐさま手軽に運用監視することができます。

具体的な機能としては以下の 2 つがあります。

  • ダッシュボードによるモニタリング
  • 各種ログの取得機能

これらについて解説します。

ダッシュボードによるモニタリング

これは Web サイトの管理ポータルに行ってみればすぐにわかると思いますが、管理ポータルの画面上に、Web サイトサービスの主要なパラメータがすぐに参照できるようなダッシュボードが提供されています。基本的な稼働状況は、こちらを見ていただければ一目瞭然でしょう。

image_thumb[12]

各種ログの取得機能

またこれに加えて、以下の 3 種類のログを FTP により簡単に取り出すことができるようになっています。

  • ① Web サーバのログ(IIS ログ)
  • ② アプリケーションエラーのログ
  • ③ Failed Request Tracing のログ

既定では、IIS ログのみ有効化されています。他のログについても有効化したい場合には、ポータル管理画面の “diagnostics” からログの取得を設定します。

image_thumb[15]

FTP でアクセスすると、以下のようなフォルダ構造になっていますので、これらを取り出して適宜解析していただけるとよいでしょう。

image_thumb[18]

[Web サイトサービスのまとめ]

というわけで、ここまで Web サイトサービスについて解説してきましたが、いかがでしょうか? 要点を改めて整理すると、以下の通りです。

  • 軽量ホスティングサービスである、Web サイトサービスを利用すると、非常に手軽にクラウドを利用することができる。
    クラウドサービスや IaaS VM サービスなどに比べて圧倒的に簡単に Azure を利用することができる
    制約事項も多いため、エンタープライズ向けのリッチなシステムを作りたい場合には、適宜クラウドサービスや IaaS VM サービスを使う
  • 2 種類の配置モデルがある。
    共有型(Shared)、占有型(Prepared)
    共有型の場合には、クォータ制限があることに注意する
  • アプリケーション開発時には、いくつか注意が必要。
    ファイル書き込み処理や国際化対応に注意しながら開発を進める

思ったより簡単に使えそうだ、と思っていただけたのであれば嬉しいところ。実際、使ってみると予想以上に簡単かつお手軽にクラウドを使うことができ、この手軽さこそが PaaS のよいところだよね、と思える出来に仕上がっています。クラウドサービス(コンピュートサービス)だとちょっとヘビーだよねぇ、と思っているような場合には非常に効果的な機能ですので、ぜひ使ってみてください。

IEブラウザの互換性問題の緩和方法

$
0
0

blog の更新をサボって早一年以上。たまにお客様にお会いすると、「blog もう更新しないんですか?」とかよく聞かれるのですが;、最近は Windows 8 タブレットアプリ開発に全力投球していたのでそれどころではありませんでした;。が、ようやく半年ぐらい苦しんでそこそこ軌道に乗ってきたので、ちょっと気分転換も兼ねて blog を書いてみようかと思ったりします。お題は IE ブラウザの互換性問題の緩和方法です。

そもそもこの記事を書こうと思った経緯は、あるお客様でこんな話を相談されたからでした。

「ブラウザを IE から別のブラウザに乗り換えたいと言っているお客さんがいる。理由としては、以前、クライアントの更改時に IE のバージョンアップに伴う再検証作業に恐ろしいコストがかかったため。他社製ブラウザだったらそういうことが起きないと思うので、IE から乗換をしたいと言われている。何か助言をもらえないか?」

ええええ;;、それはあり得ないでしょう;、と思ってしまったのですが、その後いろんなところで話を聞いてみると似たような話が次々と出てくるので、おそらくこれはきちんと現場に情報が伝わっていないのだろうと思った次第。実はマイクロソフトも様々な情報を出してはいるのですが、ぶっちゃけ情報の正確性を意識するあまりどの説明も難しくなりすぎているので、誤解を恐れずにポイントだけをざっくりまとめてみよう、というのがこの記事の趣旨です。

  • 現在のブラウザを取り巻く環境
  • IE のバージョンの変遷
  • IE のドキュメントモード(レンダリングエンジン)
  • レスポンス HTTP ヘッダーによるドキュメントモードの調整方法
  • (参考)リクエスト HTTP ヘッダー/サーバ側の挙動/ブラウザ側での解釈
  • クライアント OS やブラウザのバージョンアップに対する戦略
  • OS やランタイムのバージョンアップをなるべくラクにするために
  • Rapid Release 時代の到来による変化と対応の考え方

それでは早速スタートです^^。

[現在のブラウザを取り巻く環境]

改めて言うまでもないですが、昨今の IT 環境の進化はものすごく速いです。例えば今や IT 業界を席巻しているタブレットの急先鋒である iPad が発売されたのは 2010 年 5 月、つまり今からたったの 4 年前。ブラウザの世界もまた非常に進化が激しく、HTML5 や CSS3 などの進化は目覚ましいものがあります。こうした中、ブラウザ各社はいち早く新機能を市場に提供するために、リリースサイクルをどんどん短くしていくことになりました。Wikipedia の情報を元に、各社のリリースタイミングをざっと表にまとめてみると、以下のようになります(半年ぐらい前にまとめたのでちょっと情報が古いのはご愛嬌;)。

image

時系列順に書き換えてみると、以下のようになります。

release_after7

表を見て、「えっ? Chrome ってそんなに頻繁にバージョンアップしてるの?」と思われた方もいるかもしれませんが、Chrome などはサイレントアップグレード(特にユーザに通知することなく最新版に自動的にアップデートされる)になっており、ユーザの方々が意識することなくアップデートが行われるようになっています。これに対して、IE や Safari はバージョンアップサイクルを比較的長めに取る傾向があり、概ね 1 年に一度(最近はもう少し短くなる傾向があります)のアップデートになっていたりしますが、逆にこれらは大々的に取り上げられる傾向があります。……というより特にマイクロソフトは「新しい IE ってこんなにすごいんだぜ!」的なアピールをしまくるのでそうなっているような気もするわけですが;、とつぶやいてみる;。

こうした「素早いアップデートの提供」のことは “Rapid Release” と呼ばれていますが、こうした Rapid Release は諸刃な側面があります。つまり、最新機能を市場に素早くリリースできる一方で、新機能追加などによるデグレード問題への対処や対応が難しくなる。ブラウザ各社は、下方互換性を取りながらどのようにバグ修正や新機能を織り込んでいくのかに関して、様々な苦労をしながら製品をリリースしています。

[IE のバージョンの変遷]

さて、おそらくこの記事を読まれている方々の多くは、「クライアント OS のバージョンをアップするときに、古い IE 用に作ったWeb アプリ資産をどうすればよいの?」という課題を抱えられている方ではないかと思います。それにお答えする前に、ちょっとだけ、IE のバージョンの変遷についてまとめてみたいと思います。あまり古い話をするとキリがないので、Windows XP 世代以降の話に絞って書きだしてみると、次のようになります。おおまかにいえば、IE のバージョンアップの多くは新 OS リリースに同期して行われてきました。

image

上記ではいろいろと書かれていますが、IE バージョンアップに伴うこれらの変更点の中で特に重要なのが、業界標準への対応強化という点です。特に昔の IE には、CSS 関連の実装に問題があったり、あるいは独自機能が多かったりと、業界標準仕様から外れているところが多々ありました。(簡単に言えば、HTML/CSS の標準仕様に厳格に対応させたブラウザと、IE ブラウザとの間で、同一の HTML/CSS の表示がまるで異なってしまう) Web 界隈ではこうした点が特に非難されたこともあって、マイクロソフトでは IE 8.0 あたりから業界標準への準拠を強めていくことになりました。

半面、このことは、古い IE 向けに作られた Web アプリのブラウザ互換問題を引き起こすことにつながりました。例えば、Windows XP 上の IE 6.0 向けに作られた Web システムを保有している際、クライアント OS を Windows 8 にアップグレードしようとすると、必然的に IE が 10.0 にアップグレードされてしまう。この時に、IE 10.0 で IE 6.0 向けに作られた Web アプリをそのまま表示させると、表示が崩れてしまう。確かに業界標準仕様への準拠を高めるという方針は正しいものの、既存システムを持つ会社にとっては、ブラウザのバージョンアップが頭の痛い問題になる、という課題を抱えているわけです。

[IE のドキュメントモード(レンダリングエンジン)]

こうした問題を緩和するため、Internet Explorer には IE 7.0 世代から、ドキュメントモードと呼ばれる機能が搭載されることになりました。誤解を恐れずに非常に簡単に言うと、新しい IE には、古い IE のレンダリングエンジン(とほぼ同等のもの)も搭載されている、というものになります。例えば IE11 には、IE11 の最新の描画エンジンだけでなく、IE10, 9, 8, 7 の描画エンジンも搭載されています。このため、例えば IE11 を使って IE7 用に作られた Web サイトを開く場合には、ドキュメントモードを IE7 に切り替えてやれば OK。そうすることで、IE7 と(ほぼ)同じ描画を行わせることができます

このドキュメントモードの切り替えは、F12 開発者ツールの中で行うことができます(※ ブラウザのバージョンごとに若干方法が異なります)。ブラウザを開いている際に F12 キーを押していただくと、ブラウザ下部に開発者向けツールが出てきます。この中のエミュレーションタブの中にある「ドキュメントモード」を変更していただくことにより、描画エンジンを IE11~IE7 の範囲で切り替えることができます。(図中で “Edge” と書かれているのは、最先端のモード(このケースだと IE11 モード)を意味します。)

image

この MSDN blog の場合には、IE7~11 の範囲で描画エンジンを切り替えてもほとんど描画が変わらないと思いますが、例えば MSN などのサイトでは、レンダリングエンジンのバージョンを切り替えるとあっさり描画が崩れます。下図の左側が IE11 モードでの描画、右側が IE7 モードでの描画です。IE11 用として Web サーバから送られてきた HTML を、無理矢理 IE7 の描画エンジンでレンダリングしようと思っても、うまくレンダリングできないのは当然ですね^^。

image image

これらのことからわかるように、IE で Web アプリを表示させる場合には、Web アプリ側が利用を想定している IE バージョンと、ブラウザ側で利用するドキュメントモードのバージョンとを揃えることが重要になります。このドキュメントモードに関しては、実は IE の変遷と共にモード数が増えていっています。下表は、各 IE がサポートするドキュメントモード(すなわち各 IE が保有するレンダリングエンジン)の一覧を示したものです。

image

ドキュメントモードについては、まず以下の点に注意してください。

  • IE6 モードについて
    ドキュメントモードが本格的に導入されたのは IE7 以降であるため、IE6 のレンダリングエンジンは現在残っていません。ですが、IE6 と 7 は比較的差が小さいため(業界標準適合のために大きく描画が変わっていったのが IE8 以降であるため)、IE6 向けに作られた Web サイトは、まずとりあえず IE7 モードで描画させてみるとよいでしょう。
  • Quirks モードについて
    これは特殊な互換モードなのですが、ちょっと難しいのでとりあえずは無視してよいです。本稿では説明を割愛します。
  • IE12 以降での取り扱いについて
    ドキュメントモードについては IE12 以降での取り扱いが変わることが決定しているため、今後の動向を見守る必要があります。これについては非常に重要なので、後述します。

また注意すべき点として、ドキュメントモードさえ変えれば過去の IE と完全に同じ挙動になるというわけではありません。例えば ActiveX コントロールに絡むセキュリティ強化などについては非互換要素があります。さらに、上では説明を単純化するため、IE が複数のレンダリングエンジンを搭載していると説明しましたが、物理的な観点で言うと、各 IE は一つの dll ファイル(MSHTML.dll)しか持っておらず、その中での切り替えを行っています。こうした事情があるため、ドキュメントモードさえ変えれば過去の IE と 100% 完全に同じ挙動になるというわけではありません(=100% 互換を保証するものでもないし、再テストを全くしなくてよいというものでもない)。とはいえ、一番問題になりやすい HTML/CSS の描画問題(描画崩れ)について大幅に問題を軽減できるため、このドキュメントモードを利用して過去の IE と同様のレンダリングをさせるという方法は、ブラウザのバージョンアップの際には非常に効果的な手法であると言えます。

[レスポンス HTTP ヘッダーによるドキュメントモードの調整方法]

とはいえ、このドキュメントモードの変更を個々のエンドユーザに行ってもらうのは非現実的ですし、そもそも Web アプリを作るときには、特定の IE バージョンに最適化させる形で作ることが多いと思います。このため、通常は Web アプリケーション側からブラウザに対して、「どのドキュメントモードでレンダリングさせるのか」を指定するようにします。具体的には、レスポンス HTTP ヘッダーに、以下の文字列を追加します。

ドキュメントモード

X-UA-Compatible

IE7 標準モード

IE=7

IE8 標準モード

IE=8

IE9 標準モード

IE=9

IE10 標準モード

IE=10

IE11 標準モード

IE=11

動作を確認したい場合には、以下のようにしてください。

  • Visual Studio を起動し、新規に空の Web アプリケーションをひとつ作成する。
  • Default.aspx ファイルを追加し、下記のコードを追加する。 (見づらいスクロールでごめんなさい;)
 1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>
 2:  
 3: <!DOCTYPE html>
 4:  
 5: <html xmlns="http://www.w3.org/1999/xhtml">
 6: <head runat="server">
 7: <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 8:     <title></title>
 9: </head>
 10: <body>
 11:     <form id="form1" runat="server">
 12:     <div>
 13:         ドキュメントモード <span id="lblDocumentMode"></span>
 14:     </div>
 15:     </form>
 16: </body>
 17: </html>
 18:  
 19: <script type="text/javascript">
 1:  
 2:     document.getElementById('lblDocumentMode').innerHTML = document.documentMode;
</script>
  • web.config ファイルを追加し、カスタム HTTP ヘッダーを追加するための設定を追加する。
 
 1: <?xml version="1.0" encoding="utf-8"?>
 2: <configuration>
 3:   <system.web>
 4:     <compilation debug="true" targetFramework="4.5.1" />
 5:     <httpRuntime targetFramework="4.5.1" />
 6:   </system.web>
 7:   <system.webServer>
 8:     <httpProtocol>
 9:       <customHeaders>
 10:         <clear />
 11:         <add name="X-UA-Compatible" value="IE=9"/>
 12:       </customHeaders>
 13:     </httpProtocol>
 14:   </system.webServer>
 15: </configuration>

以上でアプリケーションを動作させてみてください。web.config 内に記述した設定に基づいて、ドキュメントモードが変わることが確認できるかと思います。

image

レスポンス HTTP ヘッダーへのカスタム HTTP ヘッダーの追加方法は、Web サーバごとに異なります。IIS の場合には上記のように web.config ファイルを利用しますが、Apache などでは設定が異なりますので注意してください。

[(参考)リクエスト HTTP ヘッダー/サーバ側の挙動/ブラウザ側での解釈]

ここまでが基本的な IE のレンダリングエンジンに関する説明なのですが、もう少し突っ込んだ解説をここではしておきたいと思います。(ちょっと難しいので、「うーん?」となってしまった方は、本項目は飛ばして、次の項目を読んでいただいて結構です。)

我々が開発する Web アプリ(特に業務用 Web アプリ)のほとんどは、どんなブラウザからリクエストが来ても、通常、全く同一の HTML/CSS を送り返します。しかし、Web アプリによっては、どのブラウザのどのバージョンからリクエストが来ているのかをサーバ側で判定し、送り返す HTML や CSS を切り替えるという機能を備えている場合があります。例えば MSN のサイトは、同一の URL でありながら、IE11 で呼び出した場合と、IE9 で呼び出した場合とで、全く異なるデザインのサイトが表示されます。(左側が IE11 で呼び出した場合、右側が IE9 で呼び出した場合)(← 右のスナップショットは IE9 じゃないだろ! というツッコミはご容赦を;。本当は IE9 できちんとスナップを取りたかったのですがちょっと大変なので;;;。でもホンモノの IE9 で呼び出してもこんな感じの画面になります。)

image  image

呼び出すブラウザによって描画が変わる仕組みは、以下のようなものです。Web サーバに HTTP 要求を送る際、Web ブラウザは自動的に、自分のブラウザの種別やバージョンの情報をリクエスト HTTP ヘッダー(具体的には UserAgent ヘッダー)に含めるようになっています。我々が開発する Web アプリ(特に業務用 Web アプリ)のほとんどはこの情報を無視するため、どんなブラウザから来ても常に同一の HTML や CSS、JavaScript を送り返すのですが、MSN のようによく作り込まれた Web アプリでは、このリクエスト HTTP ヘッダーを読み取り、その情報に基づいて、クライアントに最適な HTML や CSS などを送り返すようになっています。このため、呼び出すブラウザのバージョンによって、大幅に画面が変わるということになるわけです。(ちなみに最近では、スマホからリクエストされた際に異なる HTML/CSS を送り返すように作り込まれた Web アプリが多くなってきていますが、仕組みはこれと似たようなものであることが多いです。)

image

このことは、以下のようにすると実験することができます。例えば、IE11 を利用しながらも、Web サーバに送出するリクエスト HTTP ヘッダーとして IE9 と同じものを使うと、Web サーバ側はやってきたリクエストが IE9 からリクエストされたものであると誤解します。そして、IE11 ブラウザに対して、IE9 に対して送り返すものと同じ HTML/CSS を送り返します。

具体的には、IE の F12 開発者ツールにおいて、エミュレーションタブの中の「ユーザーエージェント文字列」を、”Internet Explorer 9” に変更してみてください。IE11 でありながら、IE9 のときと同じ HTML/CSS をサーバから受け取ることができるようになります。

image

もちろん現実的に考えて、エンドユーザがこの F12 開発者ツールを開いてユーザエージェント文字列を切り替えて使う、ということはちょっと無理なわけで、あくまでこの機能は開発者向けの簡易デバッグツール(わざわざ古い OS や IE を用意しなくてもよくなる機能)だと考えた方がよいでしょう。ただ、それ以前のポイントとして注意していただきたいのは、ユーザーエージェントの文字列切り替え機能の利用は、そもそも Web サーバ側がユーザエージェント文字列を見るように作られていないと意味がない、という点です。先に説明したように、我々が開発する Web アプリ(特に業務用 Web アプリ)のほとんどは、どんなブラウザからリクエストが来ても、通常、全く同一の HTML/CSS を送り返します。このような場合、ユーザエージェント文字列を変更して、いくら Web サーバに「僕は IE9 だよ~」とアピールしても全く意味がありません。

ここまでの話をまとめると、以下のようになります。

image

  • IE の下方互換表示で特に重要なのは、① ユーザエージェント文字列の切り替えと、② ドキュメントモードの二つである。
  • ユーザエージェント文字列の切り替えを行うと、Web サーバに対して送出するリクエスト HTTP ヘッダーを変えることができる。
    • 多くの業務系 Web アプリは、どんなブラウザからリクエストが来ても同じ HTML/CSS を返すので、リクエスト HTTP ヘッダーの変更は意味を為さないことが多い
  • ドキュメントモードを利用することにより、Web サーバから受け取った HTML/CSS をレンダリングする方法を変えることができる。
    • 多くの Web アプリは、特定の IE バージョンから利用されるという前提で開発されている。このため、異なるバージョンの IE から呼び出された場合に備え、ブラウザで利用するレンダリングエンジンのバージョンを、X-UA-Compatible レスポンス HTTP ヘッダーで指定しておくとよい。

なお、ここでは話を簡単にするために、以下のような点の解説を省いています。より深い知識を得たい方は、以下のようなポイントについても学習するとよいと思います。詳細はネット上に情報が多数存在しますので、検索してみるとよいでしょう。

  • 上記ではサーバ側からレンダリングエンジンバージョンを指定する方法として、レスポンス HTTP ヘッダーを利用する方法に絞って説明しました。この方法は、すでに作られている Web アプリに対して、後からまとめてドキュメントモードを一括指定したい場合に特に有効な方法です。しかしこれ以外にも、meta 要素を指定してページ単位にドキュメントモードを制御する方法もあります。
  • 実際のブラウザ内では、かなり複雑なロジックに基づいてドキュメントモードが決定されており、しかもこれが IE のバージョンによって違います。少し古い資料ですが、例えばこちらのような資料を見ていただくと、その決定ロジックがかなり複雑であることがわかると思います。思ったとおりのドキュメントモードにならない場合には、こうしたロジックを少し深く調査していく必要があります。 (が、そうした調査は大変なので、業務 Web アプリではレスポンス HTTP ヘッダーをいじってしまうのが最も簡単だと思います。)
  • 古い IE にはブラウザモードという機能が搭載されています。これはユーザエージェント文字列を切り替えると共に、ドキュメントモードの判定ロジックにも影響を与えるものになっています。
  • IE には互換表示設定(互換表示リスト)という機能があります。これを利用すると、特定の Web サイトに対して送信するユーザエージェント文字列や、利用するドキュメントモードを調整することができます。例えば(本稿執筆時点では)、store.apple.com のサイトを呼び出す場合は、ユーザエージェント文字列として IE10 のものが、また描画には IE10 のドキュメントモードが自動的に利用されますが、これは互換表示リストに以下の記述があるためです。

    <domain docMode="EmulateIE10" versionVector="10" uaString="IE 10" featureSwitch="overrideXUACompatible:false" trackingid="481134">store.apple.com</domain>

    互換表示リストに関しては、hebikuzure さんのこちらのエントリが詳しいです。興味がある方は読まれてみるとよいかと思います。

[クライアント OS やブラウザのバージョンアップに対する戦略]

さて、ここまでドキュメントモードを利用したレンダリングエンジンのバージョン変更方法について解説しました。この機能を利用することにより、クライアント OS のバージョンアップやブラウザのバージョンアップは、何も考えずに IE をバージョンアップする場合に比べて随分とラクになるはずです。しかし、だからといって昔のレンダリングエンジンをいつまでも際限なく利用し続けようとか、あるいはこのドキュメントモードを使うのもイヤなのでいつまでも Windows XP を使い続けようとか、そういった考え方は決して正しくないと思います。これらはいずれも、汎用製品の宿命であるサポートライフサイクルというものを無視した考え方だからです。

マイクロソフトの製品は、業界の中で見れば比較的製品サポートの長い会社であり、さらに IE に関していえば、IE11 でも未だに IE7 のレンダリングモードを搭載しています。身内の人間だから言うわけではないのですが;、エンジニア的な目線だと、たまに「ここまでやっているのにまだ文句を言われるのか;;」と思ってしまう瞬間もやっぱりあります。もちろん私もエンジニアなので、「一度作ったアプリはなるべく塩漬けしてそのままそっと放置したい」という気持ちもわかるのですが、その一方で、先々のことを考えずにアプリのことを作りっぱなしにしすぎなんじゃないか? と思う瞬間も、やはりあるのです。

OS からミドルからすべて自作するとお金がかかります。だからこそ我々はコストを下げるために汎用製品を利用してモノづくりをするわけですが、そうである以上は、作ったタイミングから先々のサポートライフサイクルを想定し、自分たちのアプリのアップグレード戦略を考えておく必要があります。おおまかにいえば、このアップグレード戦略には以下の 2 つの方式があります。

image

方式①の、タイムリーに最新版の OS やランタイムに載せ替えていく方式は、昨今、Agile や Rapid Release などの普及に併せてよく取沙汰される方式です。インターネット上でコンシューマ向けサービスを展開している企業では、こちらの方式が利用されていることが多いでしょう。理由は単純で、こうしたシステムはそもそも頻繁にアプリのコードを書き替えているため、それに合わせて最新版のインフラに取り換えていくことが容易だからです。また、この方式を取る場合にはリリース頻度が自ずと増えるため、テスト実施の効率化(例えばテストの自動化など)についても積極的に取り組んでいることが多いでしょう。昨今、Rapid Release 方式を取り入れる製品が多くなってきたこともあり、コンシューマ系では否応なくこうした対応が求められてきています。

一方、業務系のアプリ、特にイントラネット系の業務アプリでは、②の方式、つまりある程度の期間塩漬けしておき、一気にアップグレードするという戦略を採っているケースの方が多いでしょう。こうした戦略を採る背景には、イントラネットであればクライアントのバージョンなどを固定化しやすい、再テスト工数などを極力減らせる、などがあると思います。しかしこの方式②の怖いところは、いざバージョンアップが必要になったときに対応が後手に回りやすいことです。実際の現場でよく見かける例を挙げれば、以下のようなことがあります。

  • 徹底的に塩漬けを続け、サポートが切れる段階になって慌てて対応しようとする。(特に XP と IE6 に関してよく見られます)
  • 移行時に最新インフラを選ばなかったり、移行検証に時間がかかりすぎてどんどん古くなってしまう。(XP から今さら Vista に移行しようという話があったりします……)

こうしたインフラのアップグレードがそう単純でないことは私も承知していますが、とはいえ対応が後手に回れば回るほど、追い詰められやすくなるのも事実です。特に、予め、サポート切れを見込んだ戦略を立てていなかったり、対応工数・コストを予算として積んでいないと、こうした問題は致命的になりやすいです。②の方式を採るのであれば、戦略なり予算なりをきちんと検討しておくことが欠かせません。

[OS やランタイムのバージョンアップをなるべくラクにするために]

実際の OS やランタイムの移行に際しては、本稿で説明している「ドキュメントモード」のように、移行時のデグレードリスクを極力下げるような移行方法を検討することが極めて重要です。特に、②のような移行スタイルを採る場合、何も考えずにそのまま最新の OS やランタイムに載せ替えれば、多くの場合は何らかの互換問題が発生してしまうはず。アプリ修正を完全に不要にすることはできないにしても、極力、修正量を少なくするような移行方法を検討するべきです。主な検討ポイントとしては、以下のような点が挙げられます。

  • クライアント OS のアップグレード
    • .NET デスクトップアプリであれば、極力同一の CLR を利用するように設定する。
    • ブラウザアプリであれば、極力同じドキュメントモードを利用するように設定する。
  • サーバ OS のアップグレード
    • IIS が、極力同じ振る舞いをするように設定する。(アプリケーションプール、パイプラインモード、64 ビット化、CLR バージョンなど)

また実際に OS やミドルのバージョンアップを行った場合には、アプリの再テスト(デグレードテスト、リグレッションテスト)が必要になりますが、テスト戦略なしにフルパステストを再度行うようにしてしまっては、膨大なコストがかかってしまいます。これを避けるためには、以下のようなことをしておくことも欠かせません。

  • アプリケーションのコード資産を、常に綺麗に整理しておく
  • リグレッションテストをしやすいように、テストケースやテスト結果データなどをきちんと整理しておく

なお、再テストの容易化というと、真っ先にテスト自動化に飛び付こうとされる方がいらっしゃるのですが、それは必ずしも正しいアプローチではありません。例えば JUnit/NUnit などの単体テスト自動化ツールはそもそもコードのリファクタリングに対しては有効ですが、上記のような OS やミドルのアップグレードに対してはさほど有効とは言えません。また、UI レベルから行うテスト自動化ツール(例えば Coded UI Test など)は、打鍵の自動化はしてくれますが、結果検証に関しては不完全です(例:自動化ツールでは文字化けや画面崩れに気付けない)。むしろ、① テストの適切な分類、② テストケースの重要度の濃淡付け、③ 変更・修正インパクトに応じたテストケースの適切な選出ルール、などを決めて実施していくことの方が、圧倒的にコスト効果の高い再テストにつながります。昨今の開発現場のソフトウェアテストは場当たり的に実施されていることが少なくありませんが、まずそうしたやり方を実直に見直していくことも、OS やランタイムのアップグレードへの対応容易性を高める上で極めて重要です

ちなみに、マイクロソフトはソフトウェアテストの専用ツールとして、MTM(Microsoft Test Manager)というツールを提供しています。Visual Studio に比べると知名度の低いツールではありますが、MTM を理解すると、正しいテストの在り方や効果的なテストの実施方法がどのようなものなのかを知っていくことができると思います。開発者の場合は、作ることに手一杯で品質保証まで手が回らないよ! 的な方も多いでしょうが、ある程度モノづくりができるようになってきたら、こうしたソフトウェアテストに関しても知見を深めていくことをおすすめしたいです。とはいえ MTM の話は本題ではないのでまた機会を改めて。

image

[Rapid Release 時代の到来による変化と対応の考え方]

さて、今回なぜこのようなソフトウェアテストなどの話まで話題を広げて書いたのかというと、それは、今、多くの企業が OS やインフラのアップグレード戦略を見直さなければいけない時期に差し掛かっているからです。その背景にあるのが、Rapid Release 時代の到来です。下表を見てみてください。

image

(途中で Windows 98 の延命用 OS として Windows Me が出た等の例外を除けば)もともとマイクロソフトは、2~3 年に一度程度のサイクルで新しい OS をリリースしてきました。しかし 4 年前に発売された iPad というタブレットが PC 市場を一変させてしまったことからもわかるように、数年程度に一度というリリースサイクルでは、今の時代ではとても戦えないのが実情です。

いやいやそれはコンシューマの世界であってエンタープライズの世界(企業向けシステムの世界)では関係ないでしょう、と思われている方もいるかもしれませんが、それは誤りだと思います。歴史を振り返ってみれば、Windows を初めとする PC がメインフレームなどの大型コンピュータに取って代わったのと同じように、IT 業界のトレンドというのはコンシューマ市場から発生してくることが少なくありません。実際、スマホやタブレットの業務利用などはその最たるもので、コンシューマ市場においてユーザがその利便性に気付き、ビジネスへの応用を考える、ということがしばしば起こります(この現象は IT のコンシューマライゼーションと呼ばれています)。このことを考えれば、PC 市場で大きなシェアを持つマイクロソフトも、数年に一度といったのんきなペースで OS をリリースしているわけにはいきません。Windows 8 から 8.1 へたった一年でアップデートしてきたことも、こうした背景事情を考えれば必然的なものです。

しかし、こうした Rapid Release 時代への移行は、様々な変化を生み出します。例えばその一つが、IE ブラウザのドキュメントモードに関する方針変更です。実は IE11 がリリースされた際に、このドキュメントモードが deprecated 扱い(利用を推奨しない機能)に変更されました。ここまで IE は、バージョンが上がるごとに過去のレンダリングエンジンバージョンを残し続けてきたわけですが、すでに IE11 では 8 個ものドキュメントモードを持っており、このまま OS や IE のリリースのつどレンダリングエンジンを残すようにするのでは、製品サポートも含めて無理が出てきます。また、新しい IE では業界標準仕様に強く準拠するようになってきており、今までのように、マイクロソフトの独自仕様を残すためのドキュメントモードを残す必要性というのも昔に比べて減ってきています。さらに加えていうなら、昨今の Web 開発のトレンドを踏まえれば、ブラウザアプリは常に最新の業界標準仕様に追随すべきであり、ドキュメントモードを非推奨扱いに変えていこうという動きも、(インターネットの世界のトレンドだけ見れば)ある意味自然な流れともいえます。

その一方で、企業内のアプリがこうした頻繁なアップデートサイクルにリアルタイムで追随していけるようになるかと言えば、それはなかなか難しいと言わざるを得ず、実際には従来のような方式②、つまり適度な期間の塩漬けと最新版へのアップデートを繰り返す形になることが多いと思います。そうした方針がきちんと立っていないのであれば、この機会に、自社がどのようなアップデート戦略を採っていくのかを考えていく必要があるのではないでしょうか?

また、これに併せてぜひもう一つ考えていただきたいのが、本当に Web ブラウザが業務アプリの開発インフラとして最適なのかどうか? という本質的な問いかけです。業務アプリを開発する場合、OS やミドルが安定していて欲しい(=変わらないで欲しい)というのは当然の要望だとは思いますが、ここまでの話からもわかるように、Web ブラウザというものは、そもそも業務アプリ開発用のインフラとして作られているものではなく、むしろコンシューマにおける利用を前提に、開発とアップデートが繰り返されてきているものです。これは、Windows 8 においても未だ VB6 ランタイムがインストールされていることと比べると、あまりにも大きな違いです。(今だと、CLR 2.0 ランタイムが VB6 ランタイムのように、かなり長い間に渡って残り続けそうなランタイムになりつつありますね。)

日本にはブラウザ神話とでも呼ぶべきものがあり、なんでもかんでも Web ブラウザ上に載せようとする傾向があるように思います。多数のテキストボックスが所狭しと詰め込まれた画面、Fn キーによる入力制御、ドラッグ&ドロップ操作の多用、マルチウィンドウ制御、スプレッドシート処理などなど、開発生産性や保守性を度外視したような Web アプリが作られていることが少なくありません。こうしたアプリは、ブラウザのバージョンアップでの下方互換問題に当然ひっかかりやすいわけですが、根本的なところとして、そもそもそれは Web でやるべきだったのか? と言いたくなる場合もあります。RFP で最初から Web ブラウザに縛られているからといった具合にやむを得ないケースもあるでしょうが、頭ごなしになんでも Web でやろう、という発想は、今一度考え直すべき時期に来ているのではないでしょうか?

業務アプリケーション開発は、開発生産性や保守性など、様々な要素のバランスを取って行うべきもの。「作ること」だけを考えるのではなく、「保守すること」、すなわち OS やインフラのアップグレードにどのように追随していくのかを予め考えるようにしていただければと思います。

[まとめ]

いろいろと話が脱線しましたが、最後に要点をまとめると、以下のようになります。

  • 古い IE のバージョン用に作られたアプリを最新の IE で動作させる場合には、ドキュメントモードを変更して動作させるとよい。このようにすることで、描画に関する非互換問題を最小化していくことができる。
  • ドキュメントモードは、Web サーバから Web ブラウザに対して、X-UA-Compatible レスポンス HTTP ヘッダーを追加することで指示できる。Web サーバの設定を変更することで、アプリ全体に対してまとめて指定してしまうとよい。
  • 企業システムにおいては、クライアント OS や IE のバージョンをどのように上げていくのかの戦略が必要である。特に、OS や IE の Rapid Release にどのように対応していくのかの指針を定めておくことが重要である。
  • 実際のアップグレードを円滑に行うためには、特に以下の 2 点が重要である。
    • OS や IE のバージョンアップ時のデグレリスクを最小化するために、ドキュメントモードをはじめとした互換設定をうまく利用する。
    • デグレテストをなるべく容易化するために、テストケースやテスト結果データなどをきちんと整理しておく。

今回は話を簡単にするために、かなり解説を端折っていますが、この機会にもっとこの領域を詳しく知りたい! もっと正確に知りたい! という方は、ぜひ以下の資料も読んでみてください。コンサルティングサービスの森さん、長本さん、稲葉さんによる渾身の一作です。(実はこのエントリも、森さん・稲葉さんにレビューしてもらいました。お二方、本当にありがとうございました^^。)


de:code 2016 「拝啓『変わらない開発現場』を嘆く皆様へ」を担当して

$
0
0

実に2年ぶりのエントリになりますが;、今日はこちらの話題を。

少し前の話になりますが、5 月下旬に弊社イベント de:code 2016 で、ちょっと変わったセッション「拝啓『変わらない開発現場』を嘆く皆様へ ~エンプラ系 SI 開発現場の「今」を変えていくために~」を担当させていただきました。内容は、エンプラ系 SIer のプロパー(PL, PjM, SE)の方々向けに、設計やテスト手法の改善テクニックの要点などを通して、開発現場を改善していくための考え方を解説する、というもの。未来をお届けするイベントなのに最新技術を一切説明しないという異色のセッションにもかかわらず、参加者満足度(NSAT)ランキングで全 125 セッション中 2 位という結果になったことに大変感謝する一方で、私自身、いかに日本のエンプラ系 SI の闇が深いのか、を改めて実感することになりました。

セッションの内容については近いうちにテキスト化してこの blog にも掲載したいと思っているのですが、できればぜひ 1 時間ほど皆様のお時間をいただき、録画ビデオを見ていただければと思います。ダウンロード可能なので、通勤中にスマホなどで見ることも可能です。(はてブでは 100 人以上の方からブクマいただきました。ありがとうございます。m(_ _)m

そして、この内容がよいと思われた方は、ぜひ周りの方にこのビデオをそのまま紹介ください。特に SIer のプロパー(SE, PL, PjM)の皆様、そしてさらにその上司であるマネージャの方々に

もちろん、現場の皆様ご自身がこうした訴えかけをロジカルかつ根気強く上申していくことが最も重要ですが、その一方で、「当事者が言っても意見を聞いてくれない」という話もよく聞きます。そういう場合には、外部の第三者の声をうまく使って伝えていくことも必要なのだと思います。ぜひ、そうした目的のためにこちらのビデオや資料をうまく活用していただければと思います。社内勉強会やコミュニティなどでもぜひご活用ください。


……というわけで、ここから先は私個人の「つぶやき」です。(できればビデオを見た後に読んでください。)

今回、参加した方々の約半数からコメントをいただきました(← かなり高いコメント率です)。「自社のことかのように感じた」「心に刺さる内容だった」「de:code らしくはないが、このセッションの内容は本当に役立つ内容だった」といった内容のコメントが多く、とても有難く思うと共に、本当に皆様が現場で苦しまれているのだということを改めて実感しました。

そうしたコメントの一方で、「既に実践していることだらけ、レベルが低いのでは?」「新しい考え方などは得られなかった」といった厳しいフィードバックも(少数ではありますが)いただきました。忌憚のないご意見をいただき、こちらも本当にありがとうございました。m(_ _)m

今回、もう少し突っ込んだ話ができなかったことを本当に申し訳なく思うのですが(ごめんなさい;)、こうしたご指摘に関しては、私も全くもって「その通り」だと思いますし、またそれ以外にも、内容自体が受け入れがたい、という方もきっといると思います。このセッションの評価は、おそらく非常に高評価か低評価か、どちらかに極端に振れるだろうと思っており、セッションの内容についても内心、「こんなシンプルな内容で本当によいのか?(もっとその先の話をすべきじゃないのか?)」とものすごく悩んでいたのも実際のところです。そして出てきたスコアやコメントを見て「ああ、やっぱりそうなのか;」と、改めて日本のエンプラ系 SI の難しさを実感した一方で、「そんなの当たり前じゃん」と言える開発現場も確かに日本にある、と思えたことも、自分的には大きな救いでした。

願わくばこのセッションの内容に対して、日本のみんなで「そんなのレベル低すぎる」と自信を持って言える時代が来ることを。目指す先は「根拠のある作業」「根拠のある SI」。そこに向かって、日本の現場の開発者と SIer の皆様のみんなで知恵を出し合い、議論を深め、日々少しずつでも改善を続けて進んでいければと思っています。超えるべき障壁、解決すべき課題、そしてその解決方法も現場ごとにいろいろ違いがあると思いますが、みんなで頑張りましょう!

……というわけで、まるで更新してないこちらの blog ですが、今後もよろしくお願いします。m(_ _)m(← おい;)

# よかったら、動画を見た皆様の忌憚のないご意見もお聞かせください。ものすごく参考になりますので。

続・拝啓『変わらない開発現場』を嘆く皆様へ ~ ウォータフォール & アジャイル編~

$
0
0

ご存知の方も多いと思いますが、ここ最近、うちの会社の歌って踊れる DevOps エバの牛尾さんが、こんなエントリを書かれていました。

特に前者は炎上気味でしたが;、二回分のエントリを通して読めば、牛尾さんが言いたいことは極めてシンプルで、『変わらないのは回りのせいじゃなくて、あなた自身が変わらないせいだよね』(他責じゃなくて自責で考えよう)ということはわかるはず。伝え方の部分に関しては少し(いやかなり;)乱暴だとは思いますが、メッセージ自体は実におっしゃる通りで、耳が痛いという人もきっと多いと思います(私もそうです;)。そんな自由人な牛尾さんを見てすごいなぁとは思うものの、その一方で、はてブの反応を見ていると、そんなこと言ってもじゃあどうすればいいのよ? と思っている人も多いんじゃないかと思います。

もちろん本質的な意味で、人を成長させることができるのは、その人本人だけです。その人が自分の頭で考えて、自分の体で頑張るしかない。けれども、変われないと揶揄されている人たちだって、変わりたいと思っているけれどもきっかけやヒントが掴めなかったりして苦しんでいる人が圧倒的多数で、その苦しみの表れが、「現状や周囲に対する不満や文句」じゃないかと思うのです。だから、ちょっとしたヒントやきっかけ、ちょっとした環境変化、あるいは何かしらの実践可能かつ具体的なアイディアがあれば、それらを元に変われる人って実は多いんじゃないかな? とも思います。もともとこの話は先日の de:code 2016 のセッションで時間の関係上語れなかった後半のネタであること、また言いっぱなしで済まされない開発現場で生きてきたコンサルタントの自分としては、なるべくロジカルかつ具体的かつ実践可能なヒントやアイディアを提供したいと思ってこのエントリを書くことにしました。長文ですが、お付き合いいただければ幸いです。

なお念のため先にお断りしておきますが、私は本業のプロマネではなく、またこの手の話に唯一無二の正解は絶対に存在しません。数千万円の開発規模での常識が数十億円~数百億円の現場で通じるとは思いませんし、マイクロソフトのようなパッケージ製品開発の会社の常識や手法が、カスタム SI 開発の世界にもそのまま当てはまるとも思いませんし、契約形態にしろシステム特性にしろ成熟度にしろ、現場ごとに常識も違えばソリューションも違います。主にはエンプラ系 SI 開発現場を想定してこのエントリを書いていますが、個々の皆様の開発現場とはそぐわないところが多いと思います。そこは差し引いて読んでください。

とはいえ皆様一人一人が、自分のかかわるシステムの特性をきちんと把握し、自分自身の問題として何をどう変えられのか、誰に何をどうやって訴えかけるべきなのかをロジカルに考えることが最も大切です。そんなに簡単に物事は変わりませんが、それでも変えていく努力を続けることが、私は大切だと思います。


■ V 字型モデル、ウォータフォール型開発、スパイラル開発、アジャイル型開発

話を始める前に、まずこれらの基本的な違いをシンプルに説明してみたいと思います。(※ 以下はわかりやすくするために超乱暴な議論をしています。そこは承知しているので、わかっている方は申し訳ないですが差し引いて読んでください。また、学術的な定義はズレてるかもしれませんが、このエントリではここに書く内容を定義として使わせてください。)

世の中には様々な開発方法論がありますが、どのような開発方法論であっても、その根底にあるものは V 字型モデル、すなわち段階的詳細化と段階的テストです。要するに、与えられた要件を段階的に詳細化してプログラミングし、出来上がったモジュールを、段階的にくっつけながらテストする、というモデルです。

clip_image001

この V 字型モデルの考え方、すなわちモノ作りは「設計」→「実装」→「テスト」によって行われる、という考え方は、ウォータフォールだろうとアジャイルだろうと基本的に変わりません。たまに勘違いしている人がいますが、アジャイルだって設計もするしテストもするし、必要に応じてドキュメントだって書きます。むしろこれらをないがしろにすれば、アジャイルでも品質があっという間に破綻します。(もちろん無駄なドキュメントは書かないようにしますが)

では、ウォータフォール開発、スパイラル開発、アジャイル開発の違いは何かというと、複数の機能群を作らなければならないときに、これらをどのように並行開発するのか、という点に違いがあります。例えば、A~D の 4 つの機能を開発しなければならないときを考えてみます。

clip_image002

  • ウォータフォール型
    • 4 つの機能を、いっせいに同期を取りながら、設計→実装→テストします。各作業が同期を取って進むために、プロジェクトの管理がしやすい(というかラク)というのが特徴です。
    • その一方で、「動くもの」が出来上がるタイミングがかなり遅くなります。このため、仕様ミスなどの発覚が遅れ、修正コストが高くつきやすくなります。また後から仕様変更が発生した場合も、仕様変更を取り込みにくいという難点があります。
  • スパイラル型
    • ……であるのなら、まず特に重要な機能(この図だと A, B)に絞って先に設計→実装→テストしてしまい、その後に残りを開発する、というモデルにすればよいはずです。これがスパイラル型開発です。開発リソースを A, B に集中できる分、「動くもの」が出来上がるタイミングはウォータフォールよりは早くなります。
    • しかしここで問題になるのは、『いくつのスパイラルに分割するのか?』です。「早くにとりあえず動くものができる」「仕様変更の取り込みタイミングが増える」というメリットを追求するのであれば、2 スパイラルよりも 3 スパイラルの方がよく、また 3 スパイラルよりも 4 スパイラルの方がよい、ということになっていきます。
  • アジャイル型
    • これを究極的に推し進めると、『重要な機能から、五月雨式に機能を作っていく』『必要に応じて随時、仕様変更を取り込んでいく』となるはずです。これがアジャイル型です。

こういう構図で考えた場合、ウォータフォール型開発とアジャイル型開発の基本的な相違点として、以下があることがわかります。

  • ウォータフォールだろうがアジャイルだろうが、ある特定のひとつの機能だけ取ると V 字型モデル。よって、段階的詳細化と段階的テストをちゃんと実践できない人は、そもそもアジャイル云々以前の問題。V 字型モデルをちゃんと実践できない人がアジャイルに取り組もうものなら悲惨なことになります;
  • ウォータフォールからアジャイルに向かっていく際のメリットは、動くモノが早く出てくることと仕様変更の取り込みタイミングが増えていくこと。逆にデメリットは、プロジェクト管理が複雑化すること。例えば進捗管理で言えば、五月雨式に進んでるけどいつ終わるの? という質問にパッと答えにくくなりますし、変更管理で言えば、いつどこでも発生してくる可能性のある変更をどうやって管理するんだ? という話になります。これらをうまく管理しようと思うと、数値ベースのプロジェクト管理技術が必要で、昔ながらの KKD (勘と経験と度胸)に頼った Excel ベースのゆるふわプロジェクト管理では、全く歯が立ちません

clip_image003

様々な開発現場を見てきた経験から言うと、特に大規模開発では純粋ウォータフォール(=一発勝負での開発)でやっていることなんて皆無で、少なくともスパイラルに開発してたりプロトタイピングによってリスクヘッジしてたり、何らかの工夫をしているのが普通です。要件定義フェーズのみアジャイルで、以降の開発ではウォータフォール的に進める、なんていうこともよくあります。重要なのは、方法論の特性の違いを正しく理解し、対象システムや開発現場の特性、契約形態などに合わせて最適な方法論を使うことです。(少なくとも現時点ではそうしないと、実際のプロジェクトはうまくいかないです。十把一絡げに Scrum でいいじゃん! というわけにはいかないのが今の日本の実情です。それが必ずしも望ましいことだとは言いませんけれども。)

また、私の経験からすると、実際の現場はたいてい WF vs agile のレベル以前のところに大量の問題があります。すなわち段階的詳細化と段階的テストが正しくできておらず、V 字型モデルの作業でムリ・ムダ・ムラがめちゃめちゃあります。これは先日の de:code で話した通りで、このため、現在のウォータフォール/スパイラル開発のままでもまだまだ相当の改善の余地があります。というわけで、まずはアジャイル云々以前の話として、現状の開発作業のムリ・ムダ・ムラを取り除き、ちゃんと効率的な V 字モデルを実践できるようになってほしい、と思います。

……というわけで以上で終わりです、とか言うとボコボコにされそうなので;、以降はアジャイルの話にいったんフォーカスを絞ります。

 


■ 日本でなぜアジャイルが進まない?

よく言われることですが、日本でアジャイルが進まない原因のひとつに、SI の多重請負構造があります。まあ要するに、いつ終わるかわからない仕事を、完成責任を負わせられない形で任せるなんてできないよ! という話で、まったくもってごもっとも。そしてこの話になると、だから請負じゃなくて委任にすべきなんだ! 日本の悪しき伝統文化を終わらせるべき!的な議論とかが出てきて、信者同士の不毛な話になります。私は正直、そんな不毛な言い争いには全く興味がないというかそんな宗教論争みたいな話をしているからアジャイル技術の活用が進まないんだと思う)ので;、もうちょっと別の角度から話を整理してみたいと思います。

現場目線で言うと、アジャイルに関してはまず 2 つのレベル(粒度)に分けて考えた方がよい、と思います。

  • ①日常的なタスクといった細かい粒度でのプロジェクト管理手法としてのアジャイル
  • ➁サブシステム開発といった比較的大きな粒度でのプロジェクト管理手法としてのアジャイル

そしてもう一つ重要なポイントですが、一般論として、アジャイルは請負契約の境界を越えて適用するのが非常に難しい技術です。背景には、発注側と受注側の力関係による、変更管理の難しさがあります。

  • 例えばマイクロソフトのような内製中心の会社の場合、アジャイル技術や DevOps 技術を多用して、随時仕様変更を取り込むとともに、生産性を高めていくことは非常に有効ですし、やりやすいです。なぜなら、たとえ仕様変更が後から多発してお金が追加でかかってしまった場合でも、請負契約があるわけではないので、自社の中の問題として対処していくことができるためです。
  • 一方で、カスタム SI の請負契約の場合、ウォータフォールだろうとアジャイルだろうと、後から発生した仕様変更を正しく変更管理し、必要に応じて追加請求を行うことができないと、請負契約はあっさり破綻します。しかし、発注側が「アジャイル」という手法に最も期待するのは、「仕様変更をバンバン取り込んで、自分の望むものを完璧に作り上げてくれること」です。このため、ベースライン(どこからを変更とみなすのか? の基準となるもの)をきっちり合意し、そして発注側から要求される仕様変更をきっちり管理し、必要に応じて請負側からお金を追加請求できる状態になっていないと、うまくいくはずがありません。このことは、正直、仕様をかっちり決めてそれに基づいて進めるという合意があるはずのウォータフォールですら難しい(追加費用なしで後から仕様変更を無理矢理ねじ込まれる)のが実態なので、アジャイルであればなおさら難しいことになります。

ウラを返せば、アジャイルは請負契約の境界を越えなければ、適用するのが割と容易な技術です。例えば以下のようなケースを考えてみます。

  • エンド企業が SIer に対して請負契約で発注。
  • SIer が協力会社から開発者を準委任で集めてシステム開発を担当。(※ 実際には多重請負構造になっていることがほとんどですが、話を単純化するためにこの設定にします。話の本質がわかれば、ここがカスケードの請負契約になっている場合の解も論理的に検討できると思います)

clip_image004

この場合、最も簡単なのは、SIer + 協力会社の中に閉じてアジャイルプラクティスを回す方法です。


■ 請負契約の内部でのアジャイルプラクティスの活用

SIer + 協力会社の内部で作業効率を高めたい場合には、まず以下の 2 つの Agile プラクティスの活用がオススメです。前者の方が応用範囲が広いので、ここでは前者のみ解説します。

  • 日常的な担当者レベルの作業管理に Scrum のスプリントプラクティスを活用する
  • 実装作業において、CI/CD プラクティスを活用する

[Scrum のスプリントプラクティス]

Scrum って世の中でいろいろ説明されてるのですが、一般的な説明は正直わかりにくいと思うので、私なりの現場目線で Scrum のスプリントプラクティスを語ってみたいと思います。(専門家の方の中には「ちげーよ;」という方もいらっしゃるかもしれませんが、片目を瞑るぐらいで読んでいただけると助かります;)

  • まず、システム開発の作業を 1~3 週間程度の固定の長さ(スプリントと呼ばれる)ごとに区切る。(※ スプリント期間の取り方はフェーズや状況によりまちまちですが、基本的には比較的短めに切る。)
  • 各スプリントの初日のタイミングで半日程度をかけて、各作業者にそのスプリントに実施する作業タスクと、その作業時間の見積もりを作ってもらう。この際、各タスクは、1 タスクあたり最大でも 10h ぐらいになるまで細かく分解する。(スプリント計画ミーティングと呼ばれる)
  • 作業担当者には、毎日 1 回、帰り際に、「今日終わったタスクはどれか?」「明日やるタスクはどれか?」「終わらなかったタスクについては、あと何時間で終わらせられるか?」を報告してもらう。

プロジェクトマネージャは、残っているタスクの作業時間を毎日一回合算してグラフ化します。そうすると、下図のように「残りの作業時間がどんどん減っていき、スプリントの最後ではゼロになる(はず)」というグラフができます。このグラフをスプリントバーンダウンチャートと呼びます。

clip_image005

実際にやってみると、スプリントバーンダウンチャートはそんなに綺麗な図にはなりません。例えばこんな感じ。タスク見逃しで、なぜか途中で総作業残時間が増えたりすることもよくあります。また下図では示していませんが、各スプリントの最後で残作業時間がゼロにならない(=計画した作業が終わらない)なんてこともよく起こります。(※ この場合でも、スプリント期間を延長することはしません。終わらなかったタスクは次のスプリントに繰り越します。)

clip_image006

このスプリントの仕組みの最大のメリットは何か? いろいろ言われていますが、私が思う最大のメリットは、現場担当者のボトムアップの作業見積もりスキルを向上させることができるという点です。現場目線で言うと、このメリットの大きさは計り知れないモノがあります。少し補足すると、以下の通りです。

  • WBS に基づく見積もりを行う場合、一般的に見積もり手法には、トップダウンアプローチとボトムアップアプローチがある。このうち、一般的にはボトムアップアプローチの方が見積もり精度が高いと言われるが、実際にはうまくいかないことが多い。これは、現場担当者が普段から見積もり作業に慣れているわけではないために、やたらとバッファーを積んだり、逆にできもしない作業を少ない作業時間見積もりでできると言ってしまったりするため。結果的に、各作業者から提示された見積もり作業時間を、PjM が KKD で増やしたり減らしたりして使っているのが実態だったりします。
  • これに対して Scrum では、スプリント終了時に各作業者に振り返りをしてもらい、次のスプリントでの見積もりに生かしてもらう、ということをします。往々にして現場担当者は見積もりを大きく外すのが常ですが、同じタイムボックス(※ スプリントの長さは必ず一定)を何度も繰り返していれば、それなりに見積もりスキルが上がってくるはずです。
  • 現場担当者の見積もりスキルが上がってくれば、チームが 1 つのスプリントでこなせる作業量が正確に見積もれるようになってきます。それがわかると、スプリントを繰り返していったときに当該システムの開発が本当に期間内に終了するのかどうか、というのが見えてくることになります。(これをリリースバーンダウンチャートと呼びます。)

clip_image007

これが Scrum のスプリントの基本的な仕組みなのですが、このミクロスコピックな作業管理のやり方は、ウォータフォールなどの大きなレベルでのシステム開発の進め方と全く矛盾しません。ですから、例えばシステム開発全体のプロジェクト管理としてはウォータフォール的にやるけど、ミクロスコピックな日常的な作業管理としては Scrum でやる(それによって設計フェーズの作業がちゃんと期間内で終わるかどうかの予測が立てやすくなる)、なんていう組み合わせ方は、割と取り組みやすいアジャイルプラクティスの導入方法です。

……一流のアジャイラーとかからすると眉間に皺を寄せられそうな断片的な使い方かもしれませんけど;、実際の現場経験からするとこれだけでもかなり効果絶大なのです。いやだって、現場エンジニアって割といいかげんな作業計画で日々の仕事を進めていることが多いので、毎週の仕事をきちんと宣言してもらって予実管理するだけでも、ものすごい効果があるのですよ^^。

[スプリントプラクティス導入時の 2 つのポイント]

ただしこのスプリントプラクティスを導入する際には、いくつか注意点があります。中でも大きいのは以下の 2 つです。

  • タスクボードなどを導入し、作業者と PjM にほとんど負担をかけずにバーンダウンチャートを作る必要がある。
  • PjM/PL は、メンバーのやる気やモラルを最大限に引き出すことに注力する必要がある。

まずはタスクボードから。タスクボードとは、作業内容と残作業時間を書き出した付箋紙をホワイトボードに貼り付けたようなもので、3 つのレーン(未着手、作業中、作業終了)間で付箋紙を移動していくというものです。各作業担当者は、毎日、帰りがけにこの画面を開き、付箋紙をレーン間で移動させ、終わっていないタスクについては残作業時間をアップデートします。そうすると、システムがこれを自動集計してバーンダウンチャートを作ってくれます。超便利 & 超らくちんです。

clip_image008

タスクボードは、マイクロソフト製品だと Team Foundation Server 上に実装されており、オンライン版(Visual Studio Team Service)であれば 5 ユーザまで無償で使えます。めちゃめちゃ素晴らしいツールなのですが、より詳細な使い方についてはここでは説明しませんので、DevOps エバの牛尾さんに聞いてみてください。(ぉ

ちなみにこのタスクボードの仕組みから理解していただけると思いますが、Scrum のすごいところは、プロジェクト管理の仕組みの本質を捉えることで、非常にシンプルな「残作業時間の足し算」という仕組みだけで、手間をほとんどかけずに、プロジェクトの状況のリアルタイム把握を可能にした点です。もちろん、大規模システムへの適用などに関してはそんなに単純ではないのでいろいろ工夫が必要なのですが、KKD、あるいはやたらと複雑な仕組みを使いたがるプロジェクト管理というものを究極的に単純化し、数値ベースでの管理を実現した、という功績は非常に大きいです。初めてこの仕組みを知ったときには舌を巻きました。これ考えた人、マジで神だと思いますよ、ええ。

もう一つ、この仕組みが機能するための絶対必要条件は、各作業担当者から申告される残作業時間に、嘘偽りがないことです。当たり前ですが、実際には 3 時間で終わる作業を 10 時間と報告された瞬間に、このプロジェクト管理の仕組みは破綻します。しかし、もし各人が誠実に自分と向き合い、正しく作業時間を報告したとするのなら、自分のパフォーマンスを正確に知ることができますし、また自分のパフォーマンスが伸びたことも明確に数値に現れます。かつて 10 時間かかった作業が、スキルアップで 5 時間で終わるようになったら、エンジニアとしてはうれしいに決まってますよね^^。

しかし皆様もご承知の通り、普通、現場メンバーは馬鹿正直にかかっている工数を報告したりなんてしないのが普通です。なぜって、馬鹿正直に報告すれば、その分、上からの締め付けが厳しくなるだけだからです。

clip_image009

一般に、多重請負構造を基本としている日本の開発現場には、職種による階級制度が引かれていることがほとんどです。プロマネが一番偉くて、次に SE、そしてデベロッパー、テスターは最下層、なんていうことが一般的でしょう。しかしこうしたピラミッド型の組織構造、すなわちロール間に職能的上下関係が存在すると、モラルハザードが起こりやすくなり、上の人間は権力を振りかざし、下の人間は事実を隠ぺいするようになるのが普通です。これでは Scrum のプロジェクトマネジメントの仕組みがうまく機能するはずがありません。

こうした問題を踏まえ、Scrum(というかアジャイル)を実践する組織では、フラットな組織構造(チームモデル)とするのが基本です。職種の違いは役割の違いであるとして互いが互いを尊重し合い、悪いことも包み隠さず正直に言える環境がないと、現場から正しい数字は出てきません。このため PjM の人は、指示命令を与えることが仕事ではなく、むしろ現場の他のメンバーの人たちにいかに気持ちよく働いてもらい、いかにメンバーのやる気やモラルを最大限に引き出すかに注力することになります。

現場のメンバーの人たちに気持ちよく働いて最大限のパフォーマンスを発揮してもらえるように、リーダーの人は、メンバーの人たちの奴隷になって働く。それによってチームとしての結果を出す=リーダシップを発揮するわけです。この考え方をサーバントリーダシップと呼びます。

……もっとも、トップダウン型組織よりもフラット型組織の方が常に素晴らしいかというと、そういうわけではない、という点には注意してください。一般的に、トップダウン型組織は有事に強く、フラット型組織は平時に強い組織モデルです。要するに、指揮命令系統がはっきりしていると、戦争のような有事のときには軍隊のようにがちっと動けるけれども、逆に現代のような時代の場合には、末端の一人ひとりが自発的に考えて動いてもらわないと困るので、末端の人たちが考えて自由に動けるフラット型の組織モデルの方が強くなる、ということです。

また、日本の場合、給与モデルが職種によって階層化されていることが多い、という大きな制約事項もあります。簡単に言うと、優秀なデベロッパーよりも無能なプロマネの方がお金をもらっている、という話なのですが、これは欧米には当てはまりません。欧米では、優秀なテスターは新人のプロマネよりも高い給与をもらっています。組織モデルを考える際、給与モデルと切り離して考えることはできないことがほとんどです。こうした背景もあり、現時点の日本においては、トップダウン型組織構造とフラット型組織構造との間の折衷案を使うのが現実的なことが多いです。

ただ、最も重要なことは、仮にトップダウン型組織構造に近いモデルであったとしても、現場の他のメンバーの人たちにいかに気持ちよく働いてもらい、いかにメンバーのやる気やモラルを最大限に引き出すかが PjM の重要な役割になる、というポイントはきちんと押さえておく必要があります。

[近代的な『プロマネ』技術の必要性]

こうして考えると、現代のプロマネには、昔からあるような PMBOK 的なスキルセットはもちろんですが、それに加えて、計数ベースのプロジェクト管理技術や、モダンなサーバントリーダシップ的な考え方と対人スキルが求められると言えます。請負契約の契約境界の内側の方がアジャイルプラクティスは実践しやすいと思いますが、その際であっても、プロマネ技術の近代化が必要になってくると思います。


■ 請負契約をまたがる場合の考え方

先に述べたように、原理的に、請負契約をまたがる形でアジャイルプラクティスを実践していくのは非常に困難です。それは、発注側が「アジャイル」という手法に最も期待するのは「仕様変更をじゃんじゃん取り込んで、自分の望むものを完璧に作り上げてくれること」である半面、「請負契約」ではきちんと「範囲(仕様)」を決めて請け負う必要があるためです。仕様変更の管理と仕様変更に伴う追加請求が正しく機能しないと、原理的に、顧客が期待するものと発注方法とが矛盾してしまうからです。

clip_image010

ではどうすればよいのか? 上で述べた、請負契約とアジャイルが相反する理由を元に考えると、基本的な選択肢は以下のようなものになります。どれがよいのかはケースバイケースですが、いずれの方式も、エンプラ系企業では導入ハードルがかなり高いです。

  • エンド企業が、自ら内製に取り組む
    • 社内に開発リソースを持つか、あるいは請負契約ではなく準委任のような発注形態を取るようにして、自分たちで自ら開発の手綱を握る、という方法。
    • よく言われる方法である半面、現実的にはエンプラ系だとそのままでは取り組みが難しい方法。
      • 例えばオンラインゲームのように、自らがサービス提供者であり、かつ高速な開発とインクリメンタルなサービス増強を続けなければ生き残れない場合には、必然的に内製化や DevOps が進むが…
      • 多くのエンプラ系企業では、そもそもシステム開発そのものは戦略事業ではなく、システムを作ること自体は SIer または SI 子会社にアウトソースしたいというのが普通であるため。
    • やるとしても、コンシューマ系のサービスや小規模システムなど、取り組みやすいものから始めるのが現実的。
  • SI 企業側が請負型カスタム SI をやめ、SaaS 型でのサービス提供を行う
    • 請負型カスタム SI は、お客様の要求に振り回されることが多い。このため逆転の発想で、SaaS 型サービスにすることによって、開発のコントロールを自社側に持ってきてしまう、という方法。
    • カスタマイズ型パッケージ SI 製品の場合には実際に採用可能な戦略で、過去いくつかのお客様でこの手の戦略を聞いたことがあります。
  • エンド企業と SIer との間で、変更管理をきちんとやる
    • アジャイル型でシステムを段階的に構築していく場合であっても、そこに対する仕様変更についてはきっちりとお金をいただく、ということを最初にきちんとネゴる方式。
    • 理屈上は契約書で縛れるはずだが、現実的にはエンド企業と SI 企業との間の力関係によっては全く機能しないモデル。
    • さらに、実際にこれが成立するためには、エンド企業と SI 企業とが対等な関係であることはもちろんだが、それに加えて小規模ロットの見積もりがきちんと素早く精緻に行えること、そしてその見積もりに対して速やかに逐次で契約処理できるようになっていることが必要。これもまた実際にはハードルが高いです。

もしこの記事を読んでいる皆様が意思決定者またはそれに近いところにいる方であるのなら、上記のようなことを考えて部分的にでも実践するのは非常によいことだと思います。一方、皆様が現場担当者だとすると、これらを解決するのはかなりハードルが高いです……というよりほとんど無理;。うっかりすると、転職するのが最適解かもしれません。(※ 別に転職を薦めているわけではないです、念のため;。)

ただ、最初の議論に戻りますが、そもそも我々にとってアジャイルは手段であって目的ではない、ということは理解する必要があります。アジャイルの導入目的や効果はいくつもありますが、同じ目的や効果を狙う手法はアジャイルだけとは限りません。例えば…

  • 仕様変更を高速に取り込んですぐにリリースできるようにする。(オンラインゲームみたいなケース)
    • この場合は、そもそも開発リソースを社内に抱えて、内製化を強力に推進すべきです。
    • ……というよりこのケースは、すでに内製化を進めて DevOps とかやってるケースがほとんどでしょう。
  • よりよいサービスやよりよい業務アプリを、より安価に作りたい。
    • 業務アプリの品質の良し悪しは、業務設計の良し悪しによって大きく左右される。なので、要件定義フェーズ+プロトタイプ開発のみ切り出して、準委任+アジャイルでやればよい。要件が早期の段階できっちり煮詰まれば、後ろのフェーズの手戻りも必然的に減るので、構築をウォータフォールでやっても開発リスクなどは下がる。
    • 「安価に作る」ことに関しては、開発プロセスの無駄を取り除くことでもまだまだ実現できる余地がある。典型的には、発注側が要求している無駄なドキュメント納品物を割愛するなど。(ただし発注側と請負側の両者が Win-Win にならないとうまくいかないので、実際には何らか一工夫が必要)

他にもいろいろあると思いますが、ただ、アジャイルを導入したいという前に、そもそも何を目指したいのか? をはっきりさせましょう。日本はソフトウェア開発の後進国で、欧米からは 5~8 年ぐらい遅れているわけですが、もしそうだとするのなら、型落ちの改善手法でも十二分に改善できる可能性はあるのです。欧米の最新手法がそのまま導入できればそれに越したことはないかもしれませんが、Fit & Gap が大きすぎるから今のままでもいいや、となることの方がよほど大きな問題です。少しずつでもよいから改善していくこと、これを常に念頭に置いていただければと思います。いやー、実際の開発現場なんて、そんなすぐに改善できませんって、ホントに;。


■ でもって、どこから始めるの?

というわけでいろいろ書いてきましたが、上の方に書いた、「システム開発を請け負う側の内部に閉じてアジャイルプラクティスを使う」ことや、「ウォータフォール型による全体管理と、ミクロスコピックな Scrum スプリントプラクティスを組み合わせる」ことは、エンプラ系開発現場でも割と取り組みやすい手法だと思います。使えそうであれば、ぜひ使ってみてください。

 


■ 『変わらない開発現場』を嘆く皆様へ

なんかいろいろ書きたいことがたくさんありすぎて、ぼろぼろと重要なことを書き漏らしている気はするのですが;、その辺は皆様からのコメントで補完していただくことにして、最後につぶやきを少しばかり。

別に私はアジャイラーでもなければアジャイル信奉者でもなんでもない(使えるのであれば使うというスタンス)のですが、アジャイルの考え方の中に、私が非常に大好きなところがあります。それは、「人」を信じて、「人」の能力を最大限に引き出し、そして育んでいこうとするところ。アジャイルは基本的に性善説に立っており、「その人の善なる部分をいかに引き出すか?」「いかに人を成長させていくのか?」というところにものすごく腐心している、と思うのです。

翻って、日本の開発現場ってどうでしょうか?

例えば「開発標準」や「フレームワーク」といった言葉。もちろん今でも重要だとは思うのですが、最近、日本でこの言葉を聞く場合、そこにはこんな話がついてきます。「スキルの低い人であっても、一定の品質のアプリになるようにするものです」と。これ、今どきの開発の考え方として、本当に正しいんでしょうか?

スキルが低い人に、「何も考えなくてもモノが作れるツール」なんてものを与えたら、その人はますますモノを考えなくなる。それは単なる『生殺し』に他なりません。そしてモノを考えない人を増やし続ければ、結果的にはその人たちを潰してしまうばかりでなく、巡り巡って自分たちに跳ね返ってきます。確かにすべてのエンジニアのスキルが高いとは思いませんし、すべてのエンジニアのモチベーションがめちゃめちゃ高いとも思いません。残念ながらそれは事実でしょう。だけれども、大半の下請けエンジニアのスキルはおしなべて低い、という「前提条件」を常に引き、下請けエンジニアを「人手」とみなす、旧態依然とした考え方が横行していることにも問題がある、と思うのです。

たとえそこに受発注、あるいは職種という上下関係が存在していたとしても、同じゴールを目指す仲間として、互いを尊重し、高め合うような人間関係に育まれた開発現場がもっと増えてほしいと切に願いますし、そのためのアイディアや情報が世の中にもっと増えてほしい、と思います。(エバと違ってコンサルは情報発信が本業ではないので、なかなか blog が書けないのは恐縮なのですが;。ごめんなさい;。)

そういう意味で最初の話に戻りますが、変わりたいと思っている、けれどもどうすればいいのよ? と悩んで具体的な手法やヒントを欲している人や開発現場はきっと多いはず。そして多くのエンプラ系開発現場(そして人生)は、大量のしがらみと、ほんの少しの、物事を変える勇気からできている、と私は思います。このエントリであれこれ書いた話が、『変わらない開発現場』を嘆く皆様が物事をどう変えていくのかを考える際のヒントになれば、そして実際に一歩目を踏み出す手助けに(少しでも)なれば幸いです。

 

『変わらない開発現場』を変えていくために。

$
0
0

前回のエントリですが、はてブや Twitter でびっくりするほどたくさんのコメントをいただきました。当該ページの PV も 2 万超え、そして 1,000 件を超えるはてブは初めてで、勉強になるコメントも多く、なるほどと頷かされるご指摘が多かったです。この場を借りてお礼申し上げます。ありがとうございました。(はてブでエールを送っていただいた皆様もありがとうございました。励みになります。m(_ _)m)

コメントを読んでいると、やはりものすごく現場で苦しまれている方が多いと改めて感じると共に、年次や役職で抱える悩みはずいぶん違う、という印象も受けました。ただ、いずれの悩みにも共通するのは、

「意思決定者の方々になかなか理解してもらえない・動かせない」
(意思決定者=自分のマネージャ、お客様など)

という点だと思いました。実際問題として、意思決定権者にしか変えられないこと・決められないことについて、正論をタテに現場担当者に解決を詰め寄るのはただの無理ゲーで、これだと、あんまり建設的な議論にはならないのですよね;。この件に関しては様々な宗教論争、もとい議論を見てきましたが、全体的にビジネスの仕組み的な観点からの考察が不足していることや適切な問題分解・整理がなされていないこと、さらに多彩な開発現場を十把一絡げに議論しようとしていることが、建設的な議論を妨げている、と感じました。

この辺の解決方法は、結局、現場や人ごとに大きく変わるので、万能な処方箋は存在しない(個々人で自分の問題としてじっくり考えるしかない)のですが、コンサル的な視点から、考え方に関して多少のヒントは書けるかも? と思って、補足エントリとして書いてみることにしました。蛇足ではありますが、何かのヒントになれば幸いです。

今日のエントリで書きたいポイントは 3 つです。タイトルだけでだいたい内容の想像はつくと思いますので、ピンと来た方は特に読まなくてもよいと思います、はい^^。

  • 問題分解の必要性:責任境界線を明らかにする
  • 変わらない意思決定者への「伝え方」のヒント
  • 鶏と卵、先に変わるべきはどちらか?:プロフェッショナルとして

■ 問題分解の必要性:責任境界線を明らかにする

前述したように、SI 開発現場に関わる様々な問題には、自分でも変えられることと、意思決定者にしか変えられないことが存在します。(下記の例はとりあえずのケーススタディとして書きましたが、それぞれの開発現場、そしてご自身の役職やロール次第で、話は大きく変わります。なので、ちょっと時間を取って書き出してみるとよいかと思います)

  • 例 1. 「請負契約」を前提条件として、大きな粒度のアジャイルを可能にするために必要なことは?
    • お客様(意思決定者)に求められること
      • 要件定義への積極的な参画
      • 素早い契約締結
      • 柔軟な予算確保と執行 (※ アジャイルの大きな阻害要因のひとつは、日本でよくある予算確保と予算執行の硬直性です。)
    • SIer (現場担当者)に求められること
      • 適切なベースライン定義と変更管理
      • 精度の高い見積もりを素早く出せること
      • 高速な開発(設計・実装・ビルド・テスト)
    • 両者に求められること
      • 誠実な信頼関係とプロフェッショナリズム
  • 例 2. 請負契約の境界を超えない範囲で、小さな粒度のアジャイルを回していくために必要なことは?
    • SIer のプロパー(意思決定者)に求められること
      • Scrum などのアジャイル方法論の導入
      • 見積もり手法と実績管理手法の変更(プラニングポーカーとかタスクボードとか)
      • チーム運営方法の変更
      • 係数ベースのプロジェクト管理手法の導入
      • フラットで風通しのよい現場環境の醸成
      • 品質管理指標やプロセスの変更(または二重管理) (※ 全社的な品質管理基準が今どきの開発スタイルに合っておらず、そこがネックになって新しい手法などが導入しにくくなっているケースがあります。特に大きな SIer にありがちなパターンです。)
      • 現場への超優秀なエンジニアの投入 (※ Scrum のような開発プロセスでは、一時的であっても構わないので超優秀なエンジニアを投入し、その人のノウハウを周囲に伝搬させると、開発現場のレベルを一気に引き上げやすいです。)
    • 協力会社の担当エンジニア(現場担当者)に求められること
      • ALM ソリューションの導入(できれば TFS/VSTS 使ってね!(ぉ))
      • 日々の作業におけるチケット駆動開発の利用
      • 高速開発に必要な高い技術スキルの習得
      • CI/CD 環境の構築
      • 社外とのコミュニケーションによるノウハウ吸収(例:コミュニティ勉強会への参加など)
    • 両者に求められること
      • 誠実な信頼関係とプロフェッショナリズム

自分たちに関わることは自分たちの努力で解決できる問題です。しかし多くの人が悩んでいるのはそこではなく、相対する意思決定者にしか変えられないことでしょう。ここに対する具体的なソリューションがないのに何とかしろと言われると、「どないせいっちゅうねん;」と思ってしまいますし、仮に具体的なソリューションが書かれていたとしても、自分のシチュエーションに当てはめられないと、「そんな無茶いうな!」となります;。

先に書いたように、開発現場、さらにそこに関わる皆様の役職やロールも十人十色です。なので、残念ながら、皆様それぞれが抱える個々の問題に対する最適解はご自身で考えていただくしかないです;。確かにコンサルタントとかに頼めば、問題分解してくれて、最適解を教えてくれるかもしれませんが、その場合であっても、結果的にそれを実行するのは皆様ご自身です。変わらない意思決定者の方々を変えていくことすらも、自分自身の問題の一部として解決を目指すことが大切だと思います。


■ 変わらない意思決定者への「伝え方」のヒント

一般に、「他人を変えることはできない」と言われます。確かに、他人を自分の思うように変える、ということは不可能です。けれども、その人が変わりたい・変えたいと思っているときに、その人のサポートをすることはできます。

なぜそんな当たり前のことを書くのかというと、実際に意思決定者となる人と会話してみると、問題意識は持っているが上手い変え方がわからないとか、また単に知らないから変える必要性を感じていない、といったことも少なくないからです。例えばアジャイルを使った現場改善について考えてみると、その理解度は意思決定者ごとにまちまちです。

  • そもそも知らない。(知らないのでやらない or 知ってもやることに対して懐疑的になる。)
  • 中途半端に知っている。(懐疑的な人だと「やらない理由」で理論武装していることもある。)
  • かつてやってみて失敗した。(あつものに懲りてなますを吹くパターン。割と頭が固くなる。)
  • 自分は絶対的に正しいと思い込んでいる。(かなり困った頑固タイプ。)

こんなふうに分類した場合、意外と「そもそも知らないだけ」あるいは「中途半端に知っている」というパターンも多いです。実際、SIer のマネージャクラスの方々を招いて前回のエントリのような話をしつつ、VSTS のデモをすると、「知らなかった」「ぜひ使ってみたい」という F/B になることが結構あります。まず、食わず嫌いなのかどうなのか、またなぜそう思っているのかを確認することが大切です。

それがわかったら、相手の知識レベルに併せて説明を打ち込んでいくのですが、このとき、いくつか重要なポイントがあります。私が思う特に重要なポイントは以下の 3 つなのですが、IT エンジニアは(私も含めて)これらがかなり苦手だと思います……orz

  • 相手のロジックに併せて説明する(相手の土俵で語る)
  • 端的かつロジカルに、メリットをズバリ説明する
  • 第三者からの援護射撃をうまく使う

[相手のロジックに併せて説明する(相手の土俵で語る)]

特にその必要性を感じていない相手に、「1 日 10 回 deploy するのが重要です!」とか説明しても全くスルーされるのがオチですが、その最大の理由は、自分の土俵から、自分の目線と自分の価値観で物事を語ってしまうことです。これをやってしまうと嫌われるのはもちろんのこと、うっかりすると二度と話を聞いてもらえなくなります;。一般的に、優秀なエンジニアは技術に対して深いを持っていることが多く、それゆえに愛が暴走してついテクノロジ目線でモノを語ってしまうことが多いのですが、意思決定者は物事をビジネス目線で見ていることがほとんどです。なので、まず主語を意思決定者にして、相手の目線でメリットを訴求するのが基本中の基本です。(← というか、昔、これに関してさんざん怒られました、私;。今でもなかなか難しいのですが。)

例えば大きな粒度のアジャイルに関してであれば、

  • × アジャイルでやると、仕様変更を頻繁に取り込むことができるようになります。
  • ○ フルオーダーメイド方式でありながら、「どんなものが欲しいのか」が曖昧にしかわからない状態で最初から契約を切って請負発注すれば、そりゃバッファ積みまくりで高くつくでしょうし、逆に先に請負契約で金額が確定しちゃってたら、できる限りやらずにラクしてお金稼ごうとするに決まってますよね? これってお互いにとってかなり不健全ですよね?

あるいは小さな粒度のアジャイルであれば、

  • × Scrum では、スプリントという固定の長さの期間を区切り、スプリント初日にタスクの作業時間を見積もります。一日一回、残作業時間を足し合わせてグラフ化し、バーンダウンチャートを作ります。
  • ○ Scrum のスプリントプラクティスは、見積もりとその振り返りを一定期間ごとに繰り返させるというもので、これを使うと現場担当者のボトムアップの作業見積もりスキルを向上させることができます。

といった感じ。小難しい専門用語を一切使わず、一般人でもわかる言葉で、お客様目線でのメリットを訴求するのが最初のポイントです。

[端的かつロジカルに、メリットをズバリ説明する]

次に重要なのが、メリットをロジカルかつ端的にズバリ説明する、という点。説明がやたら長いお前が言うなというツッコミは甘んじて受けますが(なので私も苦手;)、上の Scrum の説明なんかが恒例で、端的に何がよいのかをスパーンと説明できないと、意思決定者には伝わりません。

IT エンジニアの悪いクセは、原理主義・経典主義に陥りやすいことです。多くの場合、意思決定者にとっては、例えばアジャイルとかオブジェクト指向の定義なんて激しくどうでもよいのです。にもかかわらず、学者肌の人ほど、自分と同じレベルの知識と理解を他者に求めようとする傾向があります。Facebook で先日のエントリの話をした際に、会社の同僚の一人がこう言いました。

「現場に近いところにいる人間からいうと、どれだけの機能が何時、いくらでできるの?という顧客の問いかけに回答できれば手法は問いません」

いやホントこれ。名前なんてホントどうでもいいですから。……といいつつ、私も経典主義の傾向はあるので;、ちょっと意識的に注意してます。ここは自戒を込めて;。

[第三者からの援護射撃をうまく使う]

そして最後に、第三者からの援護射撃をうまく使うこと。これは、当の本人が何を言ってもポジショントークか愚痴だとみなされる危険性が高いという話で、第三者の調査や事例などをうまく使ったり、あるいは第三者を連れて来て同じ話をしてもらう、というのが非常に有効です。

ただしこの際、引っ張ってくる事例の開発規模や特性などが自分のシステム開発に似ているということが非常に重要です。カスタム SI の開発現場に対して、マイクロソフトの製品開発の事例を引っ張ってきても、「参考になる部分がないとは言わないけど、うちとは前提条件が違うから合わないよね」と言われるのがオチです。

違う世界の話と思わせないためにも、例えば以下のようなポイントはぜひ考えてみてください。(また、Web 上の様々な議論を見るときにも、どの世界の話をしているのかを意識することは重要です。話がどうにも腑に落ちない・かみ合わない、という場合は、話しているシステム開発の世界が違う可能性が高いです。)

  • 開発規模:数千万円 vs 数億円 vs 数十億円 vs 数百億円
  • 業務特性:随時更新業務 vs 塩漬け業務
  • アプリ特性:低難易度×多数画面 vs 高難易度×少数画面
  • 重視ポイント:安定性 vs 先進性
  • 開発形態:内製 vs 外注
  • 契約形態:請負 vs 準委任
  • 開発チームの技術的熟練度:高 vs 低
  • 開発チームの成熟度:自律型 vs 指揮命令型

ちなみに、実際にアジャイルや DevOps などを導入しようとした場合、参考になる類似事例がないことも多いです。そういう場合には、一気にやらずに、ちょっとずつ試験的に導入してみるとか、真似できるところから少しずつやってみるとか、リスクのないシステム(研究開発プロジェクトとか)で試験的にやってみた方がよいです。慣れない開発方法論を振り回すと、開発のリスクも当然上がりますし、痛い思いをすると二度とやりたくなくなるリスクもあります。この辺はうまくバランスすることが大切です。


■ 鶏と卵、先に変わるべきはどちらか?:プロフェッショナルとして

このエントリの最初に、自分でも変えられることと、意思決定者にしか変えられないことに問題を切り分けましょう、という話を書きましたが、これに関するもうひとつ大きな問題として、先に変わるべきはどちらなのか? という点があります。

例えば、請負契約の境界を超えない範囲で、小さな粒度のアジャイルを回していくために必要なことは何か? という話題を例にとってみると、「Scrum を採用する」と意思決定者が決めることが先なのか、それとも現場側の人間が「Scrum を回していくために必要な高度なスキルセットを備える」ことが先なのか、という問題です。この二つは鶏と卵の関係を持っていることが多く、意思決定者が判断を下せば現場が変わる、けれども変わっていない現場を前提に意思決定者が判断を下すのは難しい、という側面があり、どちらが先に一歩を踏み出すべきか? という問題があります。(意思決定者がサーバントリーダシップの場合にはここは問題にならないのですが、多くの日本の現場はそうではないでしょうから、先に変わるべきはどちらなのか? という点が問題になります。)

この点に関しては、それぞれが同時に変わることを目指すべき……ではあるのですが、少なくとも現場担当者の目線で見た場合(=自分たちの問題として考えた場合)には、自分たちが先に変わっていないのに他人に変わることや理解を求めることは間違っている、と思います。

  • 他人に何かを求める前に、そもそも自分は他者の模範となり得ているのか?
  • 意思決定者に話を聞いてもらえるだけの「価値」を自分は提供できているか?

といった点を、厳しく自省することも必要なのではないかと思います。そしてそのためには、

  • プロフェッショナルとして、自分たちの価値をちゃんと図る
  • そしてその価値を伸ばしていく

ことが必要です。会社という看板が外れたときに、自分は人月何万円の価値のある仕事ができるのか? を自問し、それを高めるためには何を伸ばしていけばよいのか? を常に自問することはとても重要だと思います。

本来、意思決定者と担当者の間は、理想を言えば、互いにプロフェッショナルとしての緊張感のある関係であることが望ましいです。けれども実際にはそうなっておらず、意思決定者側にパワーバランスが偏っていることが多い。でもこのパワーバランスを生じさせているのは、単に上下関係や発注関係があるからというだけではなく、担当者側の力量不足という側面も一部にはある、と思うのです。そう話は簡単ではないことは私もわかっているのですが;、でもそういったところを目指して、意思決定者の方ばかりではなく、たまには市場の方も見て、自分/自分たちの価値を考えてそれを磨いていくことは、ものすごく大切なことだと思います。


■ 最後に:変わらない開発現場を嘆く皆様へ。

最後にポエムを少しばかり。ここまであれこれ書いてきたのですが、こう思われた方も多いと思います。

「開発現場を変えるのって、ちょーめんどくね?」

……はい、ぶっちゃけ私もそう思います。実際、この話は全くもって他人事ではなく、私自身もまさに今現在、似たような課題で激しく悩んで困っていたりするのが実情です;(なので私が解決策みたいな話をすること自体、極めて説得力に欠けることは自覚してますし、書いててグサグサと自分自身に刺さるという;。っつーかマジ変わらないし変えられなくてつらい……orz;)。

でも、こういうのもなんですが、それが現実でありリアルなんですよね;。正直、無理ゲーだと思うことは多いですし、すべて投げ出して別の素敵な職場に移った方が圧倒的にラクなケースも多いと思います。けれども多分、この話で悩んでいる方の多くは、それができないから悩んでいる。自分のことだけ考えれば転職した方が圧倒的にラク、けれどもメンバーや同僚を放り出せるかといえばそんなこともできないし、共に歩んできた家族やお世話になってきた会社、そして日本に対して背負うものや帰属意識もある。やっぱり今、自分が属しているこの現場をなんとかしたい、と思って悩んでいる人の方が、私は圧倒的多数なんじゃないかと思うのです。

となると、もうどんなに大変でも覚悟を決めて頑張るしかないのですよね;。言葉にすると陳腐でしかないのですが、

  • プロフェッショナリズム
    • お互いがやるべき役割、果たすべきミッションをきちんと線引きした上で頑張る
  • 相互理解
    • お互いの置かれている立場や気持ちを理解してコミュニケーションを取り、互いに歩み寄る

の 2 つがやっぱり大切で、これを現場担当者と意思決定者の目線に分けて考えてみると、

  • 現場担当者としての目線
    • 本来、意思決定者と自分の関係は上下関係ではなく、仕事という場におけるロール(立場)の違いであるはずです。
    • もし今現在、それが上下関係であるのなら、それを対等な(あるいは健全な)関係にするために、自分に何ができるのかを考えて行動に移していくこと、そしてそれを以て意思決定者に適切に働きかけていくことが大切なのだと私は思います。
  • 意思決定者としての目線
    • 今のやり方(発注方法とか仕事の進め方とか)が完璧だと思っている人など、多分どこにもいないと思います。問題意識を持ちながらも、なかなか解が見つけられずに困っているのが実態ではないでしょうか?
    • もしそうなら、その問題を一緒に考えながら解決していける、プロフェッショナルなスキルを持った会社や担当者を探して相談するのがよいと思います。実際、そういった人たちは上手いやり方を知っていることがよくあるもので、その力を上手く引き出すこともまた、意思決定者としての役割です。自分たちにとっての最適解は、相対する会社や担当者とともに作り上げていくしかないものだと思います。

といった具合に、それぞれの立場毎に問題を分解して『自分のコト』として捉えることが大切、だと思うのです。

正直、道は長いですし、どんなに頑張っても完全に解決できるものではないかもしれませんが、とはいえあきらめたらそこで試合終了です。現場担当者と意思決定者とが、互いにプロフェッショナルとして仕事に関与し、一方で飲み会ではつらさを語り合えるような関係になれるところを目指して、きちんと問題分解し、まずは自分にできることから一歩ずつ改善を進めてゴールに近付くことが大切……というよりそれ以外他にないんじゃないかな、と思います。このエントリが、改善を目指す皆様にとって、多少でも何かのヒントになれば幸いです。

# ……つらくなったら、みんなで語り合いたいです、ええ;。
# そしてもっと上手い方法やテクニックがあったら教えて欲しいです。いやマジで;;。

というわけでここ数回、「ゆるっとした」話をつらつらと書きましたが、こういうゆるふわ系エントリはホントに書くのが難しいです;。最後まで読まれて気分を害された方もきっといらっしゃると思いますが、その場合はすみません & ごめんなさい;。個人的には、かちっと白黒つきやすい話の方が気持ち的にもラクなので、次回は今まさに旬の、ASP.NET Core 1.0 な話をしたいと思います、はい^^。

Part 1. ASP.NET Core 1.0 ことはじめ

$
0
0

まずは基本的な ASP.NET Core 1.0 のアプリの作り方から開始していきます。

■ ソリューションファイルとプロジェクトファイルの作成

Visual Studio を開き、新しいプロジェクトを作成します。

image

今回、Application Insights は利用しないのでチェックを外しておきます。名前は “Decode2016.WebApp” にしておきます。

image

テンプレートは「空」を選びましょう。認証は今回はなし、Microsoft Azure へのホストもなしにしておきます。作成すると、以下のようなプロジェクトファイルが出来上がります。

image

従来の ASP.NET から大きく変わった点として、以下 2 つに注意してください。

  • wwwroot と書かれたフォルダは、公開する静的コンテンツ(HTML ファイルや JPEG ファイルなど)を置く場所です。従来と異なり、アプリケーションコードはここには置かず、上位のフォルダに置きます。
  • ソースコードフォルダ内のファイルは、基本的にすべてプロジェクトに所属するものとして扱われます。今回の場合だと、C:\Users\nakama\Documents\Visual Studio 2015\Projects\Decode2016.WebApp\src\Decode2016.WebApp にプロジェクトフォルダが作成されていますが、ここにファイルを配置すると、そのファイルは自動的にプロジェクトのメンバであるとみなされるようになっています。(従来の *.csproj のような管理ファイルは存在しません)

プロジェクトファイル内には以下の 2 つのコードがあります。それぞれざっくりとした解説は以下の通りです。

  • Program.cs ファイル
    • Main() 関数が書かれており、Web サーバを起動するためのコードが書かれています。
  • Startup.cs ファイル
    • ASP.NET ランタイムの初期化コードが書かれています。

では、早速 Ctrl + F5 で実行してみてください。Hello World というメッセージが表示されます。

image

このような実行結果になったのは、Startup.cs ファイル内にある “context.Response.WriteAsync(“Hello World!”);” 命令により、Hello World メッセージがブラウザに送り返されたからです。ちなみに詳細はまた別エントリで解説しますが、この Startup.cs ファイルの ConfigureServices() メソッドは、ASP.NET ランタイムの DI コンテナの初期化処理を行うためのメソッド、Configure() メソッドは、ASP.NET ランタイムのパイプラインを構成するためのメソッドです。(← 意味がわからなければとりあえず「ふーん」で OK)


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

■ ライブラリの追加

では、まず基本的なライブラリをこのプロジェクトに組み込みます。参照設定は GUI からは行えませんので、project.json ファイルを開き、以下 5 つを追加します。

Microsoft.AspNetCore.Mvc ASP.NET Core MVC 1.0 本体
Microsoft.AspNetCore.Mvc.TagHelpers View ページの作成でタグヘルパーを使えるようにする
Microsoft.AspNetCore.StaticFiles 静的ファイルを返せるようにするライブラリ
Microsoft.EntityFrameworkCore データアクセスライブラリ Entity Framework Core 1.0
Microsoft.EntityFrameworkCore.SqlServer Entity Framework Core の SQL Server 接続ドライバ

 


{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",

    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0",
    "Microsoft.EntityFrameworkCore": "1.0.0",
    "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0"
  },
(以下略)...

実際の入力時は IntelliSense が効きますのでうまく活用してください。また、各ライブラリについては相互に依存関係がありますので、必ず動作確認されているバージョンの組み合わせで利用してください。今回利用しているライブラリの場合はすべて 1.0.0 にそろえれば OK ですが、モノによっては動作確認が取れているバージョンの組み合わせが違うケースも当然ありますので注意しましょう。

Part 2. Entity Framework Core 1.0 の基本的な使い方

$
0
0

では引き続き、Entity Framework Core の利用方法を解説していきます。

■ 基本的な Entity Framework Core の利用方法

従来の Entity Framework と異なり、EF Core では O/R マッパーファイル(*.dbml)を使うことができません。このため、O/R マッピング(データベーステーブルのどこを構造体クラスのどこにマッピングするのか?)はすべてコードで指定する必要があります。ツールを利用して自動生成させることも(ある程度は)可能ですが、現時点(2016/07/02)では、手で書いてしまったほうがやりやすいと思います。

まずは以下 2 つのファイルを用意します。

  • データベースファイル
    • プロジェクトファイル直下に App_Data フォルダを掘り、pubs.mdf ファイルを置きます。
    • データベースファイルは公開する必要がないため、wwwroot 下に置く必要はありません。私は昔の名残で App_Data という名前を使っていますが、この名前である必要性も特にありません。お好みで変えていただいて結構です。
  • モデルファイル
    • プロジェクトファイル直下に Models フォルダを切り、Pubs.cs ファイルを作成します。

image_thumb[13]

Pubs.cs ファイルに、データを取り出すための構造体クラスを記述し、そこに O/R マッピング情報を記述していきます。今回は、全テーブルをやるとキリがないので、以下 6 つのテーブルについてだけ取り出してマッピングしてみます。

  • authors : 著者データ
  • titles : 書籍データ
  • titleauthor : 著者と書籍の多対多中間テーブル
  • publishers : 出版社データ
  • stores : 店舗データ
  • sales : 売上データ

image

最終的なコードは以下の通りです(※ OnConfiguring() メソッド内のパス情報は適宜書き換えてください)。えらい長いコードで恐縮ですが;、順番に説明していきます。


using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Decode2016.WebApp.Models
{
    public partial class PubsEntities : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(
                @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
                .Replace("|DataDirectory|", @"C:\Users\nakama\Documents\Visual Studio 2015\Projects\Decode2016.WebApp\src\Decode2016.WebApp\App_Data"));
        }

        public DbSet<Author> Authors { get; set; }
        public DbSet<Title> Titles { get; set; }
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Store> Stores { get; set; }
        public DbSet<Sale> Sales { get; set; }
        public DbSet<TitleAuthor> TitleAuthors { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Sale>().HasKey(s => new { s.StoreId, s.OrderNumber, s.TitleId });
            modelBuilder.Entity<TitleAuthor>().HasKey(ta => new { ta.AuthorId, ta.TitleId });
            modelBuilder.Entity<Sale>().HasOne(s => s.Title).WithMany(t => t.Sales).IsRequired();
            modelBuilder.Entity<Sale>().HasOne(s => s.Store).WithMany(s => s.Sales).IsRequired();
            modelBuilder.Entity<Publisher>().HasMany(p => p.Titles).WithOne(t => t.Publisher).IsRequired();
            modelBuilder.Entity<Author>().HasMany(a => a.TitleAuthors).WithOne(ta => ta.Author).IsRequired();
            modelBuilder.Entity<Title>().HasMany(t => t.TitleAuthors).WithOne(ta => ta.Title).IsRequired();
        }
    }

    [Table("authors")]
    public partial class Author
    {
        [Column("au_id"), Required, MaxLength(11), Key]
        public string AuthorId { get; set; }

        [Column("au_fname"), Required, MaxLength(20)]
        public string AuthorFirstName { get; set; }

        [Column("au_lname"), Required, MaxLength(40)]
        public string AuthorLastName { get; set; }

        [Column("phone"), Required, MaxLength(12)]
        public string Phone { get; set; }

        [Column("address"), MaxLength(40)]
        public string Address { get; set; }

        [Column("city"), MaxLength(20)]
        public string City { get; set; }

        [Column("state"), MaxLength(2)]
        public string State { get; set; }

        [Column("zip"), MaxLength(5)]
        public string Zip { get; set; }

        [Column("contract"), Required]
        public bool Contract { get; set; }

        [Column("rowversion"), Timestamp, ConcurrencyCheck]
        public byte[] RowVersion { get; set; }

        public ICollection<TitleAuthor> TitleAuthors { get; set; }

    }

    [Table("publishers")]
    public partial class Publisher
    {
        [Column("pub_id"), Required, MaxLength(4)]
        public string PublisherId { get; set; }

        [Column("pub_name"), MaxLength(40)]
        public string PublisherName { get; set; }

        [Column("city"), MaxLength(20)]
        public string City { get; set; }

        [Column("state"), MaxLength(2)]
        public string State { get; set; }

        [Column("country"), MaxLength(30)]
        public string Country { get; set; }

        public ICollection<Title> Titles { get; set; }
    }

    [Table("titles")]
    public partial class Title
    {
        [Column("title_id"), Required, MaxLength(6), Key]
        public string TitleId { get; set; }

        [Column("title"), Required, MaxLength(80)]
        public string TitleName { get; set; }

        [Column("type"), Required, MaxLength(12)]
        public string Type { get; set; }

        [Column("price")]
        public decimal? Price { get; set; }

        [Column("advance")]
        public decimal? Advance { get; set; }

        [Column("royalty")]
        public int? Royalty { get; set; }

        [Column("ytd_sales")]
        public int? YeatToDateSales { get; set; }

        [Column("notes"), MaxLength(200)]
        public string Notes { get; set; }

        [Column("pubdate"), Required]
        public DateTime PublishedDate { get; set; }

        [Column("pub_id"), MaxLength(4)]
        public string PublisherId { get; set; }

        public Publisher Publisher { get; set; }

        public ICollection<Sale> Sales { get; set; }

        public ICollection<TitleAuthor> TitleAuthors { get; set; }
    }

    [Table("sales")]
    public partial class Sale
    {
        // ※ 複合キーは Data Annotation で指定できないため、Fluent API を使う

        [Column("stor_id"), Required, MaxLength(4)]
        public string StoreId { get; set; }

        [Column("ord_num"), Required, MaxLength(20)]
        public string OrderNumber { get; set; }

        [Column("ord_date"), Required]
        public DateTime OrderDate { get; set; }

        [Column("qty"), Required]
        public int Quantity { get; set; }

        [Column("payterms"), Required, MaxLength(12)]
        public string PayTerms { get; set; }

        [Column("title_id"), Required, MaxLength(6)]
        public string TitleId { get; set; }

        public Store Store { get; set; }
        public Title Title { get; set; }
    }

    [Table("stores")]
    public partial class Store
    {
        [Column("stor_id"), Required, MaxLength(4), Key]
        public string StoreId { get; set; }

        [Column("stor_name"), Required, MaxLength(40)]
        public string StoreName { get; set; }

        [Column("stor_addr"), Required, MaxLength(40)]
        public string Address { get; set; }

        [Column("city"), Required, MaxLength(20)]
        public string City { get; set; }

        [Column("state"), Required, MaxLength(22)]
        public string State { get; set; }

        [Column("zip"), Required, MaxLength(5)]
        public string Zip { get; set; }

        public ICollection<Sale> Sales { get; set; }
    }

    [Table("titleauthor")]
    public partial class TitleAuthor
    {
        [Column("au_id"), Required]
        public string AuthorId { get; set; }

        [Column("title_id"), Required]
        public string TitleId { get; set; }

        [Column("au_ord")]
        public byte AuthorOrder { get; set; }

        [Column("royaltyper")]
        public int RoyaltyPercentage { get; set; }

        public Author Author { get; set; }

        public Title Title { get; set; }

    }
}

■ コードの構造について

上記のソースの各クラスの役割は以下の通りです。

  • PubsEntites
    • データベース接続を管理するクラス。DbContext クラスから派生させて作成する。
    • この接続下で取り扱うテーブル一覧もここに記述される。
    • 接続文字列の指定方法は複数あるが、基本的には OnConfiguring メソッド内で指定する。
      • EF Core では SQL Server 以外にも接続できるため、SQL Server に接続したい場合には、project.json ファイルで Microsoft.EntityFrameworkCore.SqlServer ライブラリを組み込んだ上で、options.UseSqlServer(…) として接続先を指定する。
  • Author, Publisher, Title, Sale, Store, TitleAuthor クラス
    • データベースの各テーブルに対応させて作成した構造体クラス。
    • データベース上の 1 レコードが、これらのオブジェクトの 1 インスタンスに読みだされる。
      • このため、データベース上のテーブル名は複数形、C# のクラス名は単数形になるのが一般的。
    • 属性(クラス定義やプロパティ定義の上につけられたカギカッコ)により、データベースとのマッピング方法を指定する。
      • ほとんどのマッピングは属性で指定できるが、複合キーやリレーションシップに関する情報は(現時点では)属性では指定できない。このような場合には、PubsEntities クラスの OnModelCreating() メソッド内でコードで O/R マッピング情報を指定する。
      • 属性を使って O/R マッピングを指定する方法をデータアノテーション方式、OnModelCreating() メソッド内でコードを使って O/R マッピングを指定する方法を Fluent API 方式と呼ぶ。

おおまかなコードの構造は以下の通りです。(リレーションシップの指定などに関しては少し重要なので、後述します。)


    public partial class PubsEntities : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            // ここに接続文字列を書く
        }

        // ここにテーブル一覧を書く
        public DbSet<Author> Authors { get; set; }
        public DbSet<Title> Titles { get; set; }
        public DbSet<Publisher> Publishers { get; set; }
        public DbSet<Store> Stores { get; set; }
        public DbSet<Sale> Sales { get; set; }
        public DbSet<TitleAuthor> TitleAuthors { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // ここにデータアノテーションで指定できない O/R マッピング情報を書く
        }
    }

    [Table("authors")]
    public partial class Author
    {
        [Column("au_id"), Required, MaxLength(11), Key]
        public string AuthorId { get; set; }
        [Column("au_fname"), Required, MaxLength(20)]
        public string AuthorFirstName { get; set; }
        ...
    }

    [Table("publishers")]
    public partial class Publisher
    {
        [Column("pub_id"), Required, MaxLength(4)]
        public string PublisherId { get; set; }
        ...
    }
    ...

■ リレーションシップの O/R マッピング方法について

リレーションシップについては、以下のように実装します。

  • 1 : 多について
    • 例えば、出版社(Publisher)と書籍(Title)は 1 : 多の関係になりますが、これは以下のように実装します。
    • Publisher クラス側
      • 1 つの Publisher に複数の Title が紐づけられるので、ICollection<Title> Titles プロパティを作成します。
    • Title クラス側
      • 1 つの Title には 1 つの Publisher が紐づくので、Publisher Publisher プロパティを作成します。
      • PublisherId プロパティは持っても持たなくても構いませんが、通常はあると便利なので持たせてしまいます。
    • 上記準備を済ませたうえで、リレーションシップに関する O/R マッピング情報を OnModelCreating() メソッドに記述します。

[Table("publishers")]
public partial class Publisher
{
    [Column("pub_id"), Required, MaxLength(4)]
    public string PublisherId { get; set; }
    [Column("pub_name"), MaxLength(40)]
    ...
    public ICollection<Title> Titles { get; set; }
}

[Table("titles")]
public partial class Title
{
    [Column("title_id"), Required, MaxLength(6), Key]
    public string TitleId { get; set; }
    [Column("title"), Required, MaxLength(80)]
    public string TitleName { get; set; }
    ...
    [Column("pub_id"), MaxLength(4)]
    public string PublisherId { get; set; }
    public Publisher Publisher { get; set; }
}

modelBuilder.Entity<Publisher>().HasMany(p => p.Titles).WithOne(t => t.Publisher).IsRequired();
  • 多 : 多について
    • 例えば、著者(Author)と書籍(Title)は、多 : 多テーブルである TitleAuthor テーブルを介して 多 : 多 の関係を持ちます。
    • このような多 : 多の関係については、代表的には以下の 2 つの方式での O/R マッピングが考えられます。
      • ① 直接、多 : 多の関係をオブジェクトで表現する
        • Author オブジェクトのプロパティとして ICollection<Title> Titles を、Title オブジェクトのプロパティとして ICollection<Author> Authors を持つが…
        • 中間テーブルである TitleAuthor に対応するオブジェクトは作らない
      • ② 中間テーブルまで含めてオブジェクトで表現する
        • 中間テーブルである TitleAuthor に対応するオブジェクトを明示的に作り、Author オブジェクトのプロパティとして ICollection<TitleAuthor> TitleAuthors を、Title オブジェクトのプロパティとして ICollection<TitleAuthor> TitleAuthors を持たせる
        • すなわち、2 組の 1 : 多 の関係であるとして表現してしまう
    • どちらにもメリット・デメリットがありますが、一般的には②方式の方が幅広く利用できます。
      • 今回のサンプルのように、著者(Author)と書籍(Title)の間の TitleAuthor に印税料(Royalty)などの属性がついている場合には、明確にオブジェクトとして作成する必要があります。
    • ①の方式の場合には、単に 1 : 多の関係を 2 つ作成するだけになります。

■ LINQ クエリの記述

O/R マッピングファイルができたら、あとは LINQ クエリを実行します。今回は簡単のため、Startup.cs の app.Run() 内を書き換えて実行してみましょう。


app.Run(async (context) =>
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = from a in pubs.Authors where a.State == "CA" select a;
        await context.Response.WriteAsync(query.Count().ToString());
    }
});

正しく動作すれば、15 件という結果が帰ってくるはずです。

image

なお、EF で必要となる LINQ クエリの記述方法についてはここでは説明しませんが、LINQ クエリは EF を扱う上での必須技術であるため、まだ知らないという方は必ず学習してください。拙著「LINQ テクノロジ入門」は EF の前進となる LINQ to SQL という技術を使って書かれていますが、LINQ クエリの書き方そのものは基本的に変わりません。こちらの本をざっと流し読みしていただければ、LINQ の基本的な考え方などは学習できると思います。

■ 従来の EF からの大きな変更点について

(ここはちょっと難しいのでわかる人だけ読んでください) Entity Framework Core では様々な変更が入っていますが、実用側面から見た場合、以下の 2 つは非常に大きな変更点ですので、ここで解説しておきます。

Lazy Loading の廃止

従来の EF では、リレーションシップの先にあるデータを、クエリ実行後に後から手繰れるというトンデモ仕様が含まれていました。現場側の人間からすると、誰だこの学術的機能を入れた人は;、と全力でツッコミたかったわけですが、EF Core ではこの仕様が廃止されました。このため、以下のクエリは実行時に例外が発生します。

image

image

もちろん、場合によっては「クエリ実行時に、リレーションシップの先までデータを取得しておいてほしい」ということもあるはずです。この場合には、クエリ発行前に、明示的に Include, ThenInclude 命令で取り込む対象を指定してください。

image

非同期処理

従来の EF では、ToList() や FirstOrDefault() などによるクエリ実行は同期的にしか実行できませんでしたが、こうしたクエリ実行命令に、非同期処理版が追加されました。このため、以下のようなクエリは await/async 構文を利用して、以下のように記述できるようになりました。


var query = from a in pubs.Authors where a.State == "CA" select a;
var result = query.ToList();
↓
var result = await query.ToListAsync();

EF のこのような機能拡張に合わせて、ASP.NET MVC のアクションメソッドや Web API でも、async 構文を使った定義ができるようになりました。


[HttpGet]
public ActionResult ShowTitlesByPublishers()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Publishers....;
        ViewData["TitlesByPublisher"] = await query.ToList();
    }
    return View();
}

↓

[HttpGet]
public async Task<ActionResult> ShowTitlesByPublishers()
{
    using (PubsEntities pubs = new PubsEntities())
    {
        var query = pubs.Publishers....;
        ViewData["TitlesByPublisher"] = await query.ToListAsync();
    }
    return View();
}

この新機能については様々なところでよく紹介されていますが、クライアント側(UI 処理)における async/await とは意味合いがずいぶん違うので注意してください。ざっくり説明すると、以下の通りです。

  • クライアント側における async/await 処理
    • UI スレッド(メインスレッド)上で時間がかかる処理をしてしまうと、「UI が固まる」という現象が起こる。
    • この問題を起こさないように、長時間(だいたい 30 msec よりも長い時間)を要する処理を別スレッドに切り出して行うために、async/await を使う。
  • サーバ側における async/await 処理
    • サーバ側は、通常、下図のようなマルチスレッド処理で複数のユーザの処理を捌いている。
    • この際、データアクセスのように時間のかかる処理をしてしまうと、当該スレッドは待機状態となり、CPU が遊んでしまう。
    • async/await 処理を行って、CPU を明示的に他のスレッドに回すことで、より CPU の利用効率を高めることができる。

image

サーバサイドではもともとマルチスレッドで処理が行われているため、「サーバが固まる」という現象が起こるわけではないですし、仮に async/await を明示的に行わなかったとしても、OS のマルチスレッド制御機能により、自動的に CPU リソースが他のスレッドに回されますので、すぐさま問題が起こるというわけではありません。ただ、ASP.NET ランタイムが Windows OS 以外でも動作するという話になってくると、OS によってはこのマルチスレッド制御の機能が貧弱なケースも考えられ、そのような場合には「明示的なリソース解放」を行わないと性能が出ない、というケースが出てくるかもしれません。正直、今どきの OS であればそうそう問題が起こるケースはないだろうとは思いますが、とはいえお作法としては、async/await 処理をきちんと書いた方が、環境依存のトラブルが出にくくなるという意味では安心です。

いずれにしても、サーバ側の async/await は、クライアント側の async/await とは利用目的が違う、という点は知っておくとよいでしょう。

■ 接続文字列の管理方法について

先の例では、接続文字列をハードコーディングしましたが、実際のアプリではいくつか課題があります。解決策をいくつかここで示しておきます。

  • アプリが配置されているディレクトリの自動解決
    • 従来の ASP.NET で利用していた |DataDirectory| 文字列は残念ながら ASP.NET Core では利用できません。Startup.cs ファイル内であれば、ソースコードのフォルダ名を比較的簡単に解決できます。これを用いて、Startup.cs から PubsEntities.cs にデータを引き渡すとよいでしょう。
    • 具体的には以下の通りです。
      • Startup.cs にコンストラクタを作る。コンストラクタの引数に IHostingEnvironment を付けておくと、自動的にホスティング環境の情報を渡してくれます。(詳細はここでは解説しませんが、コンストラクタインジェクションと呼ばれる ASP.NET ランタイムの DI コンテナ機能によるものです。)
      • さらに、Pubs.cs ファイル側でこれを拾うようにコードを修正します。
    • ※ (つぶやき)本来を言えば PubsEntities 側でパスを自動解決するように実装したいのですが、1.0.0 RTM 版の時点では PlatformServices クラスに ApplicationEnvironment プロパティしかなく、HostingEnvironment プロパティが存在しないため、そのような実装ができません。将来的には PlatformServices クラスから解決できるようになるのではないかと思います。

public class Startup
{
    public static string App_Data { get; set; }

    public Startup(IHostingEnvironment env)
    {
        App_Data = Path.Combine(env.ContentRootPath, "App_Data");
    }
...

public partial class PubsEntities : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(
            @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
            .Replace("|DataDirectory|", Startup.App_Data));
    }
...
  • 開発環境/運用環境の切り替え
    • #if DEBUG 文を差し込んでおき、開発環境と運用環境の設定を切り替えます。

#if DEBUG
            options.UseSqlServer(
                @"data source=(LocalDB)\mssqllocaldb;attachdbfilename=|DataDirectory|\pubs.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
                .Replace("|DataDirectory|", Startup.App_Data));
#else
            options.UseSqlServer(
                @"Server=tcp:xxxxxxxx.database.windows.net,1433;Database=pubs;User ID=xxxxxxxx@xxxxxxxx;Password=xxxxxxxx;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;");
#endif

なお、ASP.NET Core では構成設定値を web.config ファイルに書きません。この点は従来の ASP.NET Web Forms から大きく変わっている点ですのでご注意ください。背景は以下の通りです。

  • 変更になった最大の理由は、従来の ASP.NET Web Forms では web.config の設定が肥大化し、メンテナンスがつらくなってしまったため
  • 従来の web.config ファイルには、大別して以下の 2 つの設定情報が書かれたが、ASP.NET Core では、これらの情報を分離して扱う。
    • ① ASP.NET ランタイムパイプラインのカスタマイズ方法や設定
      • Startup.cs ファイルの ConfigureServices(), Configure() の 2 つのメソッド内にコードとして記述する。
      • ConfigureServices() で DI コンテナの設定を、Configure() でパイプラインの設定をする。
    • ② 接続文字列などの純粋な設定値
      • 単純なものや、セキュリティ上の問題がないものであれば、上記の例のようにソースコード中にハードコードしてしまうのがラク。
      • どうしてもファイルに切り出したい場合などは、ASP.NET Core の新しい構成設定システムを利用する。(ASP.NET Core の新しい構成設定システムについてはこのエントリでは解説しないので、こちらを参考にしてください。)

■ その他の制約事項・注意事項について

その他、現在の Entity Framework Core 1.0 に関する注意点は以下の通りです。

  • Azure の SQL Database に対する利用について
    • Windows Azure の PaaS データベースである SQL Database に対してクエリを実行する場合、スロットリング(流量制限)によりクエリ実行が失敗するケースがあります。このため、SQL Database に対してクエリを実行する場合には、自動リトライ処理を入れるのがベストプラクティスになっています。
    • EF6 ではこの自動リトライ処理の組み込みが簡単に行えましたが、EF Core 1.0 ではまだこれが実装されていません(2016/07/02 時点)。近いうちに実装されるでしょうが、現時点では制約事項と考えておく必要があります。(ちょっと自力で作り込むのは大変;)
  • 自動トランザクションとの組み合わせについて
    • ASP.NET Core では、System.Transactions 配下の TransactionScope が利用できません。Windows プラットフォームを前提とできる場合には、ランタイムとして Full CLR を利用することで TransactionScope の利用もできなくはないですが、その場合でもあまり組み合わせて利用することはオススメはしません。
    • これはそもそも Enity Framework と自動トランザクションは、設計として相性が悪いためです。
      • Entity Framework では、基本的に、データベースの入出力とは、エンティティというデータの塊の出し入れである、と発想を持っており、「エンティティ」という単位を超えるトランザクション制御はほとんどないよね? という思想を持っています。もちろん実際の業務を見るとエンティティという単位を超えるトランザクション制御が必要になる場合も存在するのですが、そういうものはごく一部だし、高速性を要求する処理だったりすることが多いので、ストプロで実装しちゃってね、という割り切り思想を持っています。(よいか悪いかはともかく)
      • 一方、自動トランザクションは、そうした割り切り型の思想はなく、むしろ「開発者の都合次第でいかようにも組んでよい」という、Entity Framework よりも低水準の技術です。このため、自動トランザクションと Entity Framework を組み合わせようとすると、「UPDLOCK ロックヒントが簡単につけられない」などの問題に突き当たることになります。
    • このため、EF Core を使う場合、(少なくとも現時点では)以下のようにするのがオススメです。(ロックヒントなどの問題は、中長期的には解決されてくる問題かもしれませんが、現時点では以下のように考えておくとよいと思います。)
      • 自動トランザクションとは組み合わせないこと。
      • どうしてもトランザクション制御が必要になるところは、ストプロで実装する。
      • あまりにもストプロ実装が増えそうなら、EF Core ではなく、生の System.Data.SqlClient などを利用することを検討してみる。

以上が EF Core 1.0 の基本的な使い方になります。その他、より詳しい情報については、de:code 2016 DEV-003 セッション 「新しく生まれ変わったデータアクセステクノロジ~Entity Framework Core 1.0 の全貌~」などを見ていただくとよいと思います。

Viewing all 44 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>