4

Assembly.Load または Assembly.LoadFile を使用して、同じアセンブリの複数のバージョンを読み込むプロジェクトがあります。次に、Assembly.CreateInstance を使用して、その特定のアセンブリから型を作成します。

これは、私が作成しているタイプが別の依存アセンブリを参照するまでうまく機能します。この特定のアセンブリの要求をインターセプトして別のアセンブリをロードし、正しいバージョン (または、より適切にはプローブ パス) を提供して依存関係を探す方法が必要です。

これが必要なのは、Assembly.CreateInstance で作成しているアセンブリの v1 と v2 には、依存アセンブリの異なるバージョンも必要になることが多いためですが、v1 と v2 の両方が既定で同じディレクトリをプローブします。

AppDomain に対して一般的に行う方法のを見てきましたが、特定のルート アセンブリからのすべての解決を処理する方法でこれを行う必要があります。私が次のようなことをすると仮定します:

AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
{
    //Use args.RequestingAssembly to determine if this is v1 or v2 based on path or whatever
    //Load correct dependent assembly for args.RequestinAssembly
    Console.WriteLine(args.Name);
    return null;
};

これは、ターゲット アセンブリによってすぐに参照される依存関係に対しては機能する可能性がありますが、それらの依存関係が参照するアセンブリについてはどうでしょうか? v1 がそれ​​自体が DepDepv1 を参照する Depv1 を参照する場合、それを正しく検出できるように、これを知る必要があります。

その場合、何らかの形でこれを追跡する必要があると思いました。おそらく、カスタム アセンブリの証拠を追加することによって - 私はそれを機能させることができませんでしたが、実行時に追加できる「アセンブリ メタ データ」プロパティはないようです。

特定のディレクトリからすべての依存関係を読み込むように特定のアセンブリに指示できれば、はるかに簡単になります。

アップデート

AssemblyResolve イベントを使用して、RequestingAssembly のパスに基づいて依存アセンブリを読み込むことができましたが、これには欠陥のあるアプローチのようです。使用されている依存アセンブリのバージョンは、たまたま最初にロードされたバージョンに完全に依存しているようです。

例えば:

  1. v1 をロード
  2. v2 をロード
  3. 参照 v1 が Depv1 の負荷を引き起こす
  4. リファレンス v2 が Depv2 の負荷を引き起こす
  5. v1 のコードは Depv1 の型を使用します (Works)
  6. v2 のコードは Depv2 の型を使用します <-- Depv1 から型を取得するため失敗します!

この時点ではステップ 5 と 6 のみを推測していますが、 Depv1 と Depv2 がロードされていることがわかります。

4

2 に答える 2

3

結局のところ、これを機能させるための鍵は、Assembly.LoadFile を確実に使用することです。LoadFile は、.NET が既に読み込まれていると見なすアセンブリと一致する場合でも、アセンブリを読み込む唯一のメソッドです。codeprojectの記事からこれを発見しました。

2 つの異なるアセンブリをロードする必要がありましたが、どちらも同じ完全な名前 (つまり、「App.Test.Domain、Version=1.0.0.0、Culture=neutral、PublicKeyToken=null」) を持っていましたが、内容が異なっていたため、LoadFile が唯一の方法でした。これ。私の最初の試みでは、タイプ AssemblyName を受け入れる Load オーバーロードを使用しましたが、AssemblyName インスタンスで定義されたパスを無視し、代わりに既に読み込まれたタイプを返します。

既に読み込まれている他の型に関係なく、依存関係グラフ全体を特定の場所から強制的に読み込むには、AssemblyResolve イベントに登録します。

AppDomain.CurrentDomain.AssemblyResolve += ResolveDependentAssembly;

そして、必ず LoadFile を使用して依存関係をロードしてください。

private Assembly ResolveDependentAssembly(object sender, ResolveEventArgs args)
{
    var requestingAssemblyLocation = args.RequestingAssembly.Location;

    if (thePathMatchesSomeRuleSoIKnowThisIsWhatIWantToIntercept)
    {
        var assemblyName = new AssemblyName(args.Name);
        string targetPath = Path.Combine(Path.GetDirectoryName(requestingAssemblyLocation), string.Format("{0}.dll", assemblyName.Name));
        assemblyName.CodeBase = targetPath; //This alone won't force the assembly to load from here!

        //We have to use LoadFile here, otherwise we won't load a differing
        //version, regardless of the codebase because only LoadFile
        //will actually load a *new* assembly if it's at a different path
        //See: http://msdn.microsoft.com/en-us/library/b61s44e8(v=vs.110).aspx
        return Assembly.LoadFile(assemblyName.CodeBase);
    }

    return null;
}

はい、このコードは、ルート アセンブリに依存関係がある場合、それらがすべて同じパスにあることを前提としています。これは間違いなく制限ですが、非ローカル依存関係のヒントを簡単に追加できます。これも、これらの追加の依存関係の既に読み込まれたバージョンが機能しない場合にのみ問題になります。

最後に、アセンブリのバージョンが適切にインクリメントされていれば、これは必要ありません。Load 呼び出しは、既にロードされている Depv1 を要求 Depv2 と同じように扱いません。私の場合、これは、継続的な統合と展開のプロセスの一環として、喜んで対処するものではありませんでした。

于 2014-04-09T21:23:48.773 に答える
3

Assembly.LoadFrom(path);依存関係を自動的に解決するものを試してください。

于 2015-07-16T10:52:56.223 に答える