【问题标题】:Modify app settings used by external DLL at runtime在运行时修改外部 DLL 使用的应用程序设置
【发布时间】:2015-08-03 19:37:04
【问题描述】:

我正在尝试构建一个简单的客户端来测试内部身份验证服务。主要思想是只拥有一个简单的测试工具,以确保该服务在每个环境中都是可调用的(并成功验证)。
在调用此服务的任何客户端应用程序中,需要在App.config 中定义一些设置。主要使用的是AuthenticationService,其中包含一个值,该值是此远程身份验证服务的 URL,由客户端中包含的 DLL 读取。此 URL 对于每个环境(开发、质量保证、生产)都不同。

例如:

  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" >
      <section name="Authenticator.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" requirePermission="false"/>
      <section name="RemoteAuthenticator.TokenCache.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=12345" requirePermission="false" />
    </sectionGroup>
  </configSections>

  <applicationSettings>
    <RemoteAuthenticator.Properties.Settings>
      <setting name="AuthenticationService" serializeAs="String">
        <value>https://environment-url</value>
      </setting>
    </RemoteAuthenticator.TokenCache.Properties.Settings>
  </applicationSettings>

我遇到的问题是构建一个能够在不同环境中调用服务进行测试的客户端。当用户向不同的环境发出请求时,需要在运行时更改设置 AuthenticationService 中 URL 的值,但我一直找不到这样做的方法。

用于调用此服务的客户端 DLL 提供了以下方式来将身份验证令牌添加到 HttpClient

new AuthorizationHelper().AddAuthorization(httpClient);

来自 DLL 的这些方法调用服务(在客户端 App.config 中指定的 URL,并将返回的身份验证令牌添加到提供的 HttpClient 内的标头中。

这就是该服务被调用的程度,因为来自App.config 的目标环境URL 在调用该服务时被DLL 读取。 DLL 中的这些方法对我来说基本上是一个黑盒,因为我无法修改获取 URL 的方式。

到目前为止,我已尝试通过ConfigurationManager 直接访问此设置,但两种方式都显示零设置。在搞砸了一段时间后,我尝试创建多个 App.config 文件,每个环境一个,并尝试在运行时根据需要重新加载它们(如本文所述:https://stackoverflow.com/a/6151688/1428743)。最后一个解决方案有些工作,但仅适用于第一次通话。任何对不同环境的连续调用都不会使用重新加载的配置中的 URL,只使用在第一次调用服务之前加载的配置中的 URL。

有什么方法可以在运行时修改此设置,以便根据需要使用此工具调用不同的环境?

【问题讨论】:

  • 您能否阐明“用户向不同环境发出请求” 的含义?用户如何调用不同的环境?
  • 我只是在制作一个简单的 UI 来向不同的环境发出请求,例如每个不同的按钮。这只是一个用于测试的超级简单工具,以确保该服务在每个环境中都可以访问(并成功验证)。
  • 你是指用户的环境,还是服务器?我只是对“环境”的含义感到困惑,因为看起来您是在运行服务的环境的上下文中使用它,而不是在运行测试...
  • 是的,服务正在其中运行。该客户端将调用该服务,因为它存在于 Dev、QA 和 Prod 中,以确保它在每个环境中都能正常工作。客户端将仅在开发人员机器上本地运行,调用服务所在的每个环境。
  • 我稍微更新了 OP 以详细说明如何从客户端调用服务。

标签: c# .net asp.net-mvc configuration app-config


【解决方案1】:

经过大量挖掘,我发现这在我的情况下实际上是不可能的。我将概述我的发现,因为它可能对以后的人有所帮助。

首先,我找不到修改 app.config 中加载程序集使用的应用程序设置值的方法。我解决这个问题的方法是为我正在测试的每个环境创建一个不同的app.config 并动态加载它们。这可以使用此处概述的方法来实现:Change default app.config at runtime

其次,加载程序集后,如果不卸载该程序集,则无法更改它已读取的app.config。单独卸载程序集实际上是不可能的,所以必须卸载整个AppDomain。获得这种所需行为的最佳方法(据我所见)是创建您自己的AppDomain 并从该域内的程序集中创建您需要的对象的实例。

例如:

AppDomain newDomain = AppDomain.CreateDomain("newDomain", null, AppDomain.CurrentDomain.SetupInformation);
YourObject obj = newDomain.CreateInstanceAndUnwrap("Your.Assembly.Name", typeof(YourObject).FullName) as YourObject;
// Do stuff
AppDomain.Unload(newDomain);

这将允许您从newDomain 中调用YourObject 实例所需的任何方法。这为您带来的是卸载此其他域、修改/重新加载app.config,然后使用您的新app.config 在此其他域中重新加载程序集的能力。请注意,在上面的代码中,您必须将完整的类型名称作为CreateInstanceAndUnwrap 的第二个参数传递,如此处所述:https://stackoverflow.com/a/20918117/1428743。此外,您必须解开调用CreateInstance 返回的值。 CreateInstanceAndUnwrap 只需一次性为您完成此操作。有关展开的更多详细信息以及为什么需要展开:https://stackoverflow.com/a/13366528/1428743。此外,您可能想知道为什么我们不只是创建一个新域并卸载现有的默认域。无法卸载默认域,也无法卸载无法立即停止的线程中运行的任何域。在这些情况下,您最终会得到 CannotUnloadAppDomainException: https://msdn.microsoft.com/en-us/library/system.cannotunloadappdomainexception(v=vs.110).aspx

最后,这可能对您有用,但仅限于特定情况。为了使CreateInstanceCreateInstanceAndUnwrap 工作,您尝试从程序集中加载的类必须继承自MarshalByRefObject 或标记为Serializable。如果这两种情况都不是(就像我一样),您将无法创建一个实例来从另一个AppDomain 调用。关于这两种情况的区别的更多细节在这里:https://stackoverflow.com/a/7047153/1428743

所以最后我的解决方案是简单地禁止用户在加载程序集后修改他们的环境设置。虽然这不是最好的用户体验(因为它需要重新启动应用程序),但在我的情况下,这是实现的唯一方法,因为我无权访问程序集中的类的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    相关资源
    最近更新 更多