8

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

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 上“计算”。我应该怎么做才能避免异常,或者我该怎么做才能达到相同的结果?

4

3 回答 3

11

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

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

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

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

于 2011-10-18T23:44:19.510 回答
2

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

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

编辑:我还可以使用 NUnit 和 TestDriven.net 复制问题。我还可以使用 NUnit 运行器而不是 TestDriven.net 来复制错误。所以这个问题似乎与通过测试框架运行这段代码有关,尽管我不知道为什么。

于 2010-05-28T21:09:52.170 回答
1

这是对@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;
}

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

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

于 2014-07-27T18:41:57.100 回答