AppDomains 和 configSections
我们在 .NET 3.5 应用程序中使用 CSLA(一个相当古老的版本),并且我们为一些用户使用它的 NetRun 应用程序加载。对于那些不熟悉 NetRun 的人来说,NetRun.exe 基本上是一个安装在用户计算机上的应用程序“运行程序”(例如,安装到 c:\Program Files\NetRun\NetRun.exe)。用户只需启动 NetRun.exe 即可启动应用程序。
NetRun.exe 的作用如下:
(1) 创建一个新的 AppDomainSetup
Dim setupDomain As New AppDomainSetup()
setupDomain.ApplicationBase = CurrentDomainPath() ' this will be C:\Program Files\NetRun\
setupDomain.ConfigurationFile = "http://www.ourdomain.com/TheApp.xml" ' The app.config file is actually named TheApp.xml on the server because it has NetRun-specific config settings that don't belong in the standard TheApp.config that is used when the app is running directly from the server.
(2) 然后使用该 AppDomainSetup 创建一个新的 AppDomain
' create new application domain
Dim newDomain As AppDomain = AppDomain.CreateDomain("TheApp", Nothing, setupDomain)
(3) 然后 NetRun.Launcher(启动帮助程序类 - 主要用于常见的启动屏幕)通过以下方式在新的 AppDomain 中实例化:
' create launcher object in new appdomain
Dim launcher As Launcher = CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher)
(4) 然后,luncher 帮助程序类通过
' use launcher object from the new domain to launch the remote app in that appdomain
launcher.RunApp()
(5) 在新的 AppDomain 中运行应用程序,在完成所有闪屏内容之后,应用程序最终通过
Dim ass = Assembly.LoadFrom("http://www.ourdomain.com/TheApp.exe")
ass.EntryPoint.Invoke(ass.EntryPoint, Nothing)
以下方式启动 因此,回顾一下,实际的运行应用程序的 AppDomain 的 ApplicationBase 是 C:\Program Files\NetRun\,而实际应用程序的 EntryPoint 位于“
应用程序本身,除了 TheApp.exe 之外,还依赖于 ALibrary.dll。迄今为止,这对我们来说效果非常好。即使 ALibrary.dll 具有被拉入 TheApp.xml 文件中的配置条目,这些配置条目也始终可以正常读取(并将继续这样做)。
在即将发布的版本中,我们添加了一个新的自定义配置文件部分,该部分在 ALibrary.dll 中定义。我们将新部分添加到 TheApp.xml 和 TheApp.config 文件中
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.Applicat...">
<section name="TheApp.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
<section name="ALibrary.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
</sectionGroup>
<!-- NEW CONFIG SECTION -->
<section name="fixedPriceExceptionModel" type="ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection, ALibrary"/>
</configSections>
(为了节省本文中的空间,部分内容被删除)
但是,当我们尝试在 NetRun 中访问新部分时,在“正常”方式通过
CType(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) _
.GetSection("fixedPriceExceptionModel"), FixedPriceExceptionModelSection)
我们得到以下异常:
创建时发生错误 配置节处理程序 fixedPriceExceptionModel:无法 加载文件或程序集“ALibrary”或 它的依赖项之一。系统 找不到指定的文件。 (http://www.ourdomain.com/TheApp.xml 第 8 行)
做了一些研究,这是因为 .NET 配置库的程序集解析器正在所有常用位置查找 ALibrary.dll; ApplicationBase、特定子目录、.NET core 目录,然后是 GAC。不幸的是,在任何这些位置都找不到 ALibrary.dll。当此应用程序直接在服务器上运行而不使用 NetRun 时,应用程序不会引发异常并正确读取配置节。
我尝试的一件事是以不同的方式设置 AppDomainSetup,因为它的 ApplicationBase 将设置为 http://www.ourdomain。 com/,但随后对 CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher) 的调用会失败,因为 NetRun.exe 不在服务器上。
这可能需要理解很多内容,我希望我描述得足够好(并且不会让您感到厌烦)。也许没有简单的解决方案,但我已经用尽了我在该领域的有限知识,并希望这里的其他人可能有神奇的修复,使我能够在 NetRun 和本地运行应用程序时可靠地访问自定义配置部分。
We are using CSLA (a rather ancient version) in our .NET 3.5 applications, and we make use of it's NetRun application loading for some of our users. For those not familiar with NetRun, NetRun.exe is basically an application "runner" that is installed on user computers (e.g. to c:\Program Files\NetRun\NetRun.exe). The user simply starts the application by launching NetRun.exe.
What NetRun.exe does is the following:
(1) Creates a new AppDomainSetup
Dim setupDomain As New AppDomainSetup()
setupDomain.ApplicationBase = CurrentDomainPath() ' this will be C:\Program Files\NetRun\
setupDomain.ConfigurationFile = "http://www.ourdomain.com/TheApp.xml" ' The app.config file is actually named TheApp.xml on the server because it has NetRun-specific config settings that don't belong in the standard TheApp.config that is used when the app is running directly from the server.
(2) Then a new AppDomain is created with that AppDomainSetup
' create new application domain
Dim newDomain As AppDomain = AppDomain.CreateDomain("TheApp", Nothing, setupDomain)
(3) Then NetRun.Launcher (a launch helper class -- mainly used for a common splash screen) is instantiated within that new AppDomain via:
' create launcher object in new appdomain
Dim launcher As Launcher = CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher)
(4) Then the luncher helper class runs the app within the new AppDomain via
' use launcher object from the new domain to launch the remote app in that appdomain
launcher.RunApp()
(5) Which, after all the splash-screen stuff, the application is finally launched via
Dim ass = Assembly.LoadFrom("http://www.ourdomain.com/TheApp.exe")
ass.EntryPoint.Invoke(ass.EntryPoint, Nothing)
So, to recap, the actual running application's AppDomain's ApplicationBase is C:\Program Files\NetRun\, while the EntryPoint to the actual app is found in "http://www.ourdomain.com/TheApp.exe". So far, so good.
The application itself, in addition to TheApp.exe, also as a dependency on ALibrary.dll. To date, this has worked wonderful for us. Even when ALibrary.dll has config entries that were pulled into TheApp.xml file, those config entries have always been read fine (and continue to do so).
With our upcoming release, we added a new custom config file section, which is defined in ALibrary.dll. We added the new section into TheApp.xml and TheApp.config files
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.Applicat...">
<section name="TheApp.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
<section name="ALibrary.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
</sectionGroup>
<!-- NEW CONFIG SECTION -->
<section name="fixedPriceExceptionModel" type="ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection, ALibrary"/>
</configSections>
(parts were ...'ed out to conserve space in this post)
However, when we try to access the new section in a NetRun launched TheApp.exe in the "normal" way via
CType(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) _
.GetSection("fixedPriceExceptionModel"), FixedPriceExceptionModelSection)
We get the following exception:
An error occurred creating the
configuration section handler for
fixedPriceExceptionModel: Could not
load file or assembly 'ALibrary' or
one of its dependencies. The system
cannot find the file specified.
(http://www.ourdomain.com/TheApp.xml
line 8)
Doing some research, this is due to the fact that the assembly resolver for the .NET configuration libraries are looking for ALibrary.dll in all the usual places; ApplicationBase, specific sub directories, .NET core directories, and then the GAC. Unfortunatly, ALibrary.dll is never going to be found in any of those locations. When this application is run directly on the server, without NetRun, the application does not throw the exception and properly reads the configuration section.
One thing I tried was to set the AppDomainSetup differently, in that its ApplicationBase would be set to http://www.ourdomain.com/, but then the subsequent call to CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher) bombs because NetRun.exe is not on the server.
This is probably a lot to take in and I hope I described it well enough (and didn't bore you). Maybe there is no easy solution, but I've exhausted my limited knowledge of this area and hope someone else here on SO might have the magic fix, allowing me to reliably access custom config sections while running the application in both NetRun and local.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
据我了解这个问题,问题是代码是在客户端计算机上运行的,而没有真正(物理)存在程序集,因为它们是从服务器加载的。
知道了这一点,您有两个选择:
1. 努力寻找现有代码的解决方案;)
2. 不使用自定义配置节处理程序。
就我个人而言,我会选择#2。
不要使用 ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection 作为配置节处理程序(这会导致异常,因为在客户端上找不到此类型),而是像其他部分一样使用默认的 System.Configuration.ClientSettingsSection - 这是 . NET Framework,所以在客户端上没有问题。之后,为所需的配置类创建一个工厂(如果您确实需要强类型),它将逐步读取配置并创建您的FixedPriceExceptionModel对象,该对象填充数据并可供任何需要它的人使用。
如果我误解了这个问题,请告诉我。
As far as I understood the problem, the thing is that code is run on client machine without real (phisical) presence of the assemblies, as they are loaded from the server.
Knowing it, you have two options:
1. Die hard trying to find a solution with existing code ;)
2. Not use a custom configuration section handler.
Personally, I'd go with #2.
Instead of using ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection as your config section handler (which causes exception, as this type cannot be found on the client), use the default System.Configuration.ClientSettingsSection as other sections do - this is part of the .NET Framework, so no problem here on the client. After that, create a factory for the needed configuration class (if you really need that strongly typed), that would read configuration step-by-step and create your FixedPriceExceptionModel object, filled with data and available to use by anything needing it.
If I misunderstood the problem, let me know.
我意识到这是对已经回答的问题的答复,但我已经设法使用 代码库 元素。
示例(如果需要,您也可以使用 file:/// url 形式):
另请参阅:
指定程序集的位置
运行时如何定位组件
I realise this is very late reply to an already answered question but I've managed to pull this off using the codebase element.
Example (you can also use file:/// url form if you need to):
See Also:
Specifying an Assembly's Location
How the Runtime Locates Assemblies
我认为这不会像你所介绍的那样起作用。
这行代码之所以有效,是因为
LoadFrom
是一个神奇的方法,可以在特定位置查找程序集及其任何依赖项。 TheApp.exe 及其 DLL 从这个神奇的 LoadFrom 上下文中加载:但是,配置节程序集加载失败,因为加载它的 .NET 框架代码(大概)使用普通的
Assembly.Load
调用,它与原始LoadFrom
调用断开连接,并且不了解 www.ourdomain.com。有多种方法可以将其他目录添加到 .NET 程序集搜索路径中,但这些都是相对于应用程序域基目录而言的。正如您所指出的,让
Assembly.Load
查找 www.ourdomain.com 的唯一其他方法是更改应用程序域基目录。在这种情况下,将 NetRun.exe 部署到 Web 服务器上是不是很糟糕?I don't think this will ever work as you've presented it.
This line of code works, because
LoadFrom
is a magic method that looks in a specific location for the assembly and any of its dependencies. TheApp.exe and its DLLs get loaded from this magic LoadFrom context:However, the config section assembly load fails, because the .NET framework code that loads it is (presumably) using a plain
Assembly.Load
call, which is disconnected form the originalLoadFrom
call and has no knowledge of www.ourdomain.com. There are various ways of getting additional directories into the .NET assembly search path, but these are all taken relative to the app domain base directory.As you've pointed out, the only other way to have
Assembly.Load
find out about www.ourdomain.com is to alter the app domain base directory. In which case, is it so bad to deploy NetRun.exe onto the web server?