如何强制 .NET 6 Winforms 应用程序使用 appsettings.json 而不是 Settings.settings?

MC9*_*000 3 c# appsettings winforms .net-6.0

如何强制 Dotnet6 桌面 Winforms 应用程序使用 appsettings.json 而不是 Settings.settings[App.config]?

如果我在 Visual Studio 2022 中创建一个新的桌面应用程序以使用 Dotnet6.0,默认情况下,它会动态创建一个 Settings.settings 文件,然后也会创建一个 App.config 文件(这很奇怪,因为据说该文件已被弃用)在 Dotnet5.0 及更高版本中!)。

不幸的是,所有设置在安装/发布后都嵌入到应用程序中,并且一旦安装,就无法在应用程序文件夹中的任何位置进行更改。

对于较旧的 DotNet4.72 桌面应用程序,app.config 始终位于发布输出中,如果发生更改,应用程序将在执行时很好地读取任何更改。

我想像 Web 应用程序 (dotnet6.0) 一样使用 appsettings.json,并且有很多关于如何使用控制台应用程序 (dotnet6.0) 执行此操作的信息,但不适用于桌面窗口。

我有很多想要转换的 DotNet4.72 桌面应用程序,但它们仍然需要能够通过动态编辑文件(如果条件发生变化)来简单地更改配置设置。虽然我可以推出自己的解决方案,但我希望像在我的 Web 应用程序 (appsettings.json) 中那样执行此操作。更好的是,我希望看到一个 VS2022 桌面模板,每次为 DotNet6 和 7 创建新的桌面应用程序时都会执行此操作。

有任何想法吗?

Gab*_*bor 11

基本上你可以使用,Microsoft.Extensions.Configuration但你也可以使用Microsoft.Extensions.Hostingnuget 包。

这是一个经过测试的工作示例,也可以处理配置更改。

脚步:

  • 将文件添加appsettings.json到项目中。
    • 设置。Build ActionNone
    • 设置。Copy to Output DirectoryCopy always
  • 为您的选项创建一个类。(在此示例中SampleOptions:)
  • 在 中配置您的选项appsettings.json
  • Microsoft.Extensions.Hosting将nuget 包添加到您的项目中。
  • 调整Program.cs
    • 配置并构建主机。(例如,将您的表格、选项注册到 DI 中)
    • 启动主机。
    • 解决IHostApplicationLifetime
    • 创建一个IServiceScope使用主机的IServiceProvider.
    • 解决你的MainForm.
    • Application.Run()使用已解析的实例调用方法MainForm
  • 调整MainForm.cs
    • 注入IOptionsMonitor<SampleOptions>.ctor
    • 注册一个侦听器,以便在 使用方法SampleOptions发生更改时调用。
      IOptionsMonitor<SampleOptions>.OnChange

这是文件。

应用程序设置.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  },
  "SampleOptions": {
    "SampleStringKey": "Sample value",
    "SampleIntegerKey": 42
  }
}
Run Code Online (Sandbox Code Playgroud)

示例选项.cs

namespace WinFormsAppNet6;

public class SampleOptions
{
    public string SampleStringKey { get; set; } = "N/A";

    public int SampleIntegerKey { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

程序.cs

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace WinFormsAppNet6;

internal static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static async Task Main() // <-- 'async Task' instead of 'void'
    {
        // To customize application configuration such as
        // set high DPI settings or default font,
        // see https://aka.ms/applicationconfiguration.
        ApplicationConfiguration.Initialize();

        using IHost host = CreateHost();
        await host.StartAsync();

        IHostApplicationLifetime lifetime = 
            host.Services.GetRequiredService<IHostApplicationLifetime>();

        using (IServiceScope scope = host.Services.CreateScope())
        {
            var mainForm = scope.ServiceProvider.GetRequiredService<MainForm>();

            Application.Run(mainForm);
        }

        lifetime.StopApplication();
        await host.WaitForShutdownAsync();
    }

    private static IHost CreateHost()
    {
        string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();

        HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

        builder.Services.AddSingleton<MainForm>();

        builder.Services.Configure<SampleOptions>(
            builder.Configuration.GetSection(nameof(SampleOptions))
        );

        return builder.Build();
    }
}
Run Code Online (Sandbox Code Playgroud)

MainForm.cs

控制:

  • Button: UpdateConfigurationButton
  • Button: ExitButton
  • RichTextBox: AppLog
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Windows.Forms;

using Microsoft.Extensions.Options;

namespace WinFormsAppNet6;

public partial class MainForm : Form
{
    private readonly IOptionsMonitor<SampleOptions> optionsMonitor;

    private readonly JsonSerializerOptions jsonSerializerOptions =
        new(JsonSerializerDefaults.Web)
        {
            WriteIndented = true
        };

    public MainForm(IOptionsMonitor<SampleOptions> optionsMonitor)
    {
        InitializeComponent();

        this.optionsMonitor = optionsMonitor;
        optionsMonitor.OnChange(OnOptionsChange);

        LogOptions(optionsMonitor.CurrentValue);
    }

    private void OnOptionsChange(SampleOptions options)
    {
        LogOptions(options);
    }

    private void LogOptions(
        SampleOptions options,
        [CallerMemberName] string? callerMemberName = null
    )
    {
        AppendLog(
            Environment.NewLine + JsonSerializer.Serialize(options),
            callerMemberName
        );
    }

    private void AppendLog(
        string message,
        [CallerMemberName] string? callerMemberName = null
    )
    {
        if (AppLog.InvokeRequired)
        {
            AppLog.Invoke(() => AppendLog(message, callerMemberName));
            return;
        }

        AppLog.AppendText(
            $"{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.ffffff} [{nameof(MainForm)}]::[{callerMemberName ?? "Unknown"}] {message}{Environment.NewLine}"
        );
    }

    private void UpdateConfigurationButton_Click(object sender, EventArgs e)
    {
        const string AppSettingsJsonFileName = "appsettings.json";

        if (!File.Exists(AppSettingsJsonFileName))
        {
            AppendLog(
                $"{nameof(FileNotFoundException)}: {AppSettingsJsonFileName}"
            );

            return;
        }

        string jsonContent = File.ReadAllText(AppSettingsJsonFileName);
        JsonNode? rootNode = JsonNode.Parse(jsonContent);

        if (rootNode is null)
        {
            AppendLog($"{nameof(JsonException)}: File parse failed.");
            return;
        }

        AppendLog(
            $"Finding key: {nameof(SampleOptions)}:{nameof(SampleOptions.SampleStringKey)}"
        );

        JsonObject rootObject = rootNode.AsObject();
        JsonObject? optionsObject = rootObject[nameof(SampleOptions)]?.AsObject();

        if (optionsObject is null)
        {
            AppendLog(
                $"{nameof(KeyNotFoundException)}: {nameof(SampleOptions)}:{nameof(SampleOptions.SampleStringKey)}"
            );

            return;
        }

        AppendLog(
            $"Updating key: {nameof(SampleOptions)}:{nameof(SampleOptions.SampleStringKey)}"
        );

        optionsObject[nameof(SampleOptions.SampleStringKey)] =
            JsonValue.Create(
                $"Value modified at {DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss.ffffff}"
            );

        AppendLog($"Saving file: {AppSettingsJsonFileName}");

        using var stream =
            new FileStream(
                AppSettingsJsonFileName,
                FileMode.OpenOrCreate,
                FileAccess.Write,
                FileShare.None
            );

        jsonContent = rootObject.ToJsonString(jsonSerializerOptions);

        stream.Write(Encoding.UTF8.GetBytes(jsonContent));
        stream.Flush();
    }

    private void ExitButton_Click(object? sender, EventArgs e)
    {
        Application.Exit();
    }
}
Run Code Online (Sandbox Code Playgroud)

截屏 运行示例应用程序