torutkのブログ

ソフトウェア・エンジニアのブログ

アプリケーション開発と保守における考慮事項

先日社内でJavaOneサンフランシスコ参加報告を行った際に、なぜかJava.NET Frameworkの違いについての質問を多々受けました。質問は過去に開発したソフトウェアを再利用する場合の互換性などで、Javaについては1996年来からウォッチしていたので回答できますが、.NET Frameworkは過去に一時期技術調査をしたことしかないので、補足調査をしてみました。

なお、調査にあたり念頭においているのはエンタープライズ向けシステムに搭載するアプリケーションソフトウェアの開発で、デスクトップ側にもアプリケーションを配置するクライアント/サーバー系システムです。システムは社内開発ではなくベンダーが開発するものとし、システム運用期間は5〜10年間でその間機能追加等の改修を含む保守を行うものとします。

バージョンについて前提の整理

「バージョンが上がってもアプリケーションに手を入れなくていいの?」といった局面でよく登場する「バージョン」という言葉ですが、.NETアプリケーション開発におけるバージョンには次のものがあります。

  • OSのバージョン(Windows XPVista、7、8、81、など)
  • .NET Frameworkのバージョン(1.0, 1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, など)
  • C#のバージョン(1.0, 2.0, 3.0, 4.0, 5.0, など)
  • Visual Studioのバージョン(2002, 2003, 2005, 2008, 2010, 2012, 2013, など)

※ サービスパックも含めるべきですが、複雑化するので割愛

議論している人がこれらのどのバージョンを言っているかは意外と明確でなかったりします。

Visual StudioのバージョンとC#のバージョン

C#は、バージョンアップごとに言語仕様が変更されているので、開発・保守において考慮すべき事項の一つです。

Visual Studioバージョン 対応C#バージョン
Visual Studio.NET 2002 1.0
Visual Studio.NET 2003 1.2
Visual Studio 2005 2.0
Visual Studio 2008 3.0
Visual Studio 2010 4.0
Visual Studio 2012 5.0
Visual Studio 2013 5.0

Visual StudioのバージョンとC#のバージョンがほぼ固定なので、保守対象のアプリケーション開発で使用したVisual Studioのバージョンを維持し続ける必要があります。例えば、Visual Studio 2005で開発した.NETアプリケーションはC#2.0で、これを保守する際はVisual Studio 2005を使い続けることになります。

ということで、.NETアプリケーション開発では、開発時に使用したVisual Studioバージョンを後生大事に維持して保守することとなります。

Visual Studioはバージョンアップに伴い機能追加され、開発作業が楽になっているので、保守のために古いバージョンのVisual Studioを使うのは苦痛かもしれません。Visual Studioはバージョンによっては使用する.NET Frameworkのバージョンをある範囲で選択できるようになっていますが、コンパイラのバージョン(たとえばC#バージョン)を選択する箇所は見当たらないので、コンパイラバージョンを合わせるには古いVisual Studioを使うことになるかと思います。

また、Visual Studioは有償ツールなので、開発または保守に必要な本数を購入する必要があります。例えば、Visual Studio 2005で開発したソフトウェアの改修を行う場合、参加するプログラマー人数分のVisual Studio 2005を揃える必要があります。ただし、Visual Studioにはダウングレード権という概念があるのでVisual Studio 2008や2010などを保有していればその本数はVisual Studio 2005に適用できるとあります。

Visual Studio.NET Frameworkバージョン

Visual Studioバージョン 対応.NET Frameworkバージョン 備考
Visual Studio.NET 2002 1.0
Visual Studio.NET 2003 1.1
Visual Studio 2005 2.0
Visual Studio 2008 2.0, 3.0, 3.5
Visual Studio 2010 4.0 .NET Framework 3.5をインストールすると、2.0, 3.0, 3.5が選択可能
Visual Studio 2012 4.0, 4.5 .NET Framework 3.5をインストールすると、2.0, 3.0, 3.5が選択可能
Visual Studio 2013 4.5.1 ?

OSと.NET Frameworkバージョン

OSバージョンと、そのOSで動作可能な.NET Frameworkバージョンの関係

OSバージョン 1.0 1.1 2.0 3.0 3.5 4.0 4.5 4.5.1
2000 V V V - - - - -
XP V V V V V V - -
Server 2003 V VV V V V V - -
Server 2003R2 V VV VV V V V - -
Vista - V VV VV V V V
Server 2008 - V VV VV V V V
7 - - VV VV VV V V
Server 2008R2 - - VV VV VV V V
8 - - V V  V - VV
Server 2012 - - V V V - VV
8.1 - - V V V - - VV
  • 凡例
    • -: インストール不可
    • V: インストール可能
    • VV: 標準でインストール

.NETアプリケーションはどのバージョンの.NET Frameworkで動くのか

.NET関係で一番よく分からないことがこれでした。

例えば、Visual Studio 2005でアプリケーションを作成したとします。
この場合、C# 2.0と.NET Framework 2.0となります。
このプログラムは、どの環境で実行可能なのでしょうか?

基本は.NET Framework 2.0のみで実行可能です。
.NET Framework 2.0がインストールされていないマシンで実行しようとするとエラーとなります。

最近Windows 8 PCが増えてきましたが、Windows 8は標準では.NET Framework 4.5がインストールされています。このマシンで、.NET Framework 3.5で開発したアプリケーションを実行しようとすると、.NET 3.5が必要だというエラーメッセージを表示して実行できません。

このように、.NETアプリケーションは、特定のバージョンの.NET Framework決め打ちで動くのが「規定」の動作です。

マイクロソフトの資料ではよく「サイドバイサイド」という言葉が出てきます。これは、バージョン互換を解決する魔法の言葉のように聞こえますが、.NET Frameworkに関して紐解くと、1つのマシン上に複数のバージョンの.NET Frameworkをインストールすることが可能で、.NETアプリケーションを実行するときはそのアプリケーションの「規定」の.NET Framework上で動く、よって.NETアプリケーションを動かすためにはそのアプリケーションが前提とする.NET Frameworkバージョンのインストールが必要、ということと理解するのがよさそうです。

ところが、実際に.NET Frameworkの全てのバージョンを1つのマシンに入れることはできません。

.NET Framework 4.0はCLRバージョンが4.0、.NET Framework 4.5はCLRバージョンが4.5と違いがあるので、この制約は厳しいのではと思います。マイクロソフトのドキュメントでは「大抵動作する」とありますが、大抵ではないものがあるのでしょう。

「規定」の動作ではなく、複数の.NET Frameworkバージョンで動くようにする方法もあります。「規定」以外のバージョンで動作確認を実施し、動作が確認できたらアプリケーションの構成ファイルに動作可能バージョンを記述する方法です。

ビルドの設定(32bit/64bit)

.NETアプリケーションは、ビルド時にAny CPU、32bit、64bitの選択があります。デフォルトはAny CPUなのですが、Any CPUは32bit OSでは32bitで動き、64bit OSでは64bitで動きます。
32bitと指定すると、64bit OSでも32bitで動きます。
64bitと指定すると、32bit OSでは動かず、64bit OSでは64bitで動きます。

ビルドの設定(Release/Debug)

.NETアプリケーションにも、リリースビルド、デバッグビルドがあります。
デバッグビルドは、シンボル情報を持つほか、実行時にJITコンパイラの最適化を抑止する設定が埋め込まれるようです。コンパイラが生成するIL(中間言語)には違いがないという話をみかけますが詳細不明。

ライブラリの作成

.NET関係で二番目によく分からないことがこれでした。

アプリケーション開発を行う際、複数のアプリケーションで共通する処理はライブラリとして一元管理したいところです。.NET Frameworkでは、独自のライブラリの管理があり、それを知らないとライブラリ化に制約を受けます。

よく見かけるのは、ライブラリのソースコードを含むプロジェクトを、ライブラリを利用するアプリケーションのソリューションファイルに追加してアプリケーションの一枚岩に組み込んでしまう方法です。アプリケーションをビルドすると一緒にDLLとして生成され、アプリケーションのexeと同じ場所に置かれます。これなら、.NET Frameworkのライブラリ管理を知らなくても動きます。

これは、事実上ライブラリのソースコードをコピペしている状態です。

複数のアプリケーションから構成されるエンタープライズシステムでは、各アプリケーションごとにライブラリもビルドされることになり、各アプリケーションの実行体が置かれるディレクトリにライブラリのDLLが置かれます。
実効環境ではライブラリがちっとも一元化されていません。もし、ライブラリにバグがあった場合、リリース管理的にはライブラリを使うアプリケーション全てを再ビルドして配布しなければならないですが、大抵はライブラリのDLLだけ上書きコピーして回りそうです。コピー漏れが発生することもありそうですが、アプリケーションのビルド設定が違っていたら生成されるバイナリも違ってしまいます。構成管理的には破綻している状況です。

そこで、アプリケーション間で共通するライブラリを一元的にビルド・インストールして利用できるようにしたいのですが、それが.NET Frameworkでは面倒な状況になります。

.NET Frameworkでは、GAC(Global Assembly Cache)という管理領域に所定の規約で作成したライブラリをインストールした場合に、アプリケーション間で一元化された共通のライブラリを利用可能になります*1
GACに置くライブラリは、同じ名前で複数バージョンを共存させることができるので、あるアプリケーションからはVer.1を使い、別なアプリケーションからはVer.2を使うということが実現可能です。この仕組みを「サイドバイサイド」と呼ぶようです。

GACに置くための規約は次になります。

  • ライブラリに厳密名をつける
  • 署名が必要
  • インストーラでGACにインストールする

GACに置くことができるライブラリは、共有アセンブリと呼ぶそうです。

CLRがアプリケーションの依存ライブラリを検索する順序は次になります。

  • マシンが開発モードで構成されているとき、環境変数DEVPATHで列挙されるフォルダ
  • GAC
  • 構成ファイルのcodebaseに指定された場所
  • "Probing"手順で検索
    • アプリケーション(.exe)が存在するフォルダ以下で、アセンブリ名+.dll(.exe)を探す
(11-23追記)

やっとみつけたGACに置くライブラリ作成方法のわかりやすい解説記事です。

この記事によると、GACに置いたライブラリ(アセンブリ)をVisual Studio上で参照することができないので、開発時はGACとは別途ライブラリ(アセンブリ)を置いて参照するというテクニックを使います。

log4netの構成

.NET Framework用ライブラリとして思いつくものがこのくらいなので・・・

log4net-1.2.12
  +-- bin
        +-- net
              +-- 1.0
              |     +-- release
              |     |     +-- log4net.dll
              |     |     +-- log4net.xml
              +-- 1.1
              |     +-- release
              |     |     +-- log4net.dll
              |     |     +-- log4net.xml
              +-- 2.0
              |     +-- release
              |     |     +-- log4net.dll
              |     |     +-- log4net.xml
              +-- 3.5
              |     :
              +-- 4.0
              :     :

という形で、.NET Frameworkバージョンごとに別々にバイナリが存在しています。
インストール手順が本家サイトに見当たらなく、ぐぐってみると、開発マシンの適当な場所にlog4netアーカイブを展開し、Visual Studioで開発アプリケーションのプロジェクトから展開した中の適当なバージョンの下のlog4net.dllを参照で設定するという使い方が多いようです。
このやり方では、log4net.dllが開発アプリケーションのexeの場所にコピーされると思われます。

NLogという別なロギングライブラリもありましたが、これもバージョン毎にDLLを提供していました。

.NET Framework考慮事項

.NET Frameworkの開発、バージョン、ライブラリ化について調べてきました。
この調査から、.NET Frameworkを使ったアプリケーション開発での考慮事項をまとめます。

.NET Frameworkを使ったアプリケーション開発は、開発に使用したVisual Studioおよび.NET Frameworkのバージョンを継続して使用する

.NET Fraweworkの世界はばら色ではありませんでした。OSのバージョン違いによるアプリケーションへの影響はなくなっていますが、逆に.NET Frameworkバージョンに縛られてしまいました。

補足調査では書きませんでしたが、Visual Studioにサービスパックを当てたかどうかでソリューションファイルが読めなくなるという現象もあるようです。

.NET Frameworkのバージョン間でもある程度の互換性があるから別に新しいバージョンの.NET Frameworkに移行してもいいんじゃないの?という声も出そうですが、エンタープライズシステムのアプリケーションは数百万行規模(数十〜数百プロジェクト)以上になることも珍しくなく、この移行と試験作業はそれなりのコストが発生し、おいそれと着手はできません(ビルド設定を含めてソースコードに何らかの手を入れてビルドし直したのであれば、品質保証上試験は避けられません)。

.NET Frameworkでアプリケーション間で共通化するライブラリはGACに置ける形式で作成する

ライブラリコードを一元管理するには、GACにインストールするのが.NET Frameworkの想定なので、アプリケーション間で共通に使うライブラリはGACに置ける規約で作成するのが本来です。

ライブラリは、利用を想定する.NET Frameworkバージョン毎にバイナリを作成する

GACに置く場合は、バージョン番号管理が少し面倒になりそうです。

同じアセンブリ名で、バージョン番号にどの.NET Frameworkバージョンに対応するかとライブラリ自体のバージョン番号を埋め込むか、アセンブリ名を.NET Frameworkバージョンを含めたものにしてしまうか・・・。

Javaから見ると

Javaの代表的な統合開発環境IDE)のひとつNetBeansでは、プロジェクトの設定で使用するJava言語のバージョンを切り替えられるので、最新のNetBeans 7.4を使って古いJDK 1.4のソースコードを作成することができます。

ライブラリについては、Javaは従来から多数のライブラリを使っています。アプリケーションごとにライブラリファイルをコピーするといったこともなく(必要ならできますが)、一元化して使用することができています。

ライブラリをJavaの各バージョン毎に用意するということもほとんど必要ありません*2

なので、.NET Frameworkがこれだけ制約をもっていることに少々意外な感じを受けました。

すこし穿った見方をすると、

  • .NET FrameworkおよびC#はVisual Stuioのバージョンアップ毎(おおよそ2、3年おき)に仕様が変わっていくため、共通化を行ったライブラリも陳腐化しやすい。なので、ライブラリに対するニーズが低い。スクラッチ&ビルドが主体となっている。
  • .NET Frameworkの各バージョン用のライブラリを提供するということは、それだけ試験を実施しなくてはならず、試験コストが高くなってしまいペイできないので、ライブラリ提供が少ない。

ということかなと・・・。

*1:コードベースを構成ファイルに指定する方法もあるようですが、アプリケーションのトップディレクトリ外を参照するにはGAC同様厳密名と署名が必要になるようです。

*2:ただし、ライブラリのAPIJava SE 5で新規追加されたジェネリクスを使うと、Java 2 SE 1.4以前では使用できないため、ある一時ライブラリがJava 2 SE 1.4以前用とJava SE 5以降用と2つのバイナリを提供していました。現時点では、Java 2 SE 1.4は既にEOL(サポートが終了)となっているので、複数バージョン用のライブラリ提供はほとんど見受けられません。ただ、来年3月にリリース予定のJava SE 8ではJava言語仕様に大きな変化(ラムダ式)があるので、しばらくJava SE 7以前用とJava SE 8用の2つのバイナリが提供されるかもしれません