Migration to .Net Core 5 - The generated .config file's name has changed
Asked Answered
B

2

6

I'm migrating one of our Visual Studio solutions to .Net Core 5. It has two projects: a WinForms application (legacy) in Visual Basic and a class library in C#.

I've noticed that the .config file generated for the WinForms application has a different name from the previous version: it used to be {WinForms application name}.exe.config and now it's {WinForms application name}.dll.config.

I haven't been able to find documentation on this name change.

What I'm worried about is our version update process. On each new version of our software, we supply the .exe and .dll files to our IT operations people and they set them up for use. If there have been changes in the .config file, we supply it as well with a note telling them which nodes to add, remove or update.

Now, I don't know if the executable will work well with a config file with the old name format. So I've experimented. First, I change the value of a key the .dll.config file and launch the .exe directly. Quite normally, the changes are taken into account. Then I rename the .dll.config file to .exe.config again, and relaunch the .exe. This time, the application uses the initial value, even when I change the .exe.config file again.

I've also tried to rename the config file manually: building the solution simply (and predictably) creates a new .dll.config file.

  1. Is there documentation about this somewhere?
  2. Is there a way to undo this name change so our IT operations people don't have to wonder about it? (They're quite busy enough already.)
Brandes answered 6/10, 2021 at 8:52 Comment(1)
As an aside, I've noticed an issue with My.Settings (it has the normal properties but getting them raises an exception): I gather from here that the issue comes from a <system.diagnostics> section in the config file that shouldn't be here and can be deleted manually. (It's regenerated by rebuilds as said in the linked page, but thankfully, in my experience, not by solution builds.)Brandes
L
3

The reason for this change is related to my answer there: Is the file format of a Library only a DLL file?

The .exe file of a .NET core app is only a stub loader. The actual code is in the .dll file. Note that for the application project, you now have both an application.dll and an application.exe. I'm rarely manipulating the config files, so I'm not exactly sure about the entries there, but if it works with .dll.config, then use that. You do have to tell your IT about the change anyway, as there might be other breaking changes as well (they might also need to install the new framework, etc.).

Do make sure your C# library and application are not using the same name. This was working previously with .NET framework, but will be a problem with .NET Core.

Lauraine answered 6/10, 2021 at 9:9 Comment(2)
Thanks! And thankfully we have different names for the library and the application, so all will be well on that count.Brandes
I was envisioning to tell our IT operation people to just rename the file, but on second thoughts the snag with the <system.diagnostics> section means they'd be much better off copying-pasting the <userSettings> section from the old to the new file.Brandes
I
1

There's a related answer here where @DeepSpace101 mentions:

Also, the location of app.config at runtime is different than what was in .net framework, instead of "projectName.exe.config". It is now "projectName.dll.config" in .net core.

I recently ran into this problem after migrating some WinForms apps from .NET Framework 4.8 to .NET 7. I noticed that all my settings were returning default values even when the .exe.config file contained an explicit override value. I first tried to see which config file the app was loading based on this answer, but AppDomain.CurrentDomain.SetupInformation.ConfigurationFile isn't available in .NET "Core". Then based on another answer, I checked ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath, which confirmed the app was loading from projectName.dll.config instead of projectName.exe.config.

Fortunately, I found a good workaround based on this answer and this GitHub issue. ConfigurationManager supports an APP_CONFIG_FILE key for AppDomain.SetData to override the .config file as long as it is called before any ConfigurationManager code is executed. I've made a utility function that I call from all of my WinForms .exes as the first line in their Program's Main() entry point:

/// <summary>
/// Overrides .NET's Core's ConfigurationManager to use AppName.exe.config instead 
/// of AppName.dll.config.
/// </summary>
/// <param name="exeConfigName">An optional name of an .exe.config file to use.
/// If null, then the entry assembly's file name without extension is used with
/// ".exe.config" appended.</param>
/// <remarks>
/// For this to take effect, this method must be called before any application code
/// pulls a configuration value.
/// </remarks>
public static void UseExeConfig(string? exeConfigName = null)
{
    // .NET Framework and Core support using APP_CONFIG_FILE to override which .config
    // file an app reads its settings from.
    // https://github.com/dotnet/runtime/pull/56748#discussion_r682287640
    //
    // Note 1: This .exe.config override only affects some sections of the file. Anything
    // read by the CLR host before the app starts (e.g., settings in the <runtime> section
    // like <gcServer>) will only be read from the app's default config file.
    // https://mcmap.net/q/435017/-relocating-app-config-file-to-a-custom-path/12708388#12708388
    // However, that's only an issue for .NET Framework since .NET Core reads its CLR host
    // runtime settings from other places. https://learn.microsoft.com/en-us/dotnet/core/runtime-config/
    //
    // Note 2: We have to be careful to only reference base CLR methods here and not use 
    // anything from referenced assemblies. If we run any code that pulls a configuration
    // setting before we change APP_CONFIG_FILE, then our change will be ignored.
    string? entryAssemblyLocation = Assembly.GetEntryAssembly()?.Location;
    if (entryAssemblyLocation != null)
    {
        string entryAssemblyFile = Path.GetFileName(entryAssemblyLocation);
        string entryConfigName = entryAssemblyFile + ".config";
        exeConfigName ??= Path.GetFileNameWithoutExtension(entryAssemblyFile) + ".exe.config";
        if (!entryConfigName.Equals(exeConfigName, StringComparison.OrdinalIgnoreCase))
        {
            string basePath = Path.GetDirectoryName(entryAssemblyLocation) ?? string.Empty;
            string exeConfigFilePath = Path.Combine(basePath, exeConfigName);
            AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", exeConfigFilePath);
        }
    }
}

This technique can also be used to force multiple applications to share a single .config file. For example, if you want Service.exe and Worker.exe to both look at Service.exe.config, then call UseExeConfig("Service.exe.config") from each of their Main methods. That allows IT/Support to only have to configure connection strings in one place.

Indented answered 13/1, 2023 at 16:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.