2010年7月24日土曜日

COMコンポーネントをIISで実行する

サーバサイドでCOMを実行したいことはままあることで、それでは、と普通にローカルで動作しているCOMをIISのプロセス上で動かすとCoCreateInstanceでインスタンスが生成できなかったりする。考えられる理由は下記の通り。

・COMがシングルスレッド上でしか実行できない
・IISのアプリケーションプールが正しくない

一つ目は、いくつかのCOMはシングルスレッド上でしか実行できない。そのためマルチスレッド上でCoCreateInstanceを行ってもインスタンス生成ができない。回避方法はSTAのスレッドを開始して、そのスレッド上でCOMを呼び出せばよい。

Thread.SetApartmentState Method

ただ、STAなので処理はすべて順番に行われる。そのためリクエストが多重になればなるほどパフォーマンス上のボトルネックになるので注意が必要だ。


ついで、IISのアプリケーションプールのIDがNetWorkServiceのままだと動作しない場合がある。その場合は下図を参考にIDをLocalServiceに変更してもらいたい。

COMを実行したいアプリケーションプールの詳細設定を選択する。

プロセスモデルのIDをLocalServiceに変更する。

WCFをSSLを介してSilverlightで使う

Silverlight4.0でSSLを介してWCFを使用したときに大変困った箇所を紹介する。1からの導入方法の紹介ではないので、それが必要な人は他のブログを参照して欲しい。


設定を行った環境は以下の通り。
Silverlight 4.0
WCF(このプロジェクトはIISにて仮想ディレクトリ化して動作させている)
IIS7
自己署名入り証明書
Windows 7 Pro 64bit


UAC
まず手始めとして、UAC(User Account Control)を無効にしたほうが良い。直接的には関係が無いのかもしれないが、UACを無効にせずに環境設定をおこなったらいつまでたっても動作しなかったものが、UAC無効化後に再度設定すると正常に動作しはじめた。特にWCFの仮想ディレクトリを作るところで違いがあるようだ。
How to Disable and Turn Off UAC in Windows 7


WCFの説明
Silverlightで開発をする場合サーバとの通信手段ではWCFを使用するのがスタンダードだ。しかし、このWCFが本当に優しくない。素人には大変厳しい仕様になっている。WCFは通信部分をすべて隠蔽してくれるので、サービスの参照の追加を行うとプロキシクラスを生成してくれて、クライアント側から簡単にサーバにアクセスできる手段を提供してくれる。しかし、この隠蔽が大変曲者だ。

プロキシクラスからサーバへリクエストを投げて何かしらの通信エラーが起こった場合、WCFはCommunicationExceptionを投げる。しかし、この例外、受け取るメッセージは常に一緒なのに要因は実に多岐に渡る。簡単に列挙するだけで下記の要因がある。

・Cross Domainエラーが発生している場合
・WCFのクライアント側の構成ファイルの記述に誤りがある場合
・証明書の設定が正しくない

ひとつひとつの要因が大きいのに、すべてCommunicationExceptionで包まれているので初見ではまったく意味が分からない。ここでは参考になったブログポストを紹介しておくので同じ現象にはまっている人は参考にして欲しい。

Network Security Access Restrictions in Silverlight
Making a Service Available Across Domain Boundaries
Cross Domainエラーが発生する場合は、inetpub/wwwrootの直下にclientaccesspolicy.xmlかcrossdomain.xmlを配置する必要がある。その両ファイルの背景と構成の説明。かなり長いので必要な箇所だけ拾い読みしてほしい。

Configuration Editor Tool (SvcConfigEditor.exe)
ServiceReferences.ClientConfigを編集するツール。記述に間違いがあるとエラーを出力するので、エレメントのチェックにも使える。

How to Set Up SSL on IIS 7
IISにSSLを設定する方法。真ん中から下以降を参考に。

IIS関連
Silverlight application running on FF gets CommunicationException during accessing a web using WCF through SSL, but not IE nor Chrome
IEとChromeでは動作するのにFireFoxだけCross Domainエラーを出力する場合に参考にしてほしい。


CommunicationExceptionでつまる原因は大体上記に当てはまると思うので、サーバに正常にアクセスできない場合は前述のブログポストを参考に対処してもらいたい。

2010年7月4日日曜日

POCOとはなんぞや 自分的まとめ

以前、MIX 10のセッションビデオの中で「こういうのはPOCOで開発するんだよ」と解説してくれているのがあって(どのセッションかは失念)、そのときはたいして気にもしていなかったのだが、最近データアクセス層を作っているときに、おっ、ここはいっちょPOCOってみるか、と思いつき色々調べたらよく分からなかったので、今後のためにまとめておく。
(※間違っているかも知れないので話半分で聞いてください)

理解の元ネタは下記から収集。
POCO vs DTO at stackoverflow
POCO As A Lifestyle
What is the Difference Between a DTO and a POCO?

POCO vs DTOとなっているのは、一見POCOとDTOは似ているので、POCOのことを正確に理解していない人は「POCOってのはDTOと一緒だよ」とのたまうらしく、そちらの世界に導かれると「じゃぁなんでPOCOが必要なの?」って質問に答えられなくなるので、ホントのところはどうなってんだよ?という魂の叫びの表れである。

DTOはData Transfer Objectの略称で、DTOの唯一の目的は状態(データとかも含むのかな?)を他の層(とか他のアプリなど)へ渡すことにあって、OOP(オブジェクト指向プログラミング)的な振る舞い(特にビジネスロジック的なもの)を持つべきではない。必要な情報を渡すためだけの箱であって、それ以上でも以下でもない。

例:
// 正しいDTO
class PersonRightDTO 
{
 public string Name { get; set; }
 public DateTime Birthday { get; set; }
}

// 正しくないDTO
class PersonWrongDTO
{
 public string Name { get; set; }
 public DateTime Birthday { get; set; }
 public int GetAge() { 
  return // Calculate here.
 }
}

POCOはPlain Old CLR Objectの略称で、これはPOJO(Plain Old Java Object)から来ている(POJOのWikiはこちら)。で、POCOはPOJOと同じく、クラスの設計に.net frameworkをかませない(命令させない、規制させない)で行おう、という考え方。

なので、ここまでをまとめると、POCOはシンプルなOOPへのアプローチ方法の説明で、DTOはデータをどっかに渡すためのパターン、となる


で、ここから自分の解釈になるのだけど、こんな風になるのかな、と図解してみた。


ビジネスエンティティやドメインモデルと呼ばれるているオブジェクトがPOCOである。各層をつなぐ矢印があるけれど、その際に渡すオブジェクト(があるならば)はDTOでもPOCOでも良い。ドメインモデルと合致しないようなデータを渡すだけならば、それようにDTOを作って渡す方が良い。なので、DTOはPOCOよりもシンプルな構造になる。

最後に、POCOはPersistence Ignorance(PI:永続化に無知。変な日本語で申し訳ないけれど訳し方が分からない)でなければならない。つまり、Save()や、GetDataById()のような、永続性を持つロジック(データの読み込み、保存など)を実装してはならない。そのようなロジックはデータアクセス層に実装しよう。

ということでPOCOを使うと各層を明確に分離できるので、より良い構造になる。


以下にDDD(Domain Driven Design)を理解するのに良いと勧められていた本を紹介しておく。Google BooksにPreviewがあるのでそちらもどうぞ。

Domain-Driven Design: Tackling Complexity in the Heart of Software

2010年7月1日木曜日

行番号を取得するクエリ SQL Server

SQL Serverで行番号を取得するクエリを紹介する。

select row_number() over (order by column1) as row_number, * from table

このクエリはSQL Server 2005から導入されていたようで、以前SQL Server 2000で開発していた際に、同様の機能がないかとちまなこになって探していたのも今や昔。

サンプルクエリが下記ブログにたくさん載っているので参照されたし。


Multipurpose Row_Number() Function