ASP.NETに限りませんが、パス(ファイルシステムからみた物理パス/Webサーバからみた仮想パスの双方)に関する情報の取扱いがあまり上手くない為に、別の仮想ディレクトリや別の物理パスに移動する際に、コード自体に手を入れる必要に迫れてしまうような作りになっているものをちょくちょく見かけます。というか、昔の自分の作ったのを見ていると、結構あるので凹む。
通常のスタンドアロンアプリでも特定の所定の場所にしかインストール出来ないなんてものは非常にイケてないと判断される場合が殆どです。Webアプリであっても、製造当時に予定していた本番稼動するものとは異なる違う物理パス/仮想パスに配置、ホストする/したいなんて言うのは日常茶飯事です。そもそも、本番前のテストの時とか、普通にあります。それが容易にできないとものすごくテストなんかやり難かったりして困ります。本当に困ります。
#ひどいのになると、開発環境、結合テスト環境、ユーザテスト環境、本番環境でそれぞれコードに手を入れないと動かないような作りにしてるとか。いや、結構あるんですよ、奥さん。
また、外部ファイルに書かれていても仮想パス、物理パス、相対パス、絶対パスが入り乱れて書かれていて、それらに一定の法則が見出せないような書き方がされているとか。
あるいは設定場所(Web.Config、DB、コード内の定数等)が分散しててそれらの法則性が解らないとか。書き換えるときに非常に混乱します。はい。
このあたりの事はちょくちょく質問されたりするというか、昨日も関東の某氏からも「定石として普通はどんなん?」みたいなことを聞かれたので、少なくとも僕がそう思っていることを書いておきます*1。
パスの性質によって分かれると思いますが、おもに私は基本的には以下の基準で振り分けるようにしています*2。
1:仮想パスであってもルートからの絶対パスで記述するものは、ハードコーディングしない。
→ 自アプリ無いのリソースのパスであれば、こんなのをハードコーディングしても配置位置が縛られるだけでなにもおいしくないです。というか、自アプリの仮想ディレクトリ内のものをルートからの絶対パスで書く=ホストする環境ごとに設定を変えないといけないくなるので、外部ファイルに記述する場合でも避けたいです。避け方は、後述。
また、他のアプリのリソースであれば、そんなもの書いて無駄に密結合を作りこんではいけません。
2:仮想の相対パスであっても、コード中で頻繁に出てくるもの、特にASPX.*ではなくて別のクラス内に書く必要があるものは定数かWeb.Config
→コード中に何度も同じリテラルを埋め込むのは、コードの可読性やリファクタリングの障害になる可能性があると考えているので、一か所で定義してしまいます。さすがに毎回Web.Config読むのはあほらしい気がするので、どっかで専用のクラス救ってPublicの定数にするか、staticのコンストラクタで読み込んでReadOnlyなstaticプロパティで公開するかにしています。少しでも変更の可能性があるなら、定数よりもreadonlyのプロパティかな。
3:物理パスはWeb.Configに。
物理パス=大抵は絶対パスになるとおもうので、これは問答無用で外部設定ファイル行き。
と、こんな感じです。
また、DB内にパラメータ設定テーブルみたいなものを作って記述する設定をすることがあるかもしれませんが、私は少なくともこれらのパスに関する情報は、基本的にDBには書きません。例えばDBサーバとWebサーバでそれぞれ本番環境とテスト環境がある際、『Webサーバは本番環境、DBはテスト環境』のような環境で動作させる時に、物理パスがそれぞれ違うとそのままでは実行させれなかったりするので。あまりいいことになった試しがないです。
#自分のアプリケーションの中のパスの話です。外部のアプリケーションへのパスはまた別の話。
概ね、これらの基準でうまく行っているとおもいます。
また、相対の仮想パスで設定しているものに対して、たとえばファイルのアップロード等で物理パスとしてアクセスする必要がある場合は、HttpServerUtility.MapPathメソッドで物理パスを取得して使います。まあ、このあたりは常識レベルか。
意外と知られていなさそうに見えるのが、同一仮想ディレクトリ内のパスで『自分が置かれているディレクトリとは別のディレクトリ配下のリソースへのパス』の記述の仕方。言葉で説明してもわかりにくそうなので、たとえば以下のケースの、Hoge.aspxから見た、foo.jpgの配置されているディレクトリ。
/HogeHogeApp/ …… アプリケーションルート img/ title_pic/ foo.jpg content/ hoge/ Hoge.aspx
絶対パスで書いちゃえばいいけれど、仮想ディレクトリ名の変更には弱くなります。これは頂けない*3。かといって、相対パスで「../../img/title_pic/foo.jpg」とか書くのも見辛いし、私は嫌です。これくらいならまだ目を瞑りたくなるかもしれませんが、深くなればなるほど、嫌度が増すこと請け合いです。
こういうときは、仮想ルートを表す「ルート演算子 (ティルダ [~]) 」を使って書いてしまうのが良いと思います。
System.Web配下のサーバコントロールはたいてい勝手に解決してくれる気がします*4し、自力でなんらかのATtributeとかに書き出す際にもVirtualPathUtility.ToAbsoluteメソッド等を使って解決することができます。
これらを念頭におけば、それなりに
・仮想ディレクトリ名に依存しない
・物理パスの変更に強い
・記述方法に統一性、法則性がある
作りにできのではないかと思っています。
また、VirtualPathUtilityクラは、こういった仮想パス環境でのパスに関するユーティリティメソッドがいくつかあります。名前の通りですが。暇な時にざっと見ておくと便利かも。
……なんか、ぐだぐだになっちゃった感が強いので、後日再整理して書きなおすかもしれません。が、要は、
・迂闊に絶対パスで書くな
・ルート演算子を活用しよう
・記述箇所は必然性と統一性を持って
ってところなねですよね。全然かけてないですね。けど、せっかく書いたのでもったいないからポストしちゃう。