我正在尝试构建一个简单的客户端来测试内部身份验证服务。主要思想是只需要一个简单的测试工具,以确保在每个环境中都可以调用此服务(并成功进行身份验证)。
在任何调用此服务的客户端应用程序中,需要在App.config
中定义一些设置。使用的主要版本是AuthenticationService
,其中包含一个值,该值是此远程身份验证服务的URL,由客户端中包含的DLL读取。此URL对于每个环境(Dev,QA,Prod)都不同。
例如:
<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
内的标头中。
这就是调用此服务的程度,因为在调用此DLL时DLL会读取App.config
的目标环境URL。 DLL中的这些方法对我来说基本上都是黑盒子,因为我无法修改URL的检索方式。
到目前为止,我已尝试直接通过ConfigurationManager
访问此设置,但两种方式都显示零设置。在讨论了一段时间后,我尝试创建多个App.config
文件,每个环境一个文件,并尝试根据需要在运行时重新加载它们(如本文所述:https://stackoverflow.com/a/6151688/1428743)。这最后的解决方案有点工作,但只适用于第一次调用。任何对不同环境的连续调用都不会使用重新加载的配置中的URL,只会使用第一次调用服务之前加载的配置中的URL。
有什么办法可以在运行时修改此设置,以便根据需要使用此工具调用不同的环境?
答案 0 :(得分:0)
经过一番挖掘,我发现在我的情况下这实际上是不可能的。我将概述我发现的内容,因为它可能会对某人有所帮助。
首先,我无法找到修改已加载程序集使用的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);
这将允许您从YourObject
内的newDomain
实例上调用所需的任何方法。这可以让您获得卸载此其他域,修改/重新加载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
最后,这可能对您有用,但仅限于特定情况。要使CreateInstance
或CreateInstanceAndUnwrap
起作用,您尝试从程序集加载的类必须从MarshalByRefObject
继承或标记为Serializable
。如果这两种情况都不是(就像我这样),您将无法创建一个要从另一个AppDomain
调用的实例。关于这两种情况之间差异的更多细节:https://stackoverflow.com/a/7047153/1428743
所以最后我的解决方案是简单地禁止用户在加载程序集后修改其环境设置。虽然这不是最好的用户体验(因为它需要重新启动应用程序),但这是在我的情况下实现的唯一方法,因为我无法访问程序集中类的代码。