ASP.NETのプラグインシステム。 アイデア開発







読者の皆さん、こんにちは。 この記事では、ASP.NET MVCで記述されたプロジェクト用のプラグインシステムの開発について説明します。 前回の記事で、パーツを個別のプラグインに分割できるシステムを作成する基本について説明しました。



読者から寄せられた多くの質問から、続編を書くようになりました。 カットの下で続けた。



エントリー



説明したシステムについて、Habrユーザーから多くの質問を受けました。 多くの人が私の記事を面白くて便利だと感じているのはとても楽しいことです。 ほとんどの場合、システムのさらなる開発について尋ねられます。 私の人生のいくつかの変化のために、私はこのトピックから少し離れなければなりませんでした。 しかし、このトピックへの関心を考慮して、今日は、実装の経験と、システムを操作する際に遭遇した問題を皆さんと共有しようと思います。



前の記事と同様に、これはすべての微妙な点を理解するのが難しいと感じる初心者向けに書かれています。 すべてを可能な限り簡単な言語で説明します(一部の場所では、経験豊富なプログラマーが単純化のためにトマトを投げ始めることがあります)。



最初のバージョンの問題と制限



前に説明したシステムには、大規模なプロジェクトで作業するときに特定の利点があります。 しかし、すべての場合と同様に、欠点もあります。 作業中のプロジェクトでシステムを実装しているときに、いくつかの不快な瞬間がありました。



  1. コンパイルされたプラグインのファイルはロード後にブロックされ、その場で更新することはできません。
  2. 古いバージョンがロードされないように、フォルダ内のプラグインのバージョンを常に監視する必要があります。
  3. プラグインを機能させるために、サードパーティのライブラリをルートプロジェクトに接続する必要があります。


これは、作業中に発生した問題の完全なリストではありません。 しかし、これらの問題は基本的なものであり、そのため、開発は非常に不愉快な行動になりかねません(私がそれらのためにどれだけの痛みを経験し、血の涙を流したか)。



問題のリストが定義され、それらを解決します。



プラグインロック



システムの最初のバージョンには、プラグインファイルのブロックという非常に大きな問題があります。 また、プラグインが「戦闘」プロジェクトに接続されている場合、この問題は非常に急激に発生しますが、その停止は非常に望ましくありません。 プラグインを更新するには、どういうわけかサイトを停止し、システムコンポーネントの更新バージョンをダウンロードする必要がありました。



この問題は、次の1行のコードに起因しています。



var currentAssambly = AppDomain.CurrentDomain.Load(assembly);
      
      





アプリケーションドメインの経験がない場合、ここで何が起こるかを説明しましょう。 始めるには、 MSDNのアプリケーションドメインクラスの定義を参照してください。

アプリケーションが実行されている分離環境であるアプリケーションドメインを表します。


より簡単に理解するために、ドメインは、アプリケーションが実行されているシステム上の別個のプロセスであると想像してください。 したがって、上記のコード行から、メインサイトのプロセスにプラグインファイルが「ロード」されていることがわかります。 その結果、アプリケーションドメインが存在する(またはサイトが起動される)限り、ファイルはロックされます。



ブロッキングの問題を解決するために、一般的に受け入れられている解決策がいくつかあります。



  1. プラグインファイルを独自のドメインにアップロードします。
  2. ドメインのオーバーロードされた.Loadメソッド(バイト[])を呼び出します。 このメソッドを使用すると、バイト配列から内容をコピーしてアセンブリを接続できます。
  3. アプリケーションドメインのシャドウコピーを有効にし、静的メソッドAssembly.Load()を使用してプラグインを読み込みます。


最初の方法は、プラグインと分離のために別々のプロセスを生成するという点で悪いです(実際、正しいドメイン設定では解決されますが、多くの追加コードを書く必要があります)、メインサイトとそのライブラリからダウンロードされたプラグイン。 したがって、使用しません。



この場合と最初の方法では、プラグインはライブラリから分離されているため、2番目の方法は機能しません。 はい。MSDN自体は、.Loadメソッド(バイト[])は補助的なものであり、静的Assembly.Load()メソッドを呼び出す方法がない場合にのみ使用されると述べています。 この場合、静的メソッドの使用を妨げるものは何もありません。



しかし、3番目の方法を採用します。 それは最小限のコードを必要とし、すでに書かれたプラグインマネージャーに簡単に埋め込むことができます。



このコードが追加されます。
 string cachePath = @"C:\Temp"; if (!System.IO.Directory.Exists(cachePath)) System.IO.Directory.CreateDirectory(cachePath, new System.Security.AccessControl.DirectorySecurity()); AppDomain.CurrentDomain.SetCachePath(cachePath); //Set shadowcopy to prevent locking plugins AppDomain.CurrentDomain.SetShadowCopyPath(AppDomain.CurrentDomain.BaseDirectory); AppDomain.CurrentDomain.SetShadowCopyFiles();
      
      







少し説明します。 シャドウコピーを使用する場合、アプリケーションドメインは、プラグイン(または他の接続されたライブラリ)をロードする前に、指定されたフォルダーにコピーを作成し、このコピーを使用してソースファイルを操作できます。



上記のコードでは、ダウンロードしたプラグインのコピーが保存されるフォルダーへのパスを指定しました。 フォルダーは書き込み可能である必要があることを忘れないでください。 そうしないと、起動時に例外が発生します。



プラグインアプリケーションをドメインに読み込んだ後、それを削除または置換することはすでに不可能です。 このため、プラグインファイルはその場で書き換えが可能ですが、再起動の必要性はなくなりません。



プラグインバージョンの追跡



この問題は、おそらく、限られた開発者のサークルでのみ発生します(このサークルが私だけに限定されていることは除外しません)が、以前は誤ってプラグインの更新バージョンを間違ったフォルダーにアップロードしたり、ファイル名を変更して2つのバージョンを一度に置いたりすることができました。



手の湾曲と詐欺なし。 一方、複数のバージョンのプラグインを一度にサーバーに保存できると便利です。 たとえば、新しいバージョンで重大なクラッシュが発生し、プログラマーが病気になった場合。 もちろん、状況は架空のもので幻想的ですが、あなたは決して知りません。 したがって、バージョントラッキングを追加します。 これは非常に簡単です。 すべてのプラグインファイルをリフレクションでパススルーして、バージョンを取得し、接続するプラグインのリストに追加します。 リストにそのようなプラグインが既にある場合、バージョン比較を実行します。 見つかったファイルのバージョンが古い場合は、置き換えます。



コード例
 ... IList<System.Reflection.AssemblyName> assemblies = new List<AssemblyName>(); foreach (var dll in libs) { ... //     if (!assemblies.Any(o => o.Name == dll.Name)) assemblies.Add(dll); //Replace assembly with higher version //  ,       else if (assemblies.Any(o => o.Name == dll.Name && o.Version < dll.Version)) { assemblies.Remove(assemblies.FirstOrDefault(o => o.Name == dll.Name)); assemblies.Add(dll); } ...
      
      







サードパーティライブラリの接続



この問題は、私に最も大きな痛みと苦しみをもたらしました。 システムに新しいプラグインが表示されない状況に何度も遭遇しました。 そして、デバッグのみが、接続されたライブラリの読み込みエラーを示しました。 そして、基本プロジェクトを詰まらせることは、一般的ではありません。 したがって、接続されたライブラリを折りたたむような種類のライブラリリポジトリを作成することが決定されました。 しかし、ここで注意を払う価値のあるニュアンスがあります。 プラグインの1つでライブラリバージョンを更新し、他のプラグインが古いバージョンである場合、エラーが発生します。 しかし、これはマインドフルネスの問題です。 頭の中でいくつかの更新の後、この機能は延期され、さらなる問題は消えます。



この場合、問題を解決するには、次のように設定(メインプロジェクト)の行に1つ追加するだけです。



  <probing privatePath="bin;plugins;plugins/SharedLibs" />
      
      





SharedLibsディレクトリを作成しました。 しかし、ご存知のように、名前は何でも構いません。 したがって、メインプロジェクトに、パラメーターで指定された場所でライブラリを探す必要があることを伝えます。 依存関係解決の残りの魔法は、.NETによって行われます。



あとがき



そこで、ASP.NET MVC上のサイト用のプラグインシステムを開発する2回目の反復を行いました。 システムの使用に問題を引き起こす主な問題は解決されました。 アイデアの開発のこの段階では、システムはすでに生産プロジェクトで使用されており、その実行可能性を示しています。



この記事は前の記事に追加されたものであるため、小さいことが判明しました。

現在、更新されたプラグインをその場で自動的に置き換える作業を行っています。 しかし、現在の実装は非常に不安定であり、投稿するのは下品です。



また、私に手紙を書いてくれたすべての人の興味を少なくとも少しでも満たすことを願っています。 ご質問にお答えします。



github.comのリポジトリ



PSフレンズ、あなたの建設的な批判は非常に重要です。 コメントで意見を述べてください。

PPS Timeは非常に短く、すべてのアイデアやアイデアを実装するには不十分です。 愛好家や興味のある人がいる場合は、githubでプロジェクトに参加してください。 システムを一緒に開発します。



All Articles