1

最初から、サードパーティ アプリケーションのログを解析して確認するためのユーティリティに取り組んでいます。これには、極端な柔軟性が明示的に要求されます。

それを達成するために、私はLINQを最大限に活用しています(そしておそらくそれを超えています:/ )。

ユーザーはユーティリティの UI を介して独自のクエリを作成することになっているため、クエリを動的にロードおよびアンロードできる必要があります。この目的のために、クエリの結果を返す単一のメソッド (文字通り「DoStuff」という名前) を持つダミー クラスを構築し (多くの場合 LINQ ベースですが、例外がある場合があります)、それらを (CSharpCodeProvider を介して) 一時的にコンパイルします。アセンブリを「使い捨て」の AppDomain にロードし、クエリを実行してから、AppDomain とアセンブリを削除します。

ここでアラームが発火します: LINQ + AppDomains = 問題が発生する可能性がたくさんあります。

さらに悪いことに、各ドメインで何が実行され、何がドメインの境界を越える (または越えようとしているか) かを確実に把握するのは非常に困難です。stackoverflow のナレッジ プールが、この問題の解決に役立つことを願っています。

したがって、詳細を詳しく説明します。ここに、私が持っているものの簡略版を示します。

メイン アセンブリ (ユーティリティの実際の .exe ファイル) では、ユーザーが実行することによってのみ "通常の方法" で読み込まれますが、次のようなものがあります。

public abstract class LogEntry { // Should this inherit from MarshalByRefObject?
    protected internal abstract void Parse(StreamReader input);
    ...
    /* There are literally dozens of classes that inherit from LogEntry, dealing with different kinds of entries, and
     *   defining an appropriate implementation of Parse().
     * The logic to choose the appropriate sub-class would be within LogData's constructor (see below).
     */
}
internal class LogData: IEnumerable<LogEntry> { // Should this inherit from MarshalByRefObject?
    private List<LogEntry> loadedData = new List<LogEntry>();
    public LogData(IEnumerable<string> LogFiles) { ... }
    /* The enumerator for this class does some heavy-dutty work to find the log files and load them "as needed":
     * Each time a new entry is retrieved from the logs, it's saved on loadedData just before returning it;
     * When the enumerator is reset and used again, it goes through loadedData until exhausting it, and only then
     * goes back into actually loading (and saving) new data.
     * I'm not including the code here because it is ugly, verbose, and probably irrelevant to the question.
     */
}

(一部のフォーム、定型的なスタートアップ コードなどは別として)。プログラムは、ユーザーにログ ファイルのコレクションを提供するように要求し (FileOpenDialogs、従来の "*" および "?" ワイルドカードを使用したファイル名パターンをサポートするテキスト フィールドなどを使用)、それらを使用して LogData の単一インスタンスを初期化します。 .

その後、クエリを実行する Doom's Day Button を含む、クエリを作成および微調整するための光沢のある GUI がユーザーに表示されます。そして、それが楽しみの始まりです。アイデアは、ユーザーのクエリを表すコードを生成し、それを次のようなものにラップすることです (ここでは "using" ステートメントとその他の部分を省略しました)。

public class whatever { // I'm thinking of making this static and saving on the constructor invokation work, but that's a side topic.
    public IEnumerable DoStuff(IEnumerable<LogEntry> Input) {
        // This will be filled with code created on-the-fly based on user input
        ...
    }
}

次に、これを CSharpCodeProvider にフィードして、(定義済みの構成可能な参照のリストを使用して) 一時アセンブリにコンパイルします。次に、アセンブリを "破棄" AppDomain にロードし、"whatever" のインスタンスを作成し、その DoStuff メソッドを呼び出し (LogData のインスタンスを渡します)、結果を取得し、ドメインをアンロードして、アセンブリを取り除きます。

さて、幕の下で何が起こっているのでしょうか?それが本題です^^。各ドメインで実行されているコードと、ドメイン間でやり取りされているデータを追跡するのはかなり困難です。いくつかの調査の後、私はいくつかの経験に基づいた推測を行っているので、誰かが私が正しいかどうかを教えてくれれば十分です:

1、LogData は MarshalByRefObject から継承する必要があるため、ユーザーのクエリ (DoStuff) に渡されると、列挙子は引き続き「既定の」アプリ ドメインで実行され、正常に動作します。

2,) 上記は、LogData インスタンスが AppDomain の境界を超えることはない (そのメンバーへの呼び出しは別の話です) ことを意味しますが、ログが DoStuff から繰り返されるときに、そのエントリ (LogEntry インスタンス) が行われます。それで、それらはシリアライズ可能である必要がありますか?逆シリアル化はまったく難しくありませんが、解析作業が2倍になるような気がします。代わりに MarshalByRefObject の子孫にすることはできますか?

3. DoStuff が返すものは何でも、参照ではなくコピーする必要があるため、「破棄」したものをアンロードした後もメインの AppDomain から引き続き使用できます。そのため、クエリが結果に含めることができる型はすべて、シリアル化可能である必要があり、決して MarshalByRefObject ではありません。

4. DoStuff が LINQ クエリを返したとしても ("return from entry in Input ..." のようなものを使用)、クエリの列挙子は "throw away" ドメインでのみ実行され、個々のアイテム (これは、上記のポイントで、これらはとにかくシリアライズ可能であるべきだとすでに述べました)実際にドメインの境界を越えます。

これらの結論は正しいですか?何か不足していますか?

ご回答ありがとうございます。

PS: 以下の Daniel のコメントに基づいて、プロジェクトのいくつかの側面についてもう少し詳しく説明する必要があると思います。

  1. 「クエリビルダー」はクエリを定義するための主要なメカニズムになることを目的としていますが、プログラムは、ビルダーだけでは不十分な場合にユーザーが生のコードを導入できるようにもします (また、プログラミングに精通していて、いくつかのクエリを入力することを好むユーザーのためにも)ドロップダウン、メニュー、およびその他のファンシーなものをナビゲートするためのコード; プロジェクトの対象ユーザーのかなりの部分である)。

  2. 言及しなかったもう 1 つの機能は、後で使用するためにクエリを保存できることです。C# コードで表されるクエリを使用すると、クエリを永続化するのは簡単です (コンパイル前は単なる文字列です)。式ツリー (またはそれに類似したもの) を永続化することは、すぐに大きなハードルになる可能性があります。

  3. CodeProviderクラスに飛び込むのはこれが初めてではありません。アセンブリを動的にロードします。LINQ でもありません。しかし、これほど深く掘り下げたのはおそらく初めてです。そして間違いなく、これらすべてをまとめようとするのは初めてです。私はこれらの各機能の可能性に精通しており、それらの相互作用に関するトリッキーな詳細が整理されれば、私が試みていることは比較的簡単になると確信しています. この質問はそれらの詳細についてのみです。それ以外はすべて処理できます。

4

0 に答える 0