kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

月別アーカイブ: 5月 2013

Doc of Code のコードから:プロバイダパターンの実装インフラ


Doc of Code では ASP.NET のプロバイダパターンを全面的に使って、モジュール間の依存性を解決しています。

というわけで、プロバイダパターンを実装する上での内部インフラストラクチャがあったりしまして、これの軽い紹介です。

プロバイダパターンとは

ASP.NET / ADO.NET その他で色々と使われているアプリケーション内でのモジュール差し込みメカニズムと言って良いでしょうかね、APIを定義するモジュールと、実装モジュールを分ける事ができ、実装は普通に web.config とかで指定する事ができます。

素の ASP.NET 上で実装する場合には ProviderBase 派生クラスを作ります。必要であれば Initialize をオーバライドし、初期化パラメーターを受け取ります。そして ProviderSettings を保持する構成セクションを作り、 ProviderCollection を初期化てプロバイダをインスタンス化します。

そのまま普通に実装するなら、以下の様なコードになります。

public abstract class HogeProvider : ProviderBase
{
    private static Lazy<ProviderCollection> _providers = new Lazy<ProviderCollection>( InitProviderCollection );
    public static ProviderCollection Providers
    {
        get { return _providers.Value; }
    }
    private static ProviderCollection InitProviderCollection()
    {
        var collection = new ProviderCollection();
        ProvidersHelper.InstantiateProviders(
            ConfigSettings.Providers,
            collection,
            typeof(HogeProvider));
        return collection;
    }
    private static Lazy<HogeSettings> _settings = new Lazy<HogeSettings>( InitSettings );
    public HogeSettings ConfigSettings
    {
        return _settings.Value;
    }
    private static HogeSettings InitSettings()
    {
        return (HogeSettings) ConfigurationManager.GetSection(“hogeProviders”);
    }
    // API’s goes here
}

public class HogeSettings : ConfigurationSection
{
    [ConfigurationProperty(“providers”, Options = ConfigurationPropertyOptions.IsRequired)]
    public ProviderSettingsCollection Providers
    {
        get { return (ProviderSettingsCollection)this[“providers”]; }
    }
    // Settings properties goes here
}

Lazy の initializer をメソッドバインドして遅延初期化とかまぁ普通にやればこうなるかなぐらいの実装ですね。

実際には defaultProvider プロパティの追加とか、それを元に Provider プロパティを出してとかやるともうちょっと伸びます。

んで、一個実装するぐらいならなんてことはない話なんですが、5つも6つも実装するとなるとぶっちゃけてお約束コードが長くて鬱になるという類です。

Initialize のオーバーライド

プロバイダ作ったら大抵なんかしら初期化のオーバーライドが必要になります。

このオーバライドをすると configs として渡される NameValueCollection をグチグチといじる必要があります。

これがコードでやると結構うっとおしいのです。

_configParam=configs[“parameterName”]==null ? defaultValue : int.Parse( configs[“parameterName”] );

をべたべた並べてやると int.Parse が失敗すれば例外吹っ飛んでいき、ダメならエラーになってくれるし、設定しくってる奴が悪いといえばそうなんだけど、ちゃんと何の設定違ってるとか例外の詳細カスタムしようとすると try {} catch { throw … } で括ってを並べる事になります。

世の中データバインディングにアノテーションだろって言ってもまぁアレです。

Doc<Code> のプロバイダ実装インフラを流用する

https://kazuktnd.wordpress.com/docofcode/ でライセンスに明記されてる通りで、「それ以外のソースコードおよびバイナリは自由に実行、解析、改変、再配布、他の製品への流用等ができます。 」の「他の製品への流用等ができます。」の通りです。アセンブリ一個取りだして流用も許諾されていますので、好きにしてください。って事です。

bin に展開されている CustomProviderInfrastructure.dll を取り出し、参照設定します。

using CustomProviderInfrastructure; // NameValueCollection の Bind 拡張メソッドに必要

public class HogeProvider: CustomProviderBase<HogeProvider, HogeSettings >
{
    [InitializeParameterRequired]
    private int _timeout;

    public override void Initialize(string name, NameValueCollection config)
    {
        config.Bind( this, self=>self._timeout, int.Parse );
    }
}
[DefaultConfigurationSectionName(“hogeSettings”)]
public class HogeSettings : CustomProviderSettingsBase
{
}

InitializeParameterRequired で _timeout が修飾されていますので、この場合 timeout は省略できません。

config.Bind は式木のプロパティ名/フィールド名から取得する要素名を決定します。デフォルトで先頭の _ は削られ、結果は camelCaseName にされます。(先頭が大文字なら小文字になる)、フィールドないしパラメータに InitializeParameterName 属性でパラメータ名が指定されれば、その名前で NameValueCollection の要素を取りだします。

変換時に例外が出たりすると、ConfigurationErrorsException にパラメータ名等付いた例外を再送出するので、例外を綺麗にするとかの為にコードを特に足す必要はありません。

Bind には省略可能パラメータでデフォルト値が指定できますので、InitializeParameterRequiredAttribute を付けない場合にはデフォルト値も指定してください。

以上で前述のコードでの ProviderCollection の絡みや ConfigurationSection の派生もすべて済みます。

web.config には DefaultConfigurationSectionName で指定されるセクションを記述します。

<hogeSettings defaultProvider=”default” >
    <providers>
        <add name=”default” type=”HogeProvider, HogeProviderServices” />
    </providers>
</hogeSettings>

あと、web.config の configSections にセクションの記述が要りますがそんなもん当然すぎるので割愛。

まとめ

好きに使って良いよなアセンブリも入ってますので、遊んであげてください。

Doc<Code> というツールをリリースします


技術特化 blog なんで、滅多にやらない事なのですが、宣伝させてください。

宣伝かよ

はい、宣伝です。軽く失注続きで本気にこのままだと路頭に迷う事態なので一人ならどうという事は無いのですが嫁子供居る身なので必死なわけで、個人で作って使ってるツールの類に値段を付けて並べてみようかとの第1弾です。

タダで手に入る物で全部回して主義の方はこの先読んでも意味ないのでお帰り下さい。

Doc<Code> “doc of code” とは

.NET Framework アセンブリとその XML ドキュメントファイルを投げ込むとドキュメントを閲覧できる ASP.NET アプリケーションです。

同様なXMLドキュメントコメントの整形を行うアプリケーションとしては Sandcastle があります。Sandcastle プロジェクト自体は現状 Sandcastle Help File Builder でメンテナンスされているようですが、若干プロジェクトに不安定要素があり、ちょっとややこしい事になっているようです。

Sandcastle との違いは、あちらはビルドプロセスでのバッチ処理でHTML、およびヘルプファイルを作成する枠組みですが、Doc<Code> では、ASP.NET アプリケーションとして要求に応じてアセンブリを随時解析してXMLドキュメントと結合表示する仕組みですので、簡単に言えばビルド時に XMLドキュメントファイルの生成以上の追加処理を必要としません。結果的にビルド時間に対するインパクトを殆ど0にする事ができます。

要するにスロービルドの原因である、誰も見ないようなドキュメント生成を、オンライン解析変換処理にする事で開発スループットをより伸ばせるツールという事です。

インストールに必要な物は?インストールの手順は?

IIS 7.5 以降(Express でも可) 、 .NET Framework 4.5が必要です。 VS2012で普通に開発してる環境なら入っている物ですね。

DocOfCode の紹介とインストールデモ等

普通に NuGet からパッケージ入れるだけで入ります。

どういう使い方が想定されていますか?

普通に開発現場でドキュメントを見る用途、および、オープンソースな物を作ってる人がAPIリファレンスをインターネット上に公開するとかに使って良い様に作っています。

インターネット公開サイトに使う場合に、アセンブリを勝手にアップロードされないようにしたい等は、AssemblyController とその関連Viewそのものを外してしまって下さい。(この場合、アップロードはファイルを直接 App_Data に配置する事でもできますし、アセンブリの一覧管理はプロバイダになっていますので、プロバイダを実装する事で管理方法そのものを変える事ができます)

表示のデザイン等に関わる View および、それに当たる css 等はすべてパッケージ内にありますので、NuGet パッケージを取りこんだうえで修正してもらえれば幾らでもカスタマイズできると思います。

使い方

単純に動かすと能書きの書かれた Index ページの上部にアセンブリと設定のリンクがありますので、アセンブリを選んでもらうと、アセンブリをアップロードするためのページへ遷移します。

アップロード時に指定するタイトルはそのままアセンブリの情報ページのパスとなります、アセンブリ内の名前空間や型、メンバ情報はすべてその配下でアクセスされます。

アップロード済みのファイルと同じタイトルでアップロードを行うと、アセンブリファイルおよび、ドキュメントコメントのXMLファイルの入れ替えになります。

後は飛び回って好きにドキュメントをみて下さいという事です。 Code Contracts で XML ドキュメントコメントを出すようにしておけばそれなりに契約内容も表示されます。

名前空間に関するドキュメントコメント

ドキュメンテーションコメントのXML仕様的には N: で名前空間についてのドキュメントを置けることになっているため、このドキュメントタグが付いた要素があれば表示されるようになっています。また、 NDoc / sandcastle と同様に NamspaceDoc という型に対するドキュメントがあれば、名前空間のドキュメントとして表示されるようにしています。

ドキュメンテーションコメント内のタグ

推奨されるドキュメンテーションコメントタグの殆どはレンダリングできるようになっています。

また、各種HTMLタグはそのままHTMLタグとして表示するようになっていますので、HTMLの aタグをドキュメントコメントに記述しておけばリンクになりますし、imgタグを記述しておけばイメージも表示されます。(当然に img はどっか Web サーバにホストしないと見れませんと思いますけど)

高度な使い方とスケーラビリティ等

Doc<Code> は解析対象アセンブリとドキュメントをすべてメモリ上に読み込んで処理を行う為、スケーラビリティのネックは基本的にはメモリ容量という事になります。また、閲覧されるドキュメントをできるだけ高速にレンダリングしたいため、メモリに読み込んだモジュールやドキュメントをできる限りメモリ保持しようとしますのでアセンブリを登録すれば登録したほどメモリを使います。開発中のドキュメント参照支援等で .NET Framework のすべてのアセンブリとドキュメントを閲覧できるサーバを立てるとかはメモリがきついとかが根本的なネックになるでしょう。

結果的に別々のサーバに、このアセンブリはこっちのサーバ、このアセンブリはこっちのサーバといった形で配置する事になります。これらのサーバ間でドキュメントのリンクを通す事がサーバリンク機能で実現できます。

App_Data/LinkedServers.txt ファイルにリンクされるサーバ名:ポート番号を記述しておくと、該当サーバに存在しないアセンブリへのドキュメントのリンクが自動で解決されます。たとえば A サーバに mscorlib を配置して、Bサーバに自前のライブラリのドキュメントを配置し、BサーバのLinkedServers.txt にAサーバを記述しておくと、Bサーバでドキュメントをレンダリングする場合に String 等 mscorlib 由来の型は A サーバへのリンクとしてレンダリングされます。

Doc<Code>サーバ間は Web API で保持しているアセンブリの問い合わせを行っていますので、Aサーバへのアセンブリの追加などを行った時にBサーバ側は特に何もしないでも自動で反映されます。

注:サーバリンクを使ってサーバ間のリンクをする場合、それぞれのサーバにライセンスが必要です。

解析系の独自拡張の実装

Microsoft CCI に関する知識は必須ですが、アセンブリ、名前空間、型、メンバのそれぞれについて、追加で解析処理を行うロジックを入れるフックポイントが用意されています。

これにより、メンバメソッドのサイクロマチック複雑度を計算するロジックを追加すると、メンバの情報取得時にそれが呼び出され、ロジックが出力した結果は View まで引き渡しされます。

今後の開発ロードマップ

現状では各種メタデータおよび、ドキュメントコメントの表示側に注力している形になっていますが、将来的にはドキュメントコメントの入力、編集ができるようにする事を計画しています。

というのも、綺麗に見えるようになれば欲が出るので、使用例とかの Example をドキュメントコメントに色々書きたくなったりするのですが、それをやるにはコメントの中にコードを書かなければならないし、コメントの中に書かれたコードの中で XML 的な制約で < や > をそのまま書けないし色々と痛々しい思いをしなければなりません。

ドキュメントを充実させたいという思いと、ソースをそんなに汚したくないという思いや色々な葛藤の結果としてドキュメントコメントを実際として機能的に捨てているという現場も多いでしょう。

その辺りに対して、ドキュメントとして入れたい物をソース外からドキュメントコメントXMLに出力できる様にするビルド時ツールとか色々絡めて、十分なドキュメントを書いてもコードファイルが汚れない、それを素早く見れて修正を入れたりとかを Web 上のツールとコード上の双方でできる様にするのを次のマイルストーンとして考えています。

お値段は?

インストールされるサーバにつき1か月300円をめどに設定しています。

販売サイトの方を準備中だったりしますが、 1ヵ月300円、3ヵ月900円、6ヵ月1600円、1年で3000円の4通りのラインナップの予定です。

販売方法としては PayPal でのクレジットカード決済で購入するとメールでライセンスファイルが送られてくるという形式、ライセンスファイルはサーバへ配置が必要です。

また、直接 kazuk.dll@kazuk.jp の方にメール頂ければ見積書、納品書等、会社関係で必要な物は出せる形ができます。

んでいつリリースなのよ

とりあえず、NuGet パッケージについては今月中には出します。販売サイトは月明け早々にも立ち上がり予定、実際のライセンス販売開始は来月10日からを予定しています。(このあたり、多少のずれはあるかもしれません)

NuGet でのパッケージングの注意事項諸々


NuGet で pack する時に失敗しそうな事を色々と見つけたので blog

web.config.transform が指定した通りには入らないっぽい

現象の発生する NuGet.exe

C:\Users\kazuk\Documents\DocOfCode\DocOfCode>..\.nuget\NuGet.exe
NuGet Version: 2.5.40416.9020

ASP.NET プロジェクトのディレクトリで nuget spec 叩いて nuspec をちょっと編集してパッケージを作れるようになりましたって状態で、web.config に関する変換を web.config.transform として作成

nuget pack を叩く

Successfully created package ‘C:\Users\kazuk\Documents\DocOfCode\DocOfCode\DocOfCode.1.0.0.33115.nupkg’.

んでNuget package explorer で中身見てみると自分が指定した web.config.transform じゃなくて、web.config の内容が入ってる。 Exclude で web.config 指定してもダメっぽい。

image

Nuget package explorer で狙った格好の web.config.transform を入れなおさないと現状だめっぽい。

web.config の中にセキュリティ的にヤバい事書いてると nuget 経由で漏れそうな気がするので注意、超注意

NuGet pack –IncludeReferencedProjects を不用意に使うと危険

なんでか知らんけど、依存プロジェクトに含まれる出力ディレクトリにコピーのついてないコンテンツまで nuget パッケージに入れられちゃう。

nuget pack –IncludeReferencedProjects を使ってパッケージ

Successfully created package ‘C:\Users\kazuk\Documents\DocOfCode\DocOfCode\DocOfCode.1.0.0.33115.nupkg’.

CreateRasKey.txt は依存プロジェクトのコンテンツなんだけど、パッケージに入っちゃう。

image

クラスライブラリプロジェクトの中に txt で作り中のメモとか置いて出力ディレクトリにコピーしてないから大丈夫だと思ってるとダダ漏れになる。(RSA秘密鍵とかそういうのをうっかりコンテンツで置いてて漏れそうになった罠)

ビルドオプションをちゃんと None にしないとダメよと。

まとめ

便利な物だけど、ファイルが表に出ていく物である以上は慎重に使ってあげないと、セキュリティ的にヤバいので、中身はちゃんと確認しましょう。