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

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

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上“计算”。 我应该怎么做以避免exception,或者我可以做些什么来达到相同的结果?

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

你是否看到同样的事情,从unit testing中运行C.Init()?

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

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

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

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

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

这是对@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(); 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,则将所有卷影复制的程序集添加到专用路径。

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