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

2023-12-05

如何强制 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 桌面应用程序,但它们仍然需要能够通过动态编辑文件(如果条件发生变化)来简单地更改配置设置。虽然我可以自己滚动解决方案,我想像在我的网络应用程序(appsettings.json)中那样执行此操作。更好的是,我希望看到一个 VS2022 桌面模板,每次为 DotNet6 和 7 创建新的桌面应用程序时都会执行此操作。

有任何想法吗?


基本上你可以使用Microsoft.Extensions.Configuration但你也可以利用Microsoft.Extensions.Hostingnuget 包也是如此。

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

Steps:

  • Add an appsettings.json file to the project.
    • Set Build Action to None.
    • Set Copy to Output Directory to Copy always.
  • 为您的选项创建一个类。 (在此示例中:SampleOptions)
  • 配置您的选项appsettings.json.
  • Add Microsoft.Extensions.Hostingnuget 包到您的项目中。
  • Modify Program.cs:
    • 配置并构建主机。 (例如,将您的表格、选项注册到 DI 中)
    • 启动主机。
    • Resolve IHostApplicationLifetime.
    • 创建一个IServiceScope使用主机的IServiceProvider.
    • 解决你的MainForm.
    • Call Application.Run()方法与你解决的MainForm实例。
  • Modify MainForm.cs:
    • Inject IOptionsMonitor<SampleOptions>进入.ctor
    • 注册一个监听器,每当SampleOptions changes
      using IOptionsMonitor<SampleOptions>.OnChange method.

这是文件。

应用程序设置.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  },
  "SampleOptions": {
    "SampleStringKey": "Sample value",
    "SampleIntegerKey": 42
  }
}

示例选项.cs

namespace WinFormsAppNet6;

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

    public int SampleIntegerKey { get; set; }
}

程序.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();
    }
}

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();
    }
}

Screenshot Running the sample application

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何强制Dotnet6桌面Winforms应用程序使用appsettings.json而不是Settings.settings? 的相关文章

  • DispatcherTimer 未按时执行

    我正在使用 c 中的 DispatchTimer 编写一个时钟应用程序 但由于某些原因 我的时钟似乎时不时地跳过 1 秒 例如 52 秒 gt 54 秒 跳过 53 秒 在我看来 计时器并不是每秒都执行一次 DispatcherTimer
  • WebClient读取错误页面的内容

    我有一个加载页面内容的应用程序 我使用 WebClient 类 即使服务器返回 404 500 等错误 我也需要检索内容 我需要这样的东西 WebClient wc new WebClient string pageContent try
  • 为什么派生类不使用基类的operator=(赋值运算符)?

    以下是实际问题的简化版本 而不是打电话Base operator int 代码似乎生成了一个临时的Derived对象并复制它 既然函数签名似乎完美匹配 为什么不使用基本赋值运算符 这个简化的示例没有显示任何不良影响 但原始代码在析构函数中有
  • 返回 int& 的函数[重复]

    这个问题在这里已经有答案了 我在网上查了一下发现一篇试图解释的文章std move和右值 http thbecker net articles rvalue references section 01 html并发现了一些我实在无法掌握的东
  • 在 GCC 和 Clang 下,使用 lambda 的简单 RAII 包装器的复制初始化意外失败

    我在创建一个简单的 RAII 包装器时遇到了一个意想不到的问题 更不用说下面代码的逻辑不完整性了 复制构造函数和赋值运算符未删除等 这意味着是一个SSCCE 令我印象深刻的是复制初始化我的包装器与临时 lambda 的结果会导致编译错误 而
  • C 中的模仿函数重写

    具体来说 函数重写能够调用基本重写方法 这有两部分 一个是预编译的库代码 1 另一个是库的用户代码 2 我在这里实现了一个尽可能最小的经典 Person 和 Employee 示例 非常感谢了解 OOP 概念的铁杆 C 开发人员的回应 我正
  • 用 C# 制作 Vista 风格的应用程序

    我正在运行 Windows Vista 并且希望外观看起来像常规 Vista 程序 有没有关于如何构建 Vista 风格应用程序的真正好的教程 文章 我还想学习如何使用本机代码并将其转换为 C 如this http bartdesmet n
  • 使用 openssl 检查服务器安全协议

    我有一个框架应用程序 它根据使用方式连接到不同的服务器 对于 https 连接 使用 openssl 我的问题是 我需要知道我连接的服务器是否使用 SSL 还是 TLS 以便我可以创建正确的 SSL 上下文 目前 如果我使用错误的上下文尝试
  • 如何使用泛型类型的 DataContractSerializer 编写自定义序列化器?

    我想编写一个自定义序列化器 用于将会话状态存储到Azure 缓存 预览版 这意味着这个自定义序列化器必须实现IDataCacheObjectSerializer 如果我错了 请告诉我 我需要编写这个自定义序列化程序的原因是我需要序列化一些包
  • Microsoft.Graph - 如何从具有不同用户名的共享邮箱发送?

    我目前正在将使用 SMTP 的服务代码移植到 Office 365 通过 SMTP 我可以使用 发件人 字段在来自共享收件箱的邮件上设置不同的用户名 同时保留共享电子邮箱地址 这似乎无法通过 Office 365 运行 其工艺流程为 客户填
  • 线程安全的 C++ 堆栈

    我是 C 新手 正在编写一个多线程应用程序 不同的编写者将对象推入堆栈 读者将它们从堆栈中拉出 或至少将指针推入对象 C 中是否有任何内置结构可以在不添加锁定代码等的情况下处理此问题 如果没有 那么 Boost 库呢 EDIT 你好 感谢您
  • c# 如何生成锦标赛括号 HTML 表

    所以我已经被这个问题困扰了三个星期 但我一生都无法弄清楚 我想做的是使用表格获得这种输出 演示 http www esl world net masters season6 hanover sc2 playoffs rankings htt
  • 如何在 C# 中使用 XmlDsigC14NTransform 类

    我正在尝试使用规范化 xml 节点System Security Cryptography Xml XMLDsigC14nTransformC net Framework 2 0 的类 该实例需要三种不同的输入类型 NodeList Str
  • 如何将字符串转换为 Indian Money 格式?

    我正在尝试将字符串转换为印度货币格式 例如如果输入为 1234567 则输出应为 12 34 567 我编写了以下代码 但它没有给出预期的输出 CultureInfo hindi new CultureInfo hi IN string t
  • 是什么原因导致 Linq 错误:此方法无法转换为存储表达式?

    我有一堆具有相同 select 语句的 Linq to Entity 方法 所以我想我会很聪明 并将其分离到它自己的方法中以减少冗余 但是当我尝试运行代码时 我得到了以下内容错误 该方法不能转化为 商店表达式 这是我创建的方法 public
  • 当在 Repository/UnitOrWork 之上使用 Service 类时,我应该在哪里放置逻辑不适合 Repository 的常用数据访问代码?

    In my 先前的问题 https stackoverflow com questions 24906548 using the generic repository unit of work pattern in large projec
  • 为什么 C# 接口名称前面加上“I”

    这种命名约定背后的基本原理是什么 我没有看到任何好处 额外的前缀只会污染 API 我的想法与康拉德一致response https stackoverflow com a 222502 9898与此相关的question https sta
  • c# 替代方案中 cfusion_encrypt 中填充的密钥是什么?

    我找到了从这里复制 C 中的 cfusion encrypt 函数的答案 ColdFusion cfusion encrypt 和 cfusion decrypt C 替代方案 https stackoverflow com questio
  • 通过 cmake 链接作为外部项目包含的 opencv 库[重复]

    这个问题在这里已经有答案了 我对 cmake 比较陌生 经过几天的努力无法弄清楚以下事情 我有一个依赖于 opencv 的项目 它本身就是一个 cmake 项目 我想静态链接 opencv 库 我正在做的是我的项目中有一份 opencv 源
  • 创建带有部分的选项卡式侧边栏 WPF

    我正在尝试创建一个带有部分的选项卡式侧边栏 如 WPF 中的以下内容 我考虑过几种方法 但是有没有更简单 更优雅的方法呢 方法一 列表框 Using a ListBox并将 SelectedItem 绑定到右侧内容控件所绑定的值 为了区分标

随机推荐

  • 在 JSON.NET 中将整数序列化为十六进制

    JSON NET 支持反序列化十六进制数 e g 0xffff 但是怎么样序列化 以下有效 但似乎太复杂 public sealed class HexJsonConverter JsonConverter public override
  • java Regex - 分割但忽略引号内的文本?

    仅使用正则表达式方法 方法 String replaceAll 和 ArrayList 如何将字符串拆分为标记 但忽略引号内存在的分隔符 分隔符是非字母数字或引用文本的任何字符 例如 字符串 你好 世界 这个 有两个令牌 应该输出 hell
  • Google Cloud CDN 以存储桶为后端签署 cookie

    作为替代方案签名网址带有 url 前缀 我试图获取签名cookie在职的 Google Cloud CDN 设置了一个后端存储桶 该存储桶已配置并适用于标准签名网址 使用这些去例子我在nodejs typescript 中实现了一个cook
  • 使用 -lpthread 标志时对“pthread_init”的未定义引用:

    我正在编译使用 gcc o outfile infile c lpthread 唯一未定义的参考是pthread init 我尝试过使用 pthread代替 lpthread 根据网上的一些建议 I am包括
  • 更改 Firemonkey StringGrid 中的文本大小

    如何更改 stringgrid 标题中的文本大小 你可以写一个OnApplyStyleLookup事件处理程序与此类似 procedure TForm1 StringGrid1ApplyStyleLookup Sender TObject
  • 从代码隐藏中打开 jQuery 对话框

    所以我必须从代 码隐藏中显示一个 jquery UI 对话框 我已经尝试了一切 this this this 并且还更改了这些答案以测试它是否适用于我但不起作用 我使用第一个解决方案 因为它是有组织的 如果我使用alert whatever
  • 使用默认触发器在窗口中消耗无限数据

    我有一个Pub Sub主题 订阅 并希望在一个订阅中消费和聚合无限数据Dataflow 我使用固定窗口并将聚合写入 BigQuery 读取和写入 没有窗口和聚合 工作正常 但是 当我将数据通过管道传输到固定窗口 以计算每个窗口中的元素 时
  • 如何在 OCaml 中使用列表实现二叉堆?

    我正在实施一个binary heap在 OCaml 中使用列表 只是为了提高我的 OCaml 技能 我觉得使用列表非常困难 经过两天的努力 我必须来这里寻求建议和提示 到目前为止 这是我的想法 显然我不能使用原来的array based使用
  • Android - AutoCompleteTextView 仅在退格时有效

    我有一个AutoCompleteTextView当用户键入时动态更新建议列表 我的问题是 当我输入时 列表会更新 但下拉列表不会显示 但是当我删除一个字符 退格键 时 会出现下拉菜单 我什至尝试明确地调用autoText showDropD
  • Python Tkinter 坐标函数不会在循环内移动画布对象

    我有一个函数 它从文本文件中读取位置 解析它们 然后使用 coords 函数将相应的对象移动到 tkinter 画布上列出的位置 正在从文件中读取数据并正确解析数据 但由于某种原因 coords 函数仅将对象移动到循环的最后一次迭代中文件中
  • WordPress:一页滚动 JQuery 脚本不起作用?

    尝试创建一个简单的一页滚动 在 WordPress 中 我有这样的导航标签 a href contact Contact a a href about About a 两个链接对应的 div 是这样的 div class bg 1 wrap
  • Grails 安全插件自定义重定向

    我是 Groovy 和 Grails 的新手 我使用 Spring Security 插件使用数据库请求的请求映射开发了一个应用程序 我想要根据角色自定义重定向到主页 如果用户是 ROLE ADMIN 他将被重定向到视图 adminUser
  • 从 https 中删除 www

    我正在尝试找到一个 htaccess 重写来删除 www 仅来自 https 页面 我发现的每篇文章都删除了所有页面的 www 我需要在 htaccess 中添加什么来执行此操作 https www mydomain com应该https
  • 当行值等于“yes”时连接 Google 表格中的标题值的数组公式

    I saw this问题但无法让它为我工作 因为它已经得到回答 并且从一年前开始 我觉得最好发布一个新问题 我有一个样本表 https docs google com spreadsheets d 1EqNfTWcNFgkv2gdovnH7
  • 返回多个变量以在 Laravel 中查看的最佳方法

    我有一个操作方法 其主体中有多个变量 如下所示 bus Bus all user User all employer employer all 将这些变量对象返回到视图正在做的事情是使用这样的代码 return view create gt
  • 使用 Nodejs 的 SendGrid 模板中的变量替换不起作用

    继USE CASE在 SendGrids github 上确实设法向我发送带有正确模板的电子邮件 但替换显然不起作用 并且在生成的电子邮件中留空 服务器端 const sgmailer require sendgrid mail sgmai
  • 使用 Protractor 设置 Angular 模型

    我正在尝试使用 Protractor 在我的网站上模拟用户故事 用户必须输入使用自动完成功能的输入 在现实生活中 用户必须在输入中键入一些文本 然后使用鼠标或更自然地使用向下箭头键选择正确的命题 问题是我似乎无法用 Protractor 来
  • 什么是 WINAPI_FAMILY_ONECORE_APP?

    我正在 GitHub 上查看 Microsoft 的 OpenSSL 端口 有一个提交引起了我的注意 那就是添加Win10通用平台支持 在提交中 一个名为WINAPI FAMILY ONECORE APP出现 然而 我在搜索时并没有找到太多
  • 如何禁用来自其他域的WebService调用

    我想避免其他人访问我的网络服务 我有一个名为 WebService asmx 的 Web 服务 它托管在 www xyz com 上 我们知道我们可以访问该服务 例如http www xyz com WebService asmx 如何避免
  • 如何强制Dotnet6桌面Winforms应用程序使用appsettings.json而不是Settings.settings?

    如何强制 Dotnet6 桌面 Winforms 应用程序使用 appsettings json 而不是 Settings settings App config 如果我在 Visual Studio 2022 中创建一个新的桌面应用程序以