在运行时修改外部 DLL 使用的应用程序设置
Modify app settings used by external DLL at runtime
我正在尝试构建一个简单的客户端来测试内部身份验证服务。主要思想是只提供一个简单的测试工具,以确保该服务在每个环境中都可调用(并成功验证)。
在调用此服务的任何客户端应用程序中,需要在 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,并将返回的身份验证令牌添加到提供的 [=15] 中的 header =].
这是调用此服务的范围,因为调用此服务时 DLL 会读取来自 App.config
的目标环境 URL。 DLL 中的这些方法对我来说基本上是一个黑盒,因为我无法修改 URL 的检索方式。
到目前为止,我已尝试直接和通过 ConfigurationManager
访问此设置,但两种方式都显示零设置。在弄乱了一段时间之后,我尝试创建多个 App.config
文件,每个环境一个,并尝试根据需要在运行时 re-load 它们(如本 post 中所述:).最后一个解决方案有点管用,但仅适用于第一次调用。对不同环境的任何连续调用都不会使用重新加载的配置中的 URL,仅使用首次调用服务之前加载的配置中的 URL。
有什么方法可以在运行时修改此设置,以便根据需要使用此工具调用不同的环境?
经过相当多的挖掘,我发现在我的情况下这实际上是不可能的。我将概述我发现的内容,因为它可能会对以后的人有所帮助。
首先,我找不到修改 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
的实例所需的任何方法。这使您能够卸载其他域 modify/reload app.config
,然后使用新的 app.config
重新加载其他域中的程序集。请注意,在上面的代码中,您必须将完整的类型名称作为 CreateInstanceAndUnwrap
的第二个参数传递,如下所示:. Also, you must unwrap the value returned by a call to CreateInstance
. CreateInstanceAndUnwrap
simply does this for you all in one shot. For more details on unwrapping, and why it is necessary: . Additionally, you may be wondering why we don't just create a new domain and unload the existing, default domain. The default domain cannot be unloaded, nor can any domain that is running in a thread that can't be stopped immediately. In these cases you will end up with a CannotUnloadAppDomainException
: https://msdn.microsoft.com/en-us/library/system.cannotunloadappdomainexception(v=vs.110).aspx
最后,这可能对您有用,但仅限于特定情况。为了使 CreateInstance
或 CreateInstanceAndUnwrap
正常工作,您尝试从程序集加载的 class 必须继承自 MarshalByRefObject
或标记为 Serializable
。如果这两种情况都不是(对我来说就是这样),您将无法创建一个实例来从另一个 AppDomain
调用。有关这两种情况之间差异的更多详细信息:
所以最后我的解决方案是简单地禁止用户在程序集加载后修改他们的环境设置。虽然这不是最好的用户体验(因为它需要重新启动应用程序),但在我的情况下这是实现的唯一方法,因为我无权访问程序集中 class 的代码。
我正在尝试构建一个简单的客户端来测试内部身份验证服务。主要思想是只提供一个简单的测试工具,以确保该服务在每个环境中都可调用(并成功验证)。
在调用此服务的任何客户端应用程序中,需要在 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,并将返回的身份验证令牌添加到提供的 [=15] 中的 header =].
这是调用此服务的范围,因为调用此服务时 DLL 会读取来自 App.config
的目标环境 URL。 DLL 中的这些方法对我来说基本上是一个黑盒,因为我无法修改 URL 的检索方式。
到目前为止,我已尝试直接和通过 ConfigurationManager
访问此设置,但两种方式都显示零设置。在弄乱了一段时间之后,我尝试创建多个 App.config
文件,每个环境一个,并尝试根据需要在运行时 re-load 它们(如本 post 中所述:).最后一个解决方案有点管用,但仅适用于第一次调用。对不同环境的任何连续调用都不会使用重新加载的配置中的 URL,仅使用首次调用服务之前加载的配置中的 URL。
有什么方法可以在运行时修改此设置,以便根据需要使用此工具调用不同的环境?
经过相当多的挖掘,我发现在我的情况下这实际上是不可能的。我将概述我发现的内容,因为它可能会对以后的人有所帮助。
首先,我找不到修改 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
的实例所需的任何方法。这使您能够卸载其他域 modify/reload app.config
,然后使用新的 app.config
重新加载其他域中的程序集。请注意,在上面的代码中,您必须将完整的类型名称作为 CreateInstanceAndUnwrap
的第二个参数传递,如下所示:. Also, you must unwrap the value returned by a call to CreateInstance
. CreateInstanceAndUnwrap
simply does this for you all in one shot. For more details on unwrapping, and why it is necessary: . Additionally, you may be wondering why we don't just create a new domain and unload the existing, default domain. The default domain cannot be unloaded, nor can any domain that is running in a thread that can't be stopped immediately. In these cases you will end up with a CannotUnloadAppDomainException
: https://msdn.microsoft.com/en-us/library/system.cannotunloadappdomainexception(v=vs.110).aspx
最后,这可能对您有用,但仅限于特定情况。为了使 CreateInstance
或 CreateInstanceAndUnwrap
正常工作,您尝试从程序集加载的 class 必须继承自 MarshalByRefObject
或标记为 Serializable
。如果这两种情况都不是(对我来说就是这样),您将无法创建一个实例来从另一个 AppDomain
调用。有关这两种情况之间差异的更多详细信息:
所以最后我的解决方案是简单地禁止用户在程序集加载后修改他们的环境设置。虽然这不是最好的用户体验(因为它需要重新启动应用程序),但在我的情况下这是实现的唯一方法,因为我无权访问程序集中 class 的代码。