如何在AppDomains中将引用作为方法参数传递?

时间:2010-05-28 20:40:01

标签: c# .net remoting appdomain marshalbyrefobject

我一直在尝试使用以下代码(所有内容都在同一个程序集中定义):

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."
   }
}

}

我要做的是将在第一个AppDomain中创建的“A”实例的引用传递给子域,并让子域在第一个域上执行方法。在'B'代码的某些方面,我将调用'remoteObj.GetSomeData()'。必须这样做是因为'GetSomeData'方法中的'byte []'必须在第一个appdomain上“计算”。   我该怎么做才能避免异常,或者我该怎样做才能达到相同的效果呢?

3 个答案:

答案 0 :(得分:11)

实际的根本原因是您的dll是从两个不同的应用域中的不同位置加载的。这会导致.NET认为它们是不同的程序集,这当然意味着类型不同(即使它们具有相同的类名,命名空间等)。

Jeff的测试在单元测试框架中运行失败的原因是单元测试框架通常会创建将ShadowCopy设置为“true”的AppDomains。但是您手动创建的AppDomain默认为ShadowCopy =“false”。这将导致dll从不同的位置加载,这导致很好的“对象类型无法转换为目标类型”。错误。

更新:经过进一步测试后,它似乎确实归结为两个AppDomain之间的ApplicationBase不同。如果匹配,则上述方案有效。如果它们不同则不会(尽管我已经确认使用windbg将dll从同一目录加载到两个AppDomain中)另外,如果我在两个AppDomain中打开ShadowCopy =“true”,那么它会失败使用不同的消息:“System.InvalidCastException:Object必须实现IConvertible”。

更新2:进一步阅读让我相信它与Load Contexts有关。当您使用其中一个“From”方法(Assembly.LoadFrom或appDomain.CreateInstanceFromAndUnwrap)时,如果在其中一个正常加载路径(ApplicationBase或其中一个探测路径)中找到该程序集,那么它是否已加载到默认值中加载上下文。如果在那里找不到程序集,则将其加载到Load-From上下文中。因此,当两个AppDomain都具有匹配的ApplicationBase时,即使我们使用“From”方法,它们也会加载到各自的AppDomain的默认加载上下文中。但是当ApplicationBase不同时,一个AppDomain将在其默认加载上下文中具有程序集,而另一个AppDomain在其加载来自上下文中具有程序集。

答案 1 :(得分:2)

我可以复制这个问题,它似乎与TestDriven.net和/或xUnit.net有关。如果我运行C.Init()作为测试方法,我会得到相同的错误消息。但是,如果我从控制台应用程序运行C.Init(),我不会得到异常。

你是否看到同样的事情,从单元测试中运行C.Init()?

编辑:我也可以使用NUnit和TestDriven.net复制该问题。我也可以使用NUnit运行器而不是TestDriven.net复制错误。因此问题似乎与通过测试框架运行此代码有关,但我不确定原因。

答案 2 :(得分:0)

这是对@RussellMcClure的评论,但由于它是复杂的评论我发布这个作为答案:

我在ASP.NET应用程序中并且关闭卷影副本(这也可以解决问题)实际上不是一个选项,但我找到了以下解决方案:

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;
}

这将使用主app-domain的shadow-copy目录作为应用程序库,如果启用了shadow-copy,则将所有卷影复制的程序集添加到专用路径。

如果有人有更好的方法,请告诉我。