Tech Fielders セミナー「データアクセス & WPF 〜 これからのビジネスアプリケーション」行ってきた

昨年末に続いて二回目の参加。Tech Fieldersはセミナー(3、4時間) → ライトニングトーク(1時間弱) → 懇親会(ビールとか飲みながら立ち話)という感じで進行して、セミナーもためになるんだけど、ライトニングトークと懇親会が楽しいんですよ! 今日はざっと数えたところ、60人くらい参加者がいたんだけど、ライトニングトーク前に一部、懇親会前にも結構な数の人が帰ってしまって、もったいないなー、と思った。スーツで来てる人が多いから、一旦会社戻らなければならない、戻るとしても酒飲んでからじゃあなー、というのがあるのかもしれない。懇親会は、その辺の会話聞いてるだけでも楽しいし、マイクロソフトの方にいろいろ訊けるのがいい。最近『ドメイン駆動』に再チャレンジ(政策)してて、今日はセミナーがEntity Frameworkの話だったから、アルコールの力を借りて、ドメインモデルってどうなんすか?? みたいな話をきけて面白かった。普段そういう話をする機会がないし、勉強会っつーか、僕は話できるほどの経験もないんで興味がある人で読書会なりやってみたいなーと思うんだけど、どうでしょうか。僕は品川に住んでるので関東近辺がいいんですが……。
話がそれた。ライトニングトークもいい。そのうち参加しようと思ってたけど、先延ばししても意味ないんで参加できる最速、4/6のに応募してみようと思います。ただ、よく考えたら年度頭の月曜日だから、なんか会社の行事ありそうなんだよなー、要確認。

以下感想。セミナーは二本立て。

データアクセス アプリケーション開発

さらに二本立てで、

  1. ADO.NET EntityFramework
  2. ADO.NET DataServices

EntityFrameworkは、今よく使ってる型付きDataSetとどう違うの、どう便利なん、というのがよくわかってなくて、実際使えば全然違うんだろうけど、大雑把に言うとテーブルの構造から自動で推測できるところを補って一段概念モデルに近い? Entityにしてくれるみたいな感じなんかなー、と思ったけどやっぱり実際使ってみないとよくわからんなーという感想。
DataServicesは全然詳しく知らなかったので、面白かった。実際業務で自分が使うというよりは、世の中のWebサービスが全部これで実装されてたら、自作のツールとかマッシュアップとかやりやすそうでいいなー、と思った。

WPFアプリケーション開発

いままでのWPF紹介ではDirect 3Dがー、とか、メディアがー、というところに偏りすぎてたので、ビジネスアプリ作成にも良い面があるよ! という話。セッションの最初に、すでにWPFをつかってビジネスとしてアプリを作成しているか聞いたら、6、7人いたので驚いた。WPFは去年ちょっと自習したので、機能面での紹介は大体知ってる事でしたが、Windowsフォームとの比較の話とか面白かった。
そういえば、MSDNマガジンにはSilverlight 2で基幹業務エンタープライズ アプリケーションを作る、という記事があったなー、これは流行りなのか、にしても、基幹業務エンタープライズ アプリケーションというのは長すぎな名前では?

しかもなんかページタイトルは

となっているのは何故……。

Windows Azure上からSystem.Management.Automation.dllを参照したい、けど出来ない……

Windows Azureのinvitation codeが来たので、やってやるぜ!! と意気込んで開発キットインストールしようとしたらXP不可……。Vistaのインストールから初めて今日やっと環境ができたのですが、ひとつわからないことがあって解決できないので、詳しい方がいらしゃったらヒントを頂けるとありがたいです。
やりたいことは、「Azure上のサービスからSystem.Management.Automation.dlll内のPSParserクラスを利用する」です。
ローカルで、通常のASP.NETサイトとして動かしているときは、利用できます。
# 通常のASP.NETサイトとして動かしているというのは、ServiceConfiguration.cscfg含む、ccprojではなく、csprojのほうをスタートアッププロジェクトに指定してデバッグしている状態のことを言っています
次に、Azureのサービスとして?ccprojのほうをスタートアッププロジェクトにしてローカル環境でデバッグすると、

System.Security.SecurityException: アセンブリは部分的に信頼されている呼び出し元を許可しません。

この例外が出ます。System.Management.Automation.dllは厳密名が付いていて、かつAllowPartiallyTrustedCallers属性が付いていないので、フルトラストからしか呼び出せないのでこのエラーになるのか? しかし、web.configにを追加すると、allowOverrideが"false"だから設定できませんよ、みたいなエラーが出る。
DLLの厳密名や属性は変更できないだろうし、フルトラストにも出来ないだろうから、この時点で無理だろうな~という気はしたものの、一応Azureにデプロイしてみると、今度は

Could not load type 'System.Management.Automation.PSToken' from assembly 'System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

また違うエラーになったので、これはどういうことなのか、と悩んでいます。デプロイしたパッケージにDLLが含まれているのかいないのかが、中を見てもよくわからない……。
ちなみに、PowerShellスクリプトを登録するとパーサ使って色変えたり自動で使われてるコマンドレットのタグを割り振ったりするスレッドフロートな掲示板でも作りたいなー、と思っていたんですが、無理そうなので、何か別のことを考えたい……。

オブジェクトの深いところの値を例外出す可能性無しにとってきたい

仕事でメンバーの数的にすごいでっかいオブジェクト扱うんですよ、それで、それのすごいばらばらな場所にある値をとってきてASP.NETの画面に出したい。100個位。という事情があって、でも、たとえば、a.b.c[0].dとかいう値を表示したいとして、絶対に例外でないように書くといちいちnullと配列の長さチェックしなければいけないわけですよ。全部コードビハインドのプロパティにしてるから、例外でるとDataBind()のところまで飛ばされちゃって、以降の値が表示されなくなるし、それだと困るから。
で、途中の値がnullでとれないこと自体はまぁどうでも良いというか、どうでもよくは無いんだけど無いものは無いしログにでもだしといて、という感じなので、いままでは、

if(instance_a.b == null ||
   instance_a.b.c == null || instance_a.b.c.Length == 0 ||
   instance_a.b.c[0].d == null)
   Console.WriteLine("どこかがnullかもしくは配列の長さがおかしかったので、instance_a.b.c[0].dは取得できませんでした!!");
else
   Console.WriteLine(instance_a.b.c[0].d.s1);

とか、

try {
   Console.WriteLine(instance_a.b.c[0].d.s1);
}
catch(NullReferenceException) {
   Console.WriteLine("どこかがnullだったので、instance_a.b.c[0].dは取得できませんでした!!");
}
catch(IndexOutOfRangeException) {
   Console.WriteLine("どこかで配列インデックスがおかしかったので、instance_a.b.c[0].dは取得できませんでした!!");
}

してたんだけど、あれだね!! Expressionてすごいね! まぁ、デリゲートでも別に出来たみたいなんだけど、こう出来るね。

static string Getda<T>(T a, Expression<Func<T, String>> x) {
   try {
      return x.Compile()(a);
   }
   catch(NullReferenceException) {
      return string.Format("どこかがnullだったので、{0}は取得できませんでした!!", x.Body);
   }
   catch(IndexOutOfRangeException) {
      return string.Format("どこかで配列インデックスがおかしかったので、{0}は取得できませんでした!!", x.Body);
   }
}

呼び出す側は、一行だ……。

Console.WriteLine(Getda(instance_a, a => a.b.c[0].d.s1));

すげー、と思って会社の人とかに見せたんだけどあんまり評判よくなかった。拡張メソッドにすると

Console.WriteLine(instance_a.Getda(a => a.b.c[0].d.s2));

とか書けて夢のようなのに……。
Complileとか、例外出るところとかが印象悪いみたいで、基本nullになってるとことか無いし保険みたいなもんだから、例外のほうは100個全部例外出ても僕のマシンで0.01秒とかだけどなー、やっぱ無しなのかな。

XAMLについて

前々エントリの


Chart -Width 300 -Height 250 -Theme Theme3 -Watermark $false -Animation $true -Series (
DataSeries -DataPoints $(
1..(Get-Random -min 3 -max 6) | ForEach-Object { DataPoint -YValue (Get-Random 100) }
)
) | Boots
XAMLで(面倒と言うか書き方が分からなかったのでランダムになってるところを固定にして)書き直すと

<vc:Chart xmlns:vc="clr-namespace:Visifire.Charts;assembly=WPFVisifire.Charts"
          Width="300" Height="250" Theme="Theme3" Watermark="False" AnimationEnabled="True">
  <vc:Chart.Series>
    <vc:DataSeries>
      <vc:DataSeries.DataPoints>
        <vc:DataPoint YValue="10" />
        <vc:DataPoint YValue="20" />
      </vc:DataSeries.DataPoints>
    </vc:DataSeries>
  </vc:Chart.Series>
</vc:Chart>

こういう感じで、C#で書くとこうなる。

Chart chrt = new Chart() {
   Width = 300, Height = 250, Theme = "Theme3", Watermark = false,
   AnimationEnabled = true,
};
DataSeries ds = new DataSeries();
ds.DataPoints.Add(new DataPoint() { YValue = 10 });
ds.DataPoints.Add(new DataPoint() { YValue = 20 });
chrt.Series.Add(ds);

本当は、C#でもオブジェクトのnewのみで再現したかったんだけど、ObservableCollectionのメンバーは、newして代入すると上手く動かないようなので、addで追加するようにした。このへん、PowerBootsではどうしてるのかなー、と思って、(Get-Item Function:\Chart).Definitionとかで、自動生成されるインスタンス作成関数の定義を覗いて見ると、パラメータで渡されたオブジェクトがIListだったらaddを試みるようにされてるっぽかった。で、まぁ、こうして並べてみると、これだけみるとPowerBootsは結構良い、綺麗じゃないかなーと思った。
普段仕事ではASP.NETばかり触ってるので、XAMLもその延長線上的なイメージで捉えてたんだけど、やっぱり全然違うものだなー。aspxファイルはどこまで行っても、htmlに埋め込まれた要素をプログラムから扱える、という感じで捉えるのが妥当なところだろうと思うけど、XAMLはUIを表現しやすい形でインスタンス生成を指示できるというか。
エッセンシャルWPF:Windows Presentation Foundation (Programmer's SELECTION)』P11。

ごく簡単に言うと、XAMLCLRオブジェクトのためのXMLベースのインスタンススクリプトです。XMLのタグとCLRの型、およびXMLの属性とCLRのプロパティおよびイベントは、それぞれ対応しています。

PowerShellCLRオブジェクトを作るのにNew-Objectをつかうしかなくて、美しくないというか、あれやで、というところがあったので、PowerBootsのオブジェクト生成関数を作ってくれる、PowerBootsの機能はWPF使う以外にも便利な場面が一杯ありそう。

PowerShellでテストすると良いよという話

Windows PowerShell Blog : Why Should I Test With PowerShell?で、PowerShellをテストに使うと良いぜ、という話が出ていて、10個ほど、利点が挙げられてたので訳してみました。コメント欄でもかかれてますけど、僕には具体的にどういうことなのか良く分からないところもありましたが……。

  1. PowerShellの中で、簡単にコマンドラインプログラムが実行できる
  2. テストケース用に、動的にコードを生成したり、テストデータを生成したりできる(これによって、ファジングとか、データ駆動なテストがとっても簡単に)
  3. PowerShellから、COMへ簡単にアクセスできるようになってる
  4. PowerShellC#に埋め込める(なので、フレームワークを書かかずに、単に、基盤へPowerShellを埋め込める)
  5. PowerShellでは弱い型付けの変数を使える
  6. コマンドラインからAPIを試せるので、手動でテストできるし、自動的にも行える
  7. コマンドラインの履歴を参照して、なにについて調査したか確認、それをテストケースに落とし込める
  8. 必要があれば、強い型キャストや、型変換が行える
  9. 追加のテスト情報を提供するのに、verbose、warning、debugストリームがサポートされている
  10. テスト環境を準備、後始末するのに、PowerShellのシステム管理機能が役立つ

細かいところだと、"strongly cast"と"coerce types"の違いがわからんねーとか、"embed PowerShell in your infrastructure"ってどういう事なの、とかは思うんだけど、僕自身最近のPowerShellの用途は仕事で作ったアプリ内のクラスを呼び出してテストしたりとか、一部分を切り出してツールにしたりとか、テスト用にデータの記録とか、なんで、ここに書いてあるのは確かにそうかな、と思います。C#なりVB.NETなりで全部できるといえばその通りなんだけど、コンパイルいらないとか、弱い型付けなんで色々楽だね、とか、C#のソースをエディタの置換とかで量産するとなんかダサい気がするけどスクリプトなら逆に効率的で良いことな印象を受けるとか気分的な点も含めて。
もう一つ、似たような用途なんですが、仕事ではASMXのWebサービスを良く作って/使っていて、ここでWebサービスを使う事自体パフォーマンス的にどうなのよ、とかは思いますが、好きに設計できるわけでもないので黙々とやっており、ログはパラメータをXMLシリアライズして残してたりするんですが、なんか障害でもあった時に、ログからリクエストを切り出して、PowerShellスクリプトで同じ要求を投げてみる、とかを良くやります。WSDL参照して作ったプロキシクラスをDLLに突っ込んでおいて、呼び出すだけなんですけど。こういう感じ。イメージですけどそのままなので……。namespaceごとに、クラス名のところを{0}にした文字列を用意しておくやり方は、『Windows PowerShellクックブック』に載ってました。


# ASMX Webサービスのプロキシクラスを含むDLL
[System.Reflection.Assembly]::LoadFile('c:\work\ws_proxy_desu.dll')
# シリアライズ
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml")

# フォーマットして使う用namespace
$ct = 'Work.Brand.Gyomu.Src.NantokaAPI.{0}'
$xml = 'System.Xml.Serialization.{0}'
$io = 'System.IO.{0}'

# シリアライザ
$slzr = New-Object ($xml -f 'XmlSerializer') -ArgumentList @($ct -f 'REQUEST_PARAM')

# ファイルからデシリアライズ / 引数からとったほうが便利
$fs = New-Object ($io -f 'FileStream') -ArgumentList @('c:\work\req.xml', 'Open')
try { $req = $slzr.Deserialize($fs) } finally { $fs.Close() }

# クライアントプロキシクラスのインスタンス
$ws = New-Object ($ct -f 'WS_Proxy')

# 必要があればURL書き換える
# $ws.Url = ''

# WebMethod呼び出し
$res = $ws.WebMethod($req)

PowerBootsについて

Joel Bennettさんが作っておられる、PowerBootsが面白いです。RubyGUIツールキット、ShoesにインスパイアされたというPowerShellGUIモジュールです。PowerBootsの概要はPowerBoots: The tutorial walkthroughが良い感じです。最初のリリースが1/5、1/16時点で5回バージョンがあがってるのでまだまだどんどん変わっていくと思いますけど。
#余談ですが、Ruby Shoesだとルビーの靴でありかわいらしい感じですが、Power Bootsはジャンピングシューズのような補助動力付き長靴のことであるらしく、Power Bootsで検索すると、面白画像が一杯出てきます。また、power shellで検索するとスピリチュアルな力を持つ貝殻の広告が沢山……。
先ほどのチュートリアルにあるサンプルコードは、もちろん、全部そのまま自分で動かすことが出来るのですが、先日のHuddled Massesのエントリ、PowerBoots - Output Graphs to Images from PowerShellのサンプル? が全然動かない……、ということで少し調べた事を書きたいと思います。
そもそも、二行目で使われている"Chart"というfunctionらしきものが、どこにも定義されていない。というか、先ほどのチュートリアルに出てきた、"Window"、"Button"、"StackPanel"なんかも、モジュールのソースを覗いてもどこにも定義されていないわけです。これらの関数はどこで定義されているかというと、モジュールのディレクトリにある、"PresentationFramework^{namespace}.Proxies.txt"ファイルに記述されたクラス名を元に、動的に生成されています。と、ここに書いてありました。で、じゃあ"Chart"とか、"DataPoints"といったクラスはどこで定義されているかというと、VisifireのDLLの中にあります。
なので、Visifireの"Download Now!"で落としてきたファイルの中から、WPFVisifire.Charts.dllを探し出して、ロードします。それから、PresentationFramework^Visifire.Charts.Proxies.txtという名前のファイルをPowerBootsのモジュールディレクトリに作り、インスタンス生成用関数を作りたいクラスの名前を列挙します。とりあえず、"Chart"、"DataSeries"、"DataPoint"辺りを列挙しておきます。それから、"Import-Module -Name PowerBoots"で、PowerBootsモジュールを読み込みます。ということで、PowerBoots自体の準備も含めて整理すると、

  1. $Env:PSMODULEPATH のどれかのディレクトリにPowerBoots自体をおく
  2. そのディレクトリにPresentationFramework^Visifire.Charts.Proxies.txtもおく
  3. ここでPowerShell起動。 # ISEじゃない、普通のPowerShellを使う場合、"-STA"オプションをつけたほうが良い?
  4. [System.Reflection.Assembly]::LoadFile('dokoka\WPFVisifire.Charts.dll')とかでアセンブリをロードする
  5. "Import-Module -Name PowerBoots"

これで、さっきまで動かなかったサンプル(の一部を改変した形で)動かすことが出来ました。


Chart -Width 300 -Height 250 -Theme Theme3 -Watermark $false -Animation $true -Series (
DataSeries -DataPoints $(
1..(Get-Random -min 3 -max 6) | ForEach-Object { DataPoint -YValue (Get-Random 100) }
)
) | Boots
実行するとこういう感じ。

0 -eq "" は$true

SqlServerから取ってきたDataTableをHTMLでテーブルにして表示する、というスクリプトPowerShellで書いていて、以下のような感じなんです。


function Write-HTML($dt, $name){
$str = "<h2>$($name)</h2><table>"

$str += "<tr>"
$dt.Columns | % { $str += "<th>" + $_.ColumnName + "</th>" }
$str += "</tr>"

foreach($row in $dt.Rows){
$str += "<tr>"
0 .. ($dt.Columns.Count - 1) | % { $str += "<td nowrap>" + (Format-DBData $row[$_]) + "</td>" }
$str += "</tr>"
}
$str += "</table>"
$SCRIPT:html += $str
}

function Format-DBData($v){
if($v -eq [System.DBNull]::Value) { return "<i><b>DBNull</b></i>" }
if("" -eq $v) { return "<i><b>空</b></i>" }
if($v -match "^\s+$") { return "<i><b>スペース × {0}</b></i>" -f $v.Length }
$ret = [System.Web.HttpUtility]::HtmlEncode($v).Replace("`n", "<br />")
return $ret
}

二つ目のfunctionでは、テーブルのカラムにDBNullが入っていたらDBNull、空文字列がはいっていたら、などと変換して返します。で、この関数の二行目、if("" -eq $v) の部分は、最初 if($v -eq "") と書いてたんですがそうするとやたらにになる。どうも、int型とかで値に0が入ってるとになるなぁと思って調べてたら

0 -eq ""

がTrueなんですよ。

"" -eq 0

はFalseなんだけど。たぶん、演算子の左側に合わせて右がまずキャストされて、

[int]""

は0なので、Trueが返ってくると思うんですけど。ちょっと不気味。そんだけです……