.Net6开发winform程序使⽤依赖注⼊
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
{
"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": "" }
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}
Program.cs , 增加DI容器
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 , 验证依赖注⼊的效果
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信息, 具体配置如下:
"AppServiceOptions": {
"appKey": "appkey1",
"appSecret": "appSecret1"
}
定义对应的 Poco Class, 推荐后缀为 Options,
public class AppServiceOptions
{
public string AppKey { get; set; } = "";
public string AppSecret { get; set; } = "";
}
注册函数 ConfigureServices()中, 注册 AppServiceOptions 类, 告知DI容器, 要基于配置⽂件AppServiceOptions section来实
例化
private static void ConfigureServices(ServiceCollection services)
{
//注册 FormMain 类app开发实例
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<> 包⼀下.
var appServiceOptionsWrapper=serviceProvider.GetRequiredService<IOptions<AppServiceOptions>>();
AppServiceOptions appServiceOptions= appServiceOptionsWrapper.Value;
将 AppServiceOptions 注⼊到 FormMain 的代码, 和主动从DI容器中获取 AppServiceOptions 实例⼀样, 都需要使⽤IOptions<> 接⼝包⼀下构造⼦形参.
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;
}
}
core 复杂 configuration Section 的读取
appsettings⽂件定义⼀个复杂的设置项, 顶层是⼀个json 数组, ⾥⾯⼜嵌套了另⼀个数组
"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对象为:
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# 代码:
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程序使⽤依赖注⼊的⽂章就介绍到这了。希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论