AppDomains全体のメソッドパラメーターとして参照を渡すにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/2932370

質問

私は次のコードを動作させようとしています(すべてが同じアセンブリで定義されています)。

namespace SomeApp{

public class A : MarshalByRefObject
{
   public byte[] GetSomeData() { // }
}

public class B : MarshalByRefObject
{
   private A remoteObj;

   public void SetA(A remoteObj)
   {
      this.remoteObj = remoteObj;
   }
}

public class C
{
   A someA = new A();
   public void Init()
   {
       AppDomain domain = AppDomain.CreateDomain("ChildDomain");
       string currentAssemblyPath = Assembly.GetExecutingAssembly().Location;
       B remoteB = domain.domain.CreateInstanceFromAndUnwrap(currentAssemblyPath,"SomeApp.B") as B;
       remoteB.SetA(someA); // this throws an ArgumentException "Object type cannot be converted to target type."
   }
}

}

私がやろうとしているのは、最初のアプリドメインで作成された「a」インスタンスの参照を子ドメインに渡し、子ドメインに最初のドメインでメソッドを実行することです。 「b」コードのある時点で、「remoteobj.getsomedata()」を呼び出します。これは、最初のAppDomainで「getSomedata」メソッドから「getSomedata」メソッドから「計算」する必要があるため、行う必要があります。例外を回避するためにどうすればよいですか、それとも同じ結果を達成するために何ができますか?

役に立ちましたか?

解決

この問題を複製できますが、testdriven.netおよび/またはxunit.netに関連しているようです。 c.init()をテスト方法として実行すると、同じエラーメッセージが表示されます。ただし、コンソールアプリケーションからc.init()を実行した場合、例外は得られません。

ユニットテストからc.init()を実行しているのと同じことを見ていますか?

編集:nunitとtestdriven.netを使用して問題を複製することもできます。また、testdriven.netの代わりにヌニットランナーを使用してエラーを複製することもできます。そのため、問題はテストフレームワークを介してこのコードを実行することに関連しているようですが、理由はわかりません。

他のヒント

実際の根本的な原因は、DLLが2つの異なるアプリドメインの異なる場所からロードされていたことです。これにより、.NETはそれらが異なるアセンブリであると考えるようになります。これはもちろん、タイプが異なることを意味します(同じクラス名、名前空間などがある場合でも)。

Jeffのテストがユニットテストフレームワークを実行したときに失敗した理由は、単位テストフレームワークが一般に「True」に設定されたShadowCopyを使用してAppDomainを作成するためです。しかし、手動で作成されたAppDomainは、デフォルトでShadowCopy = "False"になります。これにより、DLLはさまざまな場所からロードされ、「オブジェクトタイプをターゲットタイプに変換できません」につながります。エラー。

更新:さらにテストした後、2つのアプリドメイン間でアプリケーションベースが異なることにかかっているようです。それらが一致する場合、上記のシナリオは機能します。それらが違う場合はそうではありません(dllがWindbgを使用して同じディレクトリから両方のアプリドメインにロードされていることを確認しましたが)。また、両方のAppDomainsでShadowCopy = "true"をオンにすると、失敗します別のメッセージがあります:「System.InvalidCastException:オブジェクトはiconvertibleを実装する必要があります」。

update2:さらに読むことで、それが関連していると信じるようになります コンテキストをロードします. 。 「from」メソッド(Assembly.loadfrom、またはappdomain.createinstancefromwrap)のいずれかを使用する場合、アセンブリが通常のロードパス(アプリケーションベースまたはプロービングパスのいずれか)で見つかった場合、デフォルトにロードされますコンテキストをロードします。アセンブリがそこに見つからない場合、それはロードからのコンテキストにロードされます。したがって、両方のAppDomainが一致するアプリケーションベースを持っている場合、「From」メソッドを使用しても、どちらもそれぞれのAppDomainのデフォルトロードコンテキストにロードされます。ただし、アプリケーションベースが異なる場合、1つのAppDomainはデフォルトの負荷コンテキストにアセンブリを持ち、もう1つはアセンブリがロードからのコンテキストにあります。

これは@russellmcclureへのコメントですが、コメントのために複雑であるため、これを答えとして投稿します。

私はASP.NETアプリケーションの中にあり、Shadow-Copy(これも問題も解決する)をオフにすることは実際には選択肢ではありませんが、次の解決策を見つけました。

AppDomainSetup adSetup = new AppDomainSetup();
if (AppDomain.CurrentDomain.SetupInformation.ShadowCopyFiles == "true")
{
    var shadowCopyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    if (shadowCopyDir.Contains("assembly"))
        shadowCopyDir = shadowCopyDir.Substring(0, shadowCopyDir.LastIndexOf("assembly"));

    var privatePaths = new List<string>();
    foreach (var dll in Directory.GetFiles(AppDomain.CurrentDomain.SetupInformation.PrivateBinPath, "*.dll"))
    {
        var shadowPath = Directory.GetFiles(shadowCopyDir, Path.GetFileName(dll), SearchOption.AllDirectories).FirstOrDefault();
        if (!String.IsNullOrWhiteSpace(shadowPath))
            privatePaths.Add(Path.GetDirectoryName(shadowPath));
    }

    adSetup.ApplicationBase = shadowCopyDir;
    adSetup.PrivateBinPath = String.Join(";", privatePaths);
}
else
{
    adSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    adSetup.PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;
}

これにより、メインアプリドメインのShadow-Copyディレクトリをアプリケーションベースとして使用し、Shadow-Copyが有効になっている場合はすべてのShadow-Copiedアセンブリをプライベートパスに追加します。

誰かがこれを行うより良い方法があるなら、教えてください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top