スマートクライアントと参照アセンブリ

WebサーバをリポジトリとしてアセンブリをダウンロードしてローカルPC上で実行するスマートクライアントがあったとする。(.NET2.0ではノータッチデプロイを改良したClickOnceという技術が追加されるが現状はClickOnceは使わない) このスマクラの実行アセンブリが次のようなアセンブリ依存関係を持っていたとしよう。なお、それぞれのアセンブリはGACにはインストールせず全てサイドバイサイド実行を想定している。(本当は各種設定ファイルも必要だが話を単純にするために無視している)

 実行アセンブリ
      ├── アセンブリA
      └── アセンブリB

上のような依存は実行アセンブリのアセンブリマニフェストに参照するアセンブリの名前として以下の様に記述される(ILのアセンブリディレクティブとして記述される)

.assembly extern アセンブリA
{
  .ver バージョン
}
.assembly extern アセンブリB
{
  .ver バージョン
}

このようなアセンブリ構成の場合、Webサーバ上のリポジトリには"実行アセンブリ"、"アセンブリA"、"アセンブリB"の全てを配置しておき、クライアントは以下の手順で必要なアセンブリをダウンロードして実行することができるだろう。

  • 実行アセンブリをリポジトリからダウンロードする
  • 対象のアセンブリ経由でAssembly#GetReferencedAssemblies()メソッドを使用して参照されている(マニフェストに記述されている)全てのAssemblyNameを取得する
  • 取得されたAssemblyNameに合致するアセンブリをリポジトリからダウンロードする
  • 実行アセンブリを実行する

これで万事OKなのだがリフレクション等を使ってアセンブリを動的にロードするようなアプリケーションの場合、困ったことになる。アセンブリの依存は以下のようになるのだが

 実行アセンブリ
      ├── アセンブリA ── 実行時にアセンブリCをロード
      └── アセンブリB ── 実行時にアセンブリDをロード

こうすると当然だが必要とされるアセンブリCとアセンブリDはアセンブリマニフェスト上には依存するアセンブリとして記録されないので例えWebサーバ上のリポジトリにアセンブリCとDを配置していたとしても前述した手順中のAssembly#GetReferencedAssemblies()メソッドでは名前を拾えないのでダウンロード対象にはならないのだ。共有されるアセンブリ(ここではCとD)をGACに登録しておけばよさげであるが意地でもGACは使いたくない。となると

  • リポジトリ上に配置してあるアセンブリは無条件で全てダウンロードする
  • 実行アセンブリの依存を記述したXMLファイル等をリポジトリに配置し、それで必要なアセンブリを決定する

等の手段が考えられるがどっちも今ひとつ。
そこで調べてみると.NETの構成ファイルは依存するアセンブリを指定する語彙がスキーマに用意されているので以下のように書けばアセンブリマニフェストの参照アセンブリに依存アセンブリが追加されてくれないかな、と期待を込めたのだが...

  • 構成ファイルのruntime要素で依存するアセンブリを指定する
<?xml version="1.0" ?>

   
      
         
             
         
         
             
         
      
   

※ 属性:publicKeyTokenは設定していないアセンブリの場合は明示的にnullをセットしないとビルドすら通らなかった(2時間はまった..)

結果として上記の記述に対応するアセンブリディレクティブ(.assembly〜)は生成されなかった。この語彙はバージョン管理にだけ使用するらしい。
う〜ん、どうしたものか...

#追記#
最初に要件として書くべきでしたが、そもそもスマートクライアントでリフレクションを実現するためにはセキュリティの設定を大幅に緩くする必要がありますがこのエントリで言及するスマートクライアントを動かす環境はいわゆる"ローカルイントラネットで"の使用を前提にしており、全てのPCのアクセス許可セットはFullTrustに準じた運用しか考慮していません。