SoFunction
Updated on 2025-03-07

.Net6 development winform program uses dependency injection

.net  Blazor webassembly and webAPI have built-in support for dependency injection. Although Winform and Console applications do not have dependency injection functions, it is also very simple to add dependency injection.

This article will illustrate how to add dependency injection features to WinForm programs, implement the acquisition of the Configuration instance through a DI container and read the file.

Installing dependency library, a bit too many

  • Library, class library for dependency injection
  • Library, including IConfiguration interface and Configuration class
  • library, adds the function of reading Json files to IConfiguration,
  • Library, provides Host static class, has the ability to load the corresponding env’s setting value from appsettings.{}.json, and use the setting value in IConfiguration/ILoggerFactory, and add Console/EventSourceLogger and other loggers.Applicable only to core and Console class applications
  • Library, including ILogger and ILoggerFactory interfaces
  • Library, provides the AddSerilog() method for DI containers.
  • Library, providing Serilog rolling logger
  • Library, add serilog console logger
  • Library, which allows configuring Serilog in  , the top-level node requires Serilog.
  • and library, add Thread and env information to output log text

Supplementary library:

  • Library, adds the ability to instantiate objects from configuration files to DI containers, i.e. <TOptions>(IConfiguration)
  • Library provides strongly typed configuration files, which is the preferred way to read configuration files in .Net.

Configuration File

Configure a ConnectionString, and also use serilog

{
  
  "ConnectionStrings": {
    "oeeDb": "Server=localhost\\SQLEXPRESS01;Database=Oee;Trusted_Connection=True;"
  },

  "Serilog": {
    "Using": [ "", "" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": { "path": "Logs/" }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
  }
}

, add DI container

using ;
using ;

using Serilog;

namespace Collector
{
    internal static class Program
    {
        /// &lt;summary&gt;
        ///  The main entry point for the application.
        /// &lt;/summary&gt;
        [STAThread]
        static void Main()
        {
            ();
            //Writing method of dependency injection is not used            //(new FormMain());
 
            
           //Generate DI container           ServiceCollection services = new ServiceCollection();
           ConfigureServices(services);  //Register various services
            //First use the DI container to generate the serviceProvider, and then use the serviceProvider to obtain the registered instance of Main Form           var serviceProvider =();
            
           var formMain = &lt;FormMain&gt;();   // Actively get FormMain instance from the container, this is a simple way to write it           // var formMain = (FormMain)(typeof(FormMain)); // More cumbersome writing           (formMain); 
        }

       
        /// &lt;summary&gt;
        /// Register all service types in the DI container        /// &lt;/summary&gt;
        /// &lt;param name="services"&gt;&lt;/param&gt;
        private static void ConfigureServices(ServiceCollection services)
        {
            //Register FormMain class            &lt;FormMain&gt;();

            //register configuration
            IConfigurationBuilder cfgBuilder = new ConfigurationBuilder()
                .SetBasePath(())
                .AddJsonFile("")
                .AddJsonFile($"appsettings.{("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: false)
                ;
            IConfiguration configuration=();
            &lt;IConfiguration&gt;(configuration);

            //Create logger instance
            var serilogLogger = new LoggerConfiguration()
                .(configuration)
                .()
                .CreateLogger();

            //register logger
            (builder =&gt; {
                object p = (logger: serilogLogger, dispose: true);
            });

        }
    } 
}

, Verify the effect of dependency injection

using ;
using ;

namespace Collector
{
    public partial class FormMain : Form
    { 
        private readonly IConfiguration _configuration; 
        private readonly ILogger _logger;

        /// &lt;summary&gt;
        /// Add two formal parameters to the FormMain constructor, the constructor parameters will be automatically injected due to the DI container.        /// &lt;/summary&gt;
        /// &lt;param name="configuration"&gt;&lt;/param&gt;
        /// <param name="logger">Montally parameter must be an ILogger generic type, not an ILogger type</param>        public FormMain(IConfiguration configuration, ILogger&lt;FormMain&gt; logger)  
        {
            _configuration = configuration;
            _logger = logger;

            InitializeComponent();
            var connectionString = _configuration.GetConnectionString("oeeDb");  //Read oeeDb connectionString from the configuration file            _logger.LogInformation(connectionString);   //Write connection String to log file        }

    }
}

DI Container Management Configuration File Section

In the above example, we can get the connectionString through _configuration.GetConnectionString("oeeDb") which is very convenient, mainly because. Net has considered that storing connectionString in configuration files is a common practice, so the class library has built-in support.

If you store some custom information in , how can you easily read it? The Options mode recommended by Microsoft is introduced in detail below.

First install the library:

  • Library, adds the ability to instantiate objects from configuration files to DI containers, i.e. <TOptions>(IConfiguration)
  • Library provides strongly typed configuration files, which is the preferred way to read configuration files in .Net.

Assume that appKey and appSecret information should be stored, the specific configuration is as follows:

  "AppServiceOptions": {
    "appKey": "appkey1",
    "appSecret": "appSecret1"
  }

Define the corresponding Poco Class, the recommended suffix is ​​Options,

    public class AppServiceOptions
    {
        public string AppKey { get; set; } = "";
        public string AppSecret { get; set; } = "";

    }

Register function ConfigureServices(), register the AppServiceOptions class and tell the DI container that it should be instantiated based on the configuration file AppServiceOptions section.

private static void ConfigureServices(ServiceCollection services)
        {
            //Register FormMain class            &lt;FormMain&gt;();

            //register configuration
            IConfigurationBuilder cfgBuilder = new ConfigurationBuilder()
                .SetBasePath(())
                .AddJsonFile("")
                .AddJsonFile($"appsettings.{("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: false)
                ;
            IConfiguration configuration=();
            &lt;IConfiguration&gt;(configuration);

            //Create logger instance
            var serilogLogger = new LoggerConfiguration()
                .(configuration)
                .()
                .CreateLogger();

            //register logger
            (builder =&gt; {
                object p = (logger: serilogLogger, dispose: true);
            });


            //Register the AppServiceOptions class and tell the DI container that it should be instantiated based on the configuration file AppServiceOptions section            ();
            &lt;AppServiceOptions&gt;(("AppServiceOptions"));    
        }

Actively obtain the AppServiceOptions configuration information code from the DI container as follows. Note that the generic parameters of the GetRequiredService function should be packaged with IOptions<>.

   var appServiceOptionsWrapper=<IOptions<AppServiceOptions>>();
   AppServiceOptions appServiceOptions= ;

The code that injects AppServiceOptions into FormMain, just like actively obtaining AppServiceOptions instances from DI containers, you need to use the IOptions<> interface to package the constructed sub-formal parameters.

public partial class FormMain : Form
    { 
        private readonly IConfiguration _configuration; 
        private readonly ILogger _logger;
        private AppServiceOptions _appServiceOptions;

        /// &lt;summary&gt;
        /// Add three formal parameters to the FormMain constructor, the constructor parameters will be automatically injected due to the DI container.        /// &lt;/summary&gt;
        /// <param name="configuration">Modal parameters must be interface IConfigurations</param>        /// <param name="logger">Montally parameter must be an ILogger generic type, not an ILogger type</param>        /// <param name="appServiceOptionsWrapper">Modal parameters must be IOptions generic interface </param>        public FormMain(IConfiguration configuration, ILogger&lt;FormMain&gt; logger, IOptions&lt;AppServiceOptions&gt; appServiceOptionsWrapper)  
        {
            _configuration = configuration;
            _logger = logger;
            _appServiceOptions = ;

            InitializeComponent(); 
            var connectionString = _configuration.GetConnectionString("oeeDb");  //Read oeeDb connectionString from the configuration file            _logger.LogInformation(connectionString);   //Write connection String to log file        }

        private void button1_Click(object sender, EventArgs e)
        {
             = _appServiceOptions.AppKey;
        }
    }

.net core complex configuration section read

The appsettings file defines a complex setting item. The top level is a json array, and another array is nested inside.

"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"
        }
      ]
    }
  ]

The corresponding poco object is:

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; } = "";
}

Read the C# code of json:

();
//Instantiate a corresponding PlcDevices json array object, using <T>()var PlcDeviceSettings= ("PlcDevices").Get&lt;List&lt;PlcDevice&gt;&gt;();
// Or directly put the appsettings specified section into the DI container through <T>(), where T is List<PlcDevice>&lt;List&lt;PlcDevice&gt;&gt;(("PlcDevices"));

This is all about this article about the use of dependency injection for developing .Net6 winform programs. I hope it will be helpful to everyone's learning and I hope everyone will support me more.