none
クラスライブラリ内でASP.NET環境の検出 および セッションモードについて RRS feed

  • 質問

  • 現在、ビジネスロジックのクラスライブラリを作成しています。
    このモジュールを、WindowsFormsアプリからも、ASP.NET からも利用したいと
    考えたのですが、ASP.NETかどうかを検出する方法はあるでしょうか?

    例外の表示をローカルからの接続のみにしたり、エラー時に転送するページの設定も
    ありますが、やはり、エラー原因をResponse.Write したいと考えての質問です。
    WinFormsアプリの場合は、MsgBox,
    ASP.NET から呼ばれた場合は、Response.Write
    できればなぁと考えています。


    しかし、中さんのわんくま同盟さんのホームページで、セッション状態のモードに
    InProcを使用してはいけないというのを見て、設定してみた所、Session変数に
    DLL内のクラスをセットできなくなりました。
    (MarshallByRefからのオブジェクトはシリアライズできない旨のメッセージ)

    ポインタが無効になる事を考えれば、COMっぽい物が保存できなくなる事は
    理解できますが、これってかなりショックが大きいのですが、
    web.config を変えたり、binディレクトリを書き換える事は実運用中はない物として、
    メモリ使用量が一定量を超えた場合というのが気になります。
    アプリケーションプールの設定画面は、XPProSP2では確認できていませんが、
    これを設定すれば、再起動を抑止できるのでしょうか?

    ワーカプロセスのリサイクルに設定する時間が、application_startからの時間であれ
    ば、実運用上は、問題ないような気もします。
    その辺りも情報があればよろしくお願いします。

    ちなみに、アプリケーションが再起動するという事は、Page.IsPostback は False
    になると見て問題ないでしょうか?

    2006年5月13日 14:17

回答

  • ビジネスロジックやライブラリといったモジュールなら、なるべく、少なくともソースレベルでは環境には依存しない様にしたほうがよいでしょう。

    例えばエラー通知が必要なら、通知用のAPIだけを別ライブラリなどで用意しておき、ビジネスロジックなどからはそれを呼び出すだけにして、通知用のライブラリで振り分けるほうがよいでしょう。

    例えばTrace出力なんかの機能は、呼び出し元は出力先を意識する必要が無いい様になっています。具体的な出力先は設定ファイルなどで設定できるようにしておくことで、通知先は自由に設定できるようになります。

    単純に(細かい話は置いておいて)判定だけするなら、HttpContext.Currentがnullかどうかなど、ある程度確認できる方法はありますが、あまり推奨されるような方法では無いでしょう(構成ファイルなどで明示的に設定してそれを取得するほうがよい、という話もあります)。

    ※まあ多くの場合、実用上はHttpContextで調べられるとは思いますが。

     

    で、セッションのほうですが、いったい何を保存しようとしているんでしょう?
    セッションモードをInProcに制約してしまうのはできる限り避けた方がいいと思いますが…

    2006年5月13日 14:35

すべての返信

  • ビジネスロジックやライブラリといったモジュールなら、なるべく、少なくともソースレベルでは環境には依存しない様にしたほうがよいでしょう。

    例えばエラー通知が必要なら、通知用のAPIだけを別ライブラリなどで用意しておき、ビジネスロジックなどからはそれを呼び出すだけにして、通知用のライブラリで振り分けるほうがよいでしょう。

    例えばTrace出力なんかの機能は、呼び出し元は出力先を意識する必要が無いい様になっています。具体的な出力先は設定ファイルなどで設定できるようにしておくことで、通知先は自由に設定できるようになります。

    単純に(細かい話は置いておいて)判定だけするなら、HttpContext.Currentがnullかどうかなど、ある程度確認できる方法はありますが、あまり推奨されるような方法では無いでしょう(構成ファイルなどで明示的に設定してそれを取得するほうがよい、という話もあります)。

    ※まあ多くの場合、実用上はHttpContextで調べられるとは思いますが。

     

    で、セッションのほうですが、いったい何を保存しようとしているんでしょう?
    セッションモードをInProcに制約してしまうのはできる限り避けた方がいいと思いますが…

    2006年5月13日 14:35
  • >しかし、中さんのわんくま同盟さんのホームページで、セッション状態のモードに
    >InProcを使用してはいけないというのを見て、設定してみた所、Session変数に
    >DLL内のクラスをセットできなくなりました。
    >(MarshallByRefからのオブジェクトはシリアライズできない旨のメッセージ)

    見ていただきありがとうございます。

    http://naka.wankuma.com/site/column/aspnet/00012.htm

    おっしゃるようにInProc等はセッションを破棄しても問題ない場合に限って使う(キャッシュとか)べきで、その他のストレージを利用するときにはシリアライズできなければいけません。

    なちゃさんもおっしゃっているように、何故セッションにそんなものを入れなければいけないのかが問題でしょう。

    クライアントからのリクエストごとにPageオブジェクトも再生成されるように、セッションの中身も必要に応じて元の状態に戻せるようにシリアライズ/デシリアライズ出来るようにしたものしか入れられません。

    2006年5月13日 15:57
  • なちゃさん、回答ありがとうございます。
    HttpContext.Current の判定で行けそうです。

    そもそも、あまり考えてなかったのですが、 HttpResponseのインスタンスである、
    Page.Response その物が、 HttpContext.Current.Response から取ってくる必要が
    あった訳ですね・・・

    ですが、今度は、 Response.Write で詰まりました。(^^;
    Application_Start の中から Response.Write しても意味がないような・・・
    で、なちゃさんの言われるように、 Traceオブジェクトを使用するようにして見ましたが、
    今の所、上手くいきません。
    HELPにあるように、http://msdn2.microsoft.com/ja-jp/library/b0ectfxd.aspx
    web.config に、 system.diagnostics に Listner を追加してみましたが、トレースできず、
    bin/xxx.dll に対して、 bin/xxx.dll.config というファイルを、作成して、
    Listner を追加してみましたが、NGでした。
    これは、もう少し、試行錯誤してみようと思います。

    2006年5月13日 17:32
  • なちゃさん、中さん

    セッション変数に格納したいオブジェクトですが・・・
    簡単に言えば、ADO.NETのオブジェクト(DataSet,もしくはDataTable) です。
    実際には、それらを内包するビジネスオブジェクトクラスのインスタンスですが・・・

    業務システムを構築する場合、SQLServerへの負荷を下げる為に、
    一度呼んだデータや、マスタデータはキャッシュするケースがありますが、
    そういった場合です。

    試していませんが、DataTable のインスタンスもNGなんでしょうね。
    ページのロードの度に必要なデータを、DBもしくはXML等のファイルから
    読み込みなおさないといけないとなると、ASP.NETもあまり業務システム開発に
    向かないイメージになります。(少なくとも、私の中では)

    ですので、SessionState-Mode=InProc の場合に、
    Applicationプロセス再起動のケースが、
    実運用上、回避できればと考えていました。

    実運用上、回避は難しいでしょうか?
    また、実運用で、InProcはやはり避けるべきだという事でしょうか?

    よろしくお願いします。

    2006年5月13日 17:44
  • > 簡単に言えば、ADO.NETのオブジェクト(DataSet,もしくはDataTable) です。
    > 実際には、それらを内包するビジネスオブジェクトクラスのインスタンスですが・・・

    そのビジネスオブジェクトとはどういうものですか?
    DataSetやDataTableはもともとSerializableです。
    なんとなくビジネスオブジェクトをMarshalByRef派生にしているように思われますが…
    これはなんででしょう(エラーではこれが原因といわれてるような気がしますが)?
    あるいはビジネスオブジェクトそのものではなく、内部のデータだけセッションに入れるのでは駄目なんでしょうか?

    ビジネスオブジェクト自体をセッションに入れるのは、なんとなくしっくりきません。
    状態を持っているようなオブジェクトなんでしょうか?

    > ページのロードの度に必要なデータを、DBもしくはXML等のファイルから
    > 読み込みなおさないといけないとなると、ASP.NETもあまり業務システム開発に
    > 向かないイメージになります。(少なくとも、私の中では)

    セッションやらキャッシュやら、そういった場面で活用できる機能は用意されています。
    まずいろんな機能の仕組み、目的、条件など、正しい知識を身につけましょう。
    確認していないけどDataTableはセッションに入れられないとか、DataSetなどのオブジェクトを内包したビジネスオブジェクトをセッションに入れられなかったとか、みんな思い込み、もしくは正しく理解しないで使っているための誤った判断ですよね?

     

    2006年5月13日 19:18
  • > Application_Start の中から Response.Write しても意味がないような・・・

    これはちょっと意味がよくわかりません…

    > で、なちゃさんの言われるように、 Traceオブジェクトを使用するようにして見ましたが、
    > 今の所、上手くいきません。

    Traceは例で出しただけだったんですが…
    ※まあ用途としては別に間違ってはいないとは思うのでいいとは思いますが。

    > HELPにあるように、http://msdn2.microsoft.com/ja-jp/library/b0ectfxd.aspx
    > web.config に、 system.diagnostics に Listner を追加してみましたが、トレースできず、

    これはちょっと分かりませんね。参照ページのとおりにやってみても駄目ってことでしょうか?
    ※私もWebページのトレースへの出力はやったことが無いので、確認はできてません。

    設定を書き込むのは、Web.configであっています。~.dll.configとかではないです。

    2006年5月13日 19:27
  • >DataSetやDataTableはもともとSerializableです。
    >なんとなくビジネスオブジェクトをMarshalByRef派生にしているように思われますが…

    そうですか。Serializable属性をつければ良いんですね。
    メッセージの転記が正確ではありませんでした。正確には、
    シリアル化できないオブジェクトまたは MarshalByRef オブジェクトは許可されません。
    というメッセージでした。
    私の作成したClass は特に何からも派生させていません。
    で、Serializable属性を付けてみましたが、駄目でした。
    属性だけでなく、ISerialize も実装する必要があるという事でしょうか?
    メンバには、 DataTable, String, Boolean, Collection 等を持っています。
    (VB.NET です)。このCollectionがSerialize できないのかと思い、オブジェクトブラウザ
    やヘルプを見ましたが、属性の調べ方が分かりませんでした。

    >まずいろんな機能の仕組み、目的、条件など、正しい知識を身につけましょう。

    そうですね。一応、こちらに投稿する前には自分で、ヘルプやらWEBやらを検索して
    調べているんですが・・・確かに、シンプルなClassのインスタンスがNGという事から
    どんなクラスのインスタンスもNGなのかと早合点しましたが。
    また、マニュアルを隅から隅まで読んでいる訳ではない物で・・・

    2006年5月13日 21:34
  • ちょっと試した感じでは、DLL内からの出力が出来ていないようです。
    ですので、Response.Write でも行けるのかもしれません。

    DLL内のクラスのメソッドから、
    System.Diagnostics.Trace.Write としても上手く行かなかったので、
    直接、HttpContext.Current.Trace.Write としてみましたが、
    こちらもNGでした。
    このメソッド直後の、aspx.vb 内からの Trace.Write は行けました。
    また、MSDNのサンプルではビジネスオブジェクトは、App_Code 内にあり、
    ある意味、ASP.NETのセキュリティゾーン内というイメージで取れます。
    ですので、DLL内のモジュールからの、Trace出力や、Response.Writeが、
    機能していないというのが現状のようです。
    単にIISの設定なのか、これも属性なのか(、初歩的なミスなのか)、現状不明です。

    件名とは話が変わっているので別スレにした方が良いでしょうか?

    いずれにせよ、サンプルをベースにした最小構成からの検証をしてみる予定ですが・・・
    2006年5月13日 21:44
  • > 私の作成したClass は特に何からも派生させていません。
    あ、そうでしたか…

    > で、Serializable属性を付けてみましたが、駄目でした。
    > 属性だけでなく、ISerialize も実装する必要があるという事でしょうか?
    どんな風に駄目でしたか?
    同じくシリアライズできない云々でしょうか?

    > メンバには、 DataTable, String, Boolean, Collection 等を持っています。
    > (VB.NET です)。このCollectionがSerialize できないのかと思い、オブジェクトブラウザ
    > やヘルプを見ましたが、属性の調べ方が分かりませんでした。

    クラスがシリアライズできるかどうかは、一応ヘルプで分かります。そのクラスの定義で、Serializable属性がついているかを見ます。
    コレクション系のクラスや、その他フィールドやプロパティに何かを設定できるクラスの場合は、通常そのクラスに格納したオブジェクトもシリアライズできる必要があります。
    ※例えばコレクションに格納したオブジェクトもシリアライズできる必要がある、など。

    > そうですね。一応、こちらに投稿する前には自分で、ヘルプやらWEBやらを検索して
    > 調べているんですが・・・確かに、シンプルなClassのインスタンスがNGという事から
    > どんなクラスのインスタンスもNGなのかと早合点しましたが。
    > また、マニュアルを隅から隅まで読んでいる訳ではない物で・・・
    うーん、そりゃもちろんいきなりマニュアルを隅から隅まで読めとか、そんなのは無理なのは分かってるんですが、それよりも、ご自身で書いてらっしゃいますが、なんでもいきなり早合点しすぎです。

     

    2006年5月13日 23:06
  • 話の方向についていけないけど、DataSetはもちろんシリアル化可能です。

    Serializable属性を付ける=シリアル化可能であるというのは幻想です。シリアル化機構をつけなくてもシリアル化できる。もしくはISerializableを実装してきっちりシリアル化機構を持っているものについてSerializable属性を付けることが出来ます。

    簡単なサンプルで試さなきゃ

    2006年5月14日 1:39
  • そもそも、WindowsForm からも ASP.NET からも呼べるクラスライブラリと謳っているならば、そんな判断を安易にライブラリ内でしちゃいけないわけです。(もちろん、そうせざるを得ない場合もありますが)


    例外の表示をローカルからの接続のみにしたり、エラー時に転送するページの設定も
    ありますが、やはり、エラー原因をResponse.Write したいと考えての質問です。
    WinFormsアプリの場合は、MsgBox,
    ASP.NET から呼ばれた場合は、Response.Write
    できればなぁと考えています。

    エラーならば例外で返し、MessageBox でエラーを表示するか、Response.Write でエラーを表示するかは、クラスライブラリのクライアントが決める事です。

    シリアル化の話ですが、他の方も仰っていますように Serializable属性 をつけただけでシリアル化できるのは、そのクラスのメンバが「全て」シリアル化できる場合のみです。そうでない場合は、ISerializable を実装し、カスタムシリアルしないといけません。

     

    2006年5月14日 4:10
  • なちゃさん、中さん、囚人さん

    コメントありがとうございました。


    ・ASP.NET で、DLLからTrace.Write できなかった件
     結論としては、単に、私の操作ミス(VS2005IDEの不具合?or MSのトラップ)
     のようでした。
     WEBサイトのプロパティページからDLLに参照を追加すると、
     ソリューションエクスプローラのbin ディレクトリの下に当該ファイルが上がります。
     (この時の元パスを仮に c:\bin とします) ソリューションエクスプローラ内のファイルを
     右クリックから削除して、binフォルダを右クリックして参照の追加として、
     c:\src\xxx\bin\debug にあるファイルを指定します。
     これで実行すると、 c:\bin にある古いDLLがコピーし直されるという現象でした。
     DLLを結構変更した為なのか、必ずそういった動きをするのか分かりませんが、
     ルート名前空間とファイル名が同じであれば、
     参照情報を正しく更新してほしい物です。
     で、正しいファイルを入れればDLL内からも trace.write できました。

    ・Serializable属性の件
     この属性・実装で回避できそうなので、後は自分でゴリゴリやってみます。
     この属性・実装で、回避できることが分かってよかったです。

    ・当初の趣旨であった、クラスライブラリ内からのメッセージ
     ローカルIISの場合は、DLL内からのMsgBoxが効くので、HttpContext.current から
     動作環境を判断して、メッセージを変えようと思います。
     Trace.Write は動作検証まではOKでしたが、私の出したいメッセージから勘案すると、
     設定のオーバーヘッドが大きいので。
     (出したい相手はエンドユーザーではなく、社内開発者の為)

     以上、ありがとうございました。

    ・蛇足
     ちなみに、Trace/Debug, My.Log My.Application.Log などのListenerによる出力先
     変更機能は、ヘルプのななめ読みで一応しってました。

     あと、SessionState-Mode を StateServer にすると、
     VS2005IDEのWEBサイトメニューの一番下にある、ASP.NET構成という、
     web.config 編集の為のASP.NETユーティリティも動かなくなります。

    2006年5月14日 13:13
  • 追加です。
    Serialize できないのは、 System.Data.SqlClient.SqlClientFactory
    でした。

    DataSet, DataTableなどのキャッシュはSerializeできるとして、
    DBConnectionはできないと言う事ですね。(当然か?)

    コネクションレスの動作にする前提でも、オブジェクトはSession変数には入れれず、
    Application_Startの中で初期化しておく必要がありそうです・・・
    2006年5月14日 15:56
  • > コネクションレスの動作にする前提でも、オブジェクトはSession変数には入れれず、
    > Application_Startの中で初期化しておく必要がありそうです・・・

    いや、何度も書くようですが、なんでそんなものをセッションに入れたい(というかキャッシュしたい?)んでしょうか?
    Application_Startの中で初期化はいらんことではまりそうなので、止めておいたほうがいいです。

     

    2006年5月14日 16:06
  • 追加です。
    Serializable属性を付けたClassの中でも、シリアル化する必要のないメンバには、
    NonSerialized属性を付ければ、セッション状態モード:StateServer(or SQLServer)
    でも、Session変数にオブジェクトインスタンスを格納する事ができました。
    (私が付けたのは、DBProviderFactory及びDBConnectionです)

    なちゃさん

    そうですね。Session状態をSerializeするという事は、
    考えると、Application_Startも、Session_Startも走らないという事なのかも
    しれませんね。(試してませんが)

    どういった場合かですか・・・
    2TeerのC/S業務システムを、Webをフロントエンドにした3Teerシステムにする場合
    とでも言いましょうか・・・簡単に言えば、WEBのUIも実装しておきたい場合です。
    ですので、インターネット向けサイトの構築というより、イントラシステムです。
    また、2Teerの業務システムは、通常Connectionは毎回切断しないケースが多いと
    思います。(効率が悪いので)
    今回のケースでは、セッション状態モードをInProc を避けるとなると、
    DBConnectionを毎回切断する形でのシステム構築を強要される形になります。
    DataAdapter.Fill等、切断が切られていれば、Fillメソッド後にもまた切断してくれる
    ようですが、サーバーが複数に分かれる場合に、ConnectionStringを複数
    保持する必要があります。
    また、複数サーバーへの分散処理をカプセル化した場合、
    そういったオブジェクトその物をSession変数に入れておきたくなります。

    DB上で自社で、ユーザー管理を構築し、ログオン後に接続サーバーを分けたい
    といった場合、セッション状態モードInProcは2階層システムと同じイメージで、
    ASP.NETシステムを構築できます。

    2006年5月15日 2:06