当类在另一个命名空间中时编组 .NET 对象

Posted

技术标签:

【中文标题】当类在另一个命名空间中时编组 .NET 对象【英文标题】:Marshalling .NET Object when class is in another namespace 【发布时间】:2011-10-25 13:45:16 【问题描述】:

我在 .NET Windows 服务中跨应用程序域编组对象时遇到问题。

我创建了两个应用程序,它们跨应用程序域编组一个对象并通过代理 MarshalByRefObject 运行代码

第一个应用程序是一个简单的概念证明,因为我没有很多跨应用程序域编组的经验。它包含一个项目,其中MarshalByRefObject 定义在与项目其余部分相同的命名空间中。这个应用程序运行良好,使用与我的其他应用程序几乎相同的代码。主要区别在于我在第二个应用程序中编组的类是在另一个命名空间中定义的。

另一个项目更复杂,它是一个包含多个项目的 Windows 服务。主 Windows 服务加载一个执行编组的库。 Marshal 目标类型的类类型是在另一个库中定义的,所以我使用完全限定的命名空间/类名。

我面临的问题是,当它到达下面代码的最后一行时,它会抛出异常:

无法从程序集 ProductNameService 中加载 CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler,其中产品名称是主要的 Windows 服务类。

代码:

AppDomain compilerDomain = null;
AppDomainSetup compilerDomainSetup;
CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler scriptCompiler;

...

// Setup a seperate AppDomain 
compilerDomainSetup = new AppDomainSetup();
exeAssembly = Assembly.GetEntryAssembly().FullName;
compilerDomainSetup.ApplicationBase = System.Environment.CurrentDirectory;

compilerDomainSetup.DisallowBindingRedirects = false;
compilerDomainSetup.DisallowCodeDownload = true;
compilerDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
compilerDomain = AppDomain.CreateDomain("LiveLinkCSScriptDomain", null, compilerDomainSetup);

// Create an instance of the MarshalByRefScriptCompiler in the other AppDomain
scriptCompiler = (CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler)compilerDomain.CreateInstanceAndUnwrap(exeAssembly, typeof(CompanyName.ProductGroup.BusinessObjects.ProductName.MarshalByRefScriptCompiler).FullName);

我已经对此异常进行了研究,我发现几乎所有内容都表明这是 DLL 版本控制的问题,但是我的 DLL 不在 GAC 中,并且没有安装其他版本。我正在使用 installutil 进行干净构建并安装服务。

我使用MSDN documentation 作为创建编组代码的指南

我想知道加载MarshalByRefScriptCompiler 是否存在问题,因为该类型在另一个库中。我可以在一个简单的 winforms 应用程序中创建一个MarshalByRefScriptCompiler,但我在我的 Windows 服务中遇到了异常。

任何提示或见解将不胜感激!

【问题讨论】:

【参考方案1】:

我应该可以帮助你。我最近花了很多时间 (How do I pass references as method parameters across AppDomains?) 处理不同的跨应用程序域编组问题。我的第一个建议是尝试使用 CreateInstanceFromAndUnwrap 而不是 CreateInstanceAndUnwrap

我也对这行有点警惕:

compilerDomainSetup.ApplicationBase = System.Environment.CurrentDirectory;

您的原始 AppDomain 是如何创建的?您是否托管在 IIS 中,在这种情况下您的原始 AppDomain 将使用 ShadowCopy?所有的dll都在一个文件夹中吗?

编辑:

总而言之,如果您的 compilerDomainSetup.ApplicationBase 设置为包含您的 dll 的目录并且您传入正确的第一个参数(例如 typeof(MarshalByRefScriptCompiler ).Assembly.FullName)。

或者,您可以使用 CreateInstanceFromAndUnwrap 并将包含程序集的位置(例如 typeof(MarshalByRefScriptCompiler).Assembly.Location)作为第一个参数传递。

【讨论】:

感谢您的回复。我将 ApplicationBase 更改为:compilerDomainSetup.ApplicationBase = @"C:\ServiceSchedulerDebug\Debug";,这是所有 DLL 所在的位置(都在一个文件夹中)。但是在异常中仍然显示它在 C:\Windows\System32 中查找。有什么想法吗? 听起来您的零钱几乎没有收到。您的 Windows 服务正在 C:\ServiceSchedulerDebug\Debug 文件夹中运行? 是的,我正在从命令行使用 installutil 进行调试构建和安装。该服务安装在 C:\ServiceScheduler\Debug 中(更改了目录)。 AppDomain.CurrentDomain.BaseDirectory 是 C:\ServiceScheduler\Debug。我仍在试图弄清楚为什么它在 C:\Windows\System32 中查找,我认为这是导致问题的原因。 您需要使用 CreateInstanceFromAndUnwrap 并传入 dll 的路径作为第一个参数:typeof(MarshalByRefScriptCompiler).Assembly.Location 通过windows运行一个服务,工作目录是windows\system目录。这就是您看到这种行为的原因。【参考方案2】:

作为一个起点,您可以尝试Process Monitor 来确定它试图从哪里加载您丢失的类型。很可能就像在错误的目录中查找程序集一样简单。

【讨论】:

以上是关于当类在另一个命名空间中时编组 .NET 对象的主要内容,如果未能解决你的问题,请参考以下文章

JAXB 编组:更改应声明命名空间的元素

具有动态类名的 PHP 命名空间

WCF 客户端,XML 命名空间前缀导致空对象

如何获取JAXB对象的命名空间

为啥结构必须与模板类在同一个命名空间中才能编译?

Java SoapMessage 添加空命名空间