从app.config运行时元素

时间:2016-11-21 14:05:33

标签: visual-studio asp.net-4.5 .net-4.6.2

我正在尝试在我的应用中使用新的长路径支持。为了使用它,在不强迫客户端在其机器上安装最新的.net 4.6.2版本的情况下,应该只将这些元素添加到他的app.config中(请参阅链接以获取更多信息https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/):< / p>

<startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
<runtime>
    <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false" />
</runtime>

当我在我的执行项目中使用它时,它可以很好地工作。问题出在我的测试项目中(使用Nunit)。我已经将app.config添加到我的测试项目中,就像我将它添加到执行项目中一样。

使用ConfigurationManager类我已经设法确保确实加载了app配置(简而言之:使用我能够在单元测试中检索的app设置)。

使用ConfigurationManager.GetSection(“runtime”),我甚至设法确保运行时元素已正确加载(_rawXml值与app.config中的相同)。

但是(!)由于某种原因,app config运行时元素不会影响UseLegacyPathHandling变量,因此我的所有长路径调用都会失败。

我猜这个问题在某种程度上与测试项目成为使用Nunit引擎加载的dll的事实有关,这是执行入口点。

我在另一个项目中面临完全相同的问题,这是一个由Office Word应用程序加载的DLL。我相信在这两种情况下问题都是相同的,并且源于项目不是执行切入点这一事实。

重要的是要理解我无法访问他们自己的执行(Word Office或Nunit),因此我无法自己配置它们。

是否可以选择以某种方式动态地从头开始加载AppContextSwitchOverrides?其他想法将是最受欢迎的。感谢。

1 个答案:

答案 0 :(得分:2)

我一直有同样的问题,并注意到同样缺乏对该特定设置的加载。

到目前为止,我得到的是设置的缓存至少部分归咎于此。 如果你查看它是如何实现的,禁用缓存对未来的值调用没有影响(例如,如果启用了缓存并且在此期间访问了某些内容,那么它将始终被缓存)。

https://referencesource.microsoft.com/#mscorlib/system/AppContext/AppContext.cs

对于大多数设置而言,这似乎不是问题,但由于某些原因,我可以在第一次进入代码时缓存UseLegacyPathHandling和BlockLongPaths设置。

目前,我没有一个好的答案,但如果你在过渡期间需要一些东西,我有一个非常可疑的临时修复你。使用反射,您可以修复程序集init中的设置。它按名称写入私有变量,并使用特定值0来使缓存无效,因此这是一个非常精细的修复,不适合长期解决方案。

话虽如此,如果您需要暂时“正常工作”的内容,您可以检查设置,并根据需要应用黑客攻击。 这是一个简单的示例代码。这将是您在测试类中需要的方法。

    [AssemblyInitialize]
    public static void AssemblyInit(TestContext context)
    {
        // Check to see if we're using legacy paths
        bool stillUsingLegacyPaths;
        if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths) && stillUsingLegacyPaths)
        {
            // Here's where we trash the private cached field to get this to ACTUALLY work.
            var switchType = Type.GetType("System.AppContextSwitches"); // <- internal class, bad idea.
            if (switchType != null)
            {
                AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);   // <- Should disable legacy path handling

                // Get the private field that is used for caching the path handling value (bad idea).
                var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                legacyField?.SetValue(null, (Int32)0); // <- caching uses 0 to indicate no value, -1 for false, 1 for true.

                // Ensure the value is set.  This changes the backing field, but we're stuck with the cached value for now.
                AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths);
                TestAssert.False(stillUsingLegacyPaths, "Testing will fail if we are using legacy path handling.");
            }
        }
    }