服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - .Net6开发winform程序使用依赖注入

.Net6开发winform程序使用依赖注入

2022-12-13 13:50harrychinese C#

本文详细讲解了.Net6开发winform程序使用依赖注入的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

.net  Blazor webassembly 和 webAPI 内建支持依赖注入, Winform 和 Console 应用虽然不带有依赖注入功能, 但增加依赖注入也很简单. 

本文将示例如何为 WinForm 程序增加依赖注入特性, 实现通过DI容器获取Cofiguration 实例, 并读取appsettings.json文件.

安装依赖库, 有点多

  • Microsoft.Extensions.DependencyInjection 库, 依赖注入的类库
  • Microsoft.Extensions.Configuration 库, 包含IConfiguration接口 和 Configuration类
  • Microsoft.Extensions.Configuration.Json 库, 为 IConfiguration 增加了读取 Json 文件功能,
  • Microsoft.Extensions.Hosting 库,  提供 Host 静态类,  有能力从 appsettings.{env.EnvironmentName}.json 加载相应 env  的设定值,  并将设定值用于IConfiguration/ILoggerFactory中, 同时增加 Console/EventSourceLogger 等 logger. 仅适用于 Asp.Net core 和 Console 类应用
  • Microsoft.Extensions.Logging 库,  包含 ILogger 和 ILoggerFactory 接口
  • Serilog.Extensions.Logging 库, 为DI 容器提供 AddSerilog() 方法.
  • Serilog.Sinks.File 库, 提供 Serilog rolling logger
  • Serilog.Sinks.Console 库, 增加 serilog console logger
  • Serilog.Settings.Configuration 库, 允许在 appsetting.json  配置 Serilog, 顶层节点要求是 Serilog. 
  • Serilog.Enrichers.Thread 和 Serilog.Enrichers.Environment 库,  为输出日志文本增加 Thread和 env 信息

补充库:

  • Microsoft.Extensions.Options.ConfigurationExtensions 库,  为DI容器增加了从配置文件中实例化对象的能力, 即  serviceCollection.Configure<TOptions>(IConfiguration)
  • Microsoft.Extensions.Options 库,  提供以强类型的方式读取configuration文件, 这是.Net中首选的读取configuration文件方式.

appsettings.json 配置文件

配置一个 ConnectionString, 另外配 serilog

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  
  "ConnectionStrings": {
    "oeeDb": "Server=localhost\\SQLEXPRESS01;Database=Oee;Trusted_Connection=True;"
  },
 
  "Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": { "path": "Logs/serilog.txt" }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
  }
}

Program.cs , 增加DI容器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
 
using Serilog;
 
namespace Collector
{
    internal static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            ApplicationConfiguration.Initialize();
            //未使用依赖注入的写法
            //Application.Run(new FormMain());
 
            
           //生成 DI 容器
           ServiceCollection services = new ServiceCollection();
           ConfigureServices(services);  //注册各种服务类
 
            //先用DI容器生成 serviceProvider, 然后通过 serviceProvider 获取Main Form的注册实例
           var serviceProvider =services.BuildServiceProvider();
            
           var formMain = serviceProvider.GetRequiredService<FormMain>();   //主动从容器中获取FormMain实例, 这是简洁写法
           // var formMain = (FormMain)serviceProvider.GetService(typeof(FormMain));  //更繁琐的写法
           Application.Run(formMain);
        }
 
       
        /// <summary>
        /// 在DI容器中注册所有的服务类型
        /// </summary>
        /// <param name="services"></param>
        private static void ConfigureServices(ServiceCollection services)
        {
            //注册 FormMain 类
            services.AddScoped<FormMain>();
 
            //register configuration
            IConfigurationBuilder cfgBuilder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: false)
                ;
            IConfiguration configuration=cfgBuilder.Build();
            services.AddSingleton<IConfiguration>(configuration);
 
            //Create logger instance
            var serilogLogger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .Enrich.FromLogContext()
                .CreateLogger();
 
            //register logger
            services.AddLogging(builder => {
                object p = builder.AddSerilog(logger: serilogLogger, dispose: true);
            });
 
        }
    }
}

FormMain.cs , 验证依赖注入的效果

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
 
namespace Collector
{
    public partial class FormMain : Form
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger _logger;
 
        /// <summary>
        /// 为 FormMain 构造子增加两个形参, 构造子参数将由于DI容器自动注入
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="logger">形参必须是 ILogger泛型类型, 不能是 ILogger 类型</param>
        public FormMain(IConfiguration configuration, ILogger<FormMain> logger) 
        {
            _configuration = configuration;
            _logger = logger;
 
            InitializeComponent();
            var connectionString = _configuration.GetConnectionString("oeeDb");  //从配置文件中读取oeeDb connectionString
            _logger.LogInformation(connectionString);   //将connection String 写入到日志文件中
        }
 
    }
}

DI容器管理配置文件Section

上面示例, 我们通过 _configuration.GetConnectionString("oeeDb")  可以拿到connectionString, 非常方便, 这主要是得益于.Net 已经类库已经考虑到在配置文件中存储 connectionString 是一个普遍的做法, 所以类库内置支持了.

如果在 appsettings.json 中存一些自定义的信息, 如何方便读取呢? 微软推荐的 Options 模式, 下面详细介绍.

首先安装库:

  • Microsoft.Extensions.Options.ConfigurationExtensions 库,  为DI容器增加了从配置文件中实例化对象的能力, 即  serviceCollection.Configure<TOptions>(IConfiguration)
  • Microsoft.Extensions.Options 库,  提供以强类型的方式读取configuration文件, 这是.Net中首选的读取configuration文件方式.

假设 appsettings.json 中要存放appKey和appSecret信息, 具体配置如下:

?
1
2
3
4
"AppServiceOptions": {
  "appKey": "appkey1",
  "appSecret": "appSecret1"
}

定义对应的 Poco Class,  推荐后缀为 Options,

?
1
2
3
4
5
6
public class AppServiceOptions
{
    public string AppKey { get; set; } = "";
    public string AppSecret { get; set; } = "";
 
}

注册函数 ConfigureServices()中,  注册 AppServiceOptions 类, 告知DI容器, 要基于配置文件AppServiceOptions section来实例化

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static void ConfigureServices(ServiceCollection services)
        {
            //注册 FormMain 类
            services.AddScoped<FormMain>();
 
            //register configuration
            IConfigurationBuilder cfgBuilder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: false)
                ;
            IConfiguration configuration=cfgBuilder.Build();
            services.AddSingleton<IConfiguration>(configuration);
 
            //Create logger instance
            var serilogLogger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .Enrich.FromLogContext()
                .CreateLogger();
 
            //register logger
            services.AddLogging(builder => {
                object p = builder.AddSerilog(logger: serilogLogger, dispose: true);
            });
 
 
            //注册 AppServiceOptions 类, 告知DI容器, 要基于配置文件AppServiceOptions section来实例化
            services.AddOptions();
            services.Configure<AppServiceOptions>(configuration.GetSection("AppServiceOptions"));   
        }

主动从DI容器中获取 AppServiceOptions 配置信息代码如下, 注意GetRequiredService函数的的泛型参数要使用 IOptions<> 包一下.

?
1
2
var appServiceOptionsWrapper=serviceProvider.GetRequiredService<IOptions<AppServiceOptions>>();
AppServiceOptions appServiceOptions= appServiceOptionsWrapper.Value;

将 AppServiceOptions 注入到 FormMain 的代码, 和主动从DI容器中获取 AppServiceOptions 实例一样, 都需要使用 IOptions<> 接口包一下构造子形参.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class FormMain : Form
    {
        private readonly IConfiguration _configuration;
        private readonly ILogger _logger;
        private AppServiceOptions _appServiceOptions;
 
        /// <summary>
        /// 为 FormMain 构造子增加三个形参, 构造子参数将由于DI容器自动注入
        /// </summary>
        /// <param name="configuration">形参必须是接口 IConfigurations</param>
        /// <param name="logger">形参必须是 ILogger泛型类型, 不能是 ILogger 类型</param>
        /// <param name="appServiceOptionsWrapper">形参必须是 IOptions 泛型接口 </param>
        public FormMain(IConfiguration configuration, ILogger<FormMain> logger, IOptions<AppServiceOptions> appServiceOptionsWrapper) 
        {
            _configuration = configuration;
            _logger = logger;
            _appServiceOptions = appServiceOptionsWrapper.Value;
 
            InitializeComponent();
            var connectionString = _configuration.GetConnectionString("oeeDb");  //从配置文件中读取oeeDb connectionString
            _logger.LogInformation(connectionString);   //将connection String 写入到日志文件中
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            this.Text = _appServiceOptions.AppKey;
        }
    }

.net core 复杂 configuration Section 的读取

appsettings文件定义一个复杂的设置项, 顶层是一个json 数组, 里面又嵌套了另一个数组

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"PlcDevices": [
    {
      "PlcDeviceId": "Plc1",
      "IpAddress": "127.0.0.1",
      "Port": 1234,
      "SlaveId": 1,
      "DataPoints": [
        {
          "ModbusAddress": 0,
          "EqpId": "eqp1"
        },
        {
          "ModbusAddress": 0,
          "EqpId": "eqp2"
        }
      ]
    },
 
    {
      "PlcDeviceId": "Plc2",
      "IpAddress": "127.0.0.2",
      "Port": 1234,
      "SlaveId": "2",
      "DataPoints": [
        {
          "ModbusAddress": 0,
          "EqpId": "eqp3"
        },
        {
          "ModbusAddress": 0,
          "EqpId": "eqp4"
        }
      ]
    }
  ]

对应poco对象为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PlcDevice
{
    public string IpAddress { get; set; } = "";
    public int Port { get; set; } = 0;
    public string PlcDeviceId { get; set; } = "";
    public int SlaveId { get; set; }
    public List<DataPoint> DataPoints { get; set; }
 
}
 
public class DataPoint
public int ModbusAddress { get; set; }
    public string EqpId { get; set; } = "";
}

读取 json 的C# 代码:

?
1
2
3
4
5
services.AddOptions();
//实例化一个对应 PlcDevices json 数组对象, 使用了 IConfiguration.Get<T>()
var PlcDeviceSettings= configuration.GetSection("PlcDevices").Get<List<PlcDevice>>();
//或直接通过 service.Configure<T>() 将appsettings 指定 section 放入DI 容器, 这里的T 为 List<PlcDevice>
services.Configure<List<PlcDevice>>(configuration.GetSection("PlcDevices"));

到此这篇关于.Net6开发winform程序使用依赖注入的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/harrychinese/p/winform_DependencyInjection.html

延伸 · 阅读

精彩推荐
  • C#C#位运算以及实例计算详解

    C#位运算以及实例计算详解

    这篇文章主要给大家介绍了关于C#位运算以及实例计算的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需...

    艾三元11492022-07-28
  • C#VMS中解协议常用方法备忘(小结)

    VMS中解协议常用方法备忘(小结)

    这篇文章主要介绍了VMS中解协议常用方法备忘(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们...

    森大科技3312022-08-16
  • C#C# wpf解决Popup弹出位置异常问题解决

    C# wpf解决Popup弹出位置异常问题解决

    本文主要介绍了C# wpf解决Popup弹出位置异常问题解决,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Alfred-N7872022-12-07
  • C#C#实现程序开机启动的方法

    C#实现程序开机启动的方法

    这篇文章主要介绍了C#实现程序开机启动的方法,涉及C#针对应用程序及注册表的相关操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    冇一朵小尐尒尕尗尛尜4372021-11-24
  • C#C#微信开发之接收 / 返回文本消息

    C#微信开发之接收 / 返回文本消息

    本文主要介绍了C#微信开发之接收 / 返回文本消息原理与实现方法,具有一定的参考价值,下面跟着小编一起来看下吧...

    likar11252021-12-22
  • C#C#如何利用反射将枚举绑定到下拉框详解

    C#如何利用反射将枚举绑定到下拉框详解

    这篇文章主要给大家介绍了关于C#如何利用反射将枚举绑定到下拉框的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    孤者自清8352022-02-25
  • C#大白话讲解C# 中的委托

    大白话讲解C# 中的委托

    这篇文章主要介绍了C# 中的委托的相关资料,帮助初学者更好的理解和使用c#,感兴趣的朋友可以了解下...

    沙奇码丶5662022-10-19
  • C#Unity中3DText显示模糊不清的解决方案

    Unity中3DText显示模糊不清的解决方案

    这篇文章主要介绍了Unity中3DText显示模糊不清的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    提莫从前很快乐4782022-11-11