Examples of injection based on the net Core3.1 implementation-dependent winform

Original: WinForm dependency injection instance based on net Core3.1

Examples of injection based on the net Core3.1 implementation-dependent winform

1. Background

net core3.1 is Microsoft's long-term 3-year support LTS version, released on 2019-12-03, and supports the Winfrom with WPF desktop application on windows platform. This article describes the first step for use of Winform, and the application layer relates to DBConfig ORM, storage layer and the like dependency poured into the container, from the container and call instance by the constructor method, supplied to each form controls used.
Note: Dependency injection explanation herein is based on Microsoft's own native DI, can be modeled by Ninject or AutoFac operation itself, the principles of communication.

2. Dependency Injection

2.1 Dependency injection is what?

Dependency injection is controlled by reversing (the IOC), design patterns belong + factory mode Proxy mode, an example of the type of interface or call instance, the life cycle of the serviceProvider injection set, the control instance instantiation and configuration lifecycle, and returns to the examples programmers call, so as to achieve the liberation of programmer productivity, do not go to a new instance, do not take up consideration of dependencies between instance, consider the examples do not take the life cycle. Implemented, divided into three stages, a first, container programmer injected a service phase, the second phase of the programmer calls DI instance, the third instance serviceProvider service manager returns to the program according to the configuration corresponding to the time of injection and configured examples life cycle.

FIG dependency can be appreciated that an injection procedure call instance

Source source , thanks author.

Here again the reader is to be explained ServiceCollection service container, serviceProvider service manager, manages the service container, when the program sends the abstract interface, or type, serviceProvider based on the life cycle of a good set, returns an instance of good examples of configuration required life cycle for programmers.

2.1 dependency injection purposes

serviceProvider inversion of control through a proxy mode, he will hold control, will need to use all of the interface type, reflecting the corresponding instance, instantiate and set a good example of the life cycle, and then return control to the programmer, do not go to a new instance, do not take up consideration of dependencies between instances, do not take consider the life cycle of the instances, the ultimate goal is the liberation of programmer productivity, allowing programmers to more easily write programs.

2.2 rely on the benefits brought about by injection

Control 2.2.1 Life Cycle

While implantation may be provided the following three life cycle:

  • Transient
    during each injection, are re-new a new instance.
  • Scoped
    Each Request will re-new a new instance of the same Request Pipeline through no matter how many are using the same instance.
  • Singleton
    is instantiated after not disappear, there will only be one instance of the program is running.

    2.2.1.1 life cycle test example

  • Example corresponding to a defined three different life cycle of the interface
public interface ISample
{
    int Id { get; }
}

public interface ISampleTransient : ISample
{
}

public interface ISampleScoped : ISample
{
}

public interface ISampleSingleton : ISample
{
}

public class Sample : ISampleTransient, ISampleScoped, ISampleSingleton
{
    private static int _counter;
    private int _id;

    public Sample()
    {
        _id = ++_counter;
    }

    public int Id => _id;
}
  • The service corresponding to the interface register to the vessel
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddTransient<ISampleTransient, Sample>();
        services.AddScoped<ISampleScoped, Sample>();
        services.AddSingleton<ISampleSingleton, Sample>();
        // Singleton 也可以用以下方法注册
        // services.AddSingleton<ISampleSingleton>(new Sample());
    }
}
  • Controller obtain a corresponding instance of DI HashCode
public class HomeController : Controller
{
    private readonly ISample _transient;
    private readonly ISample _scoped;
    private readonly ISample _singleton;

    public HomeController(
        ISampleTransient transient,
        ISampleScoped scoped,
        ISampleSingleton singleton)
    {
        _transient = transient;
        _scoped = scoped;
        _singleton = singleton;
    }

    public IActionResult Index() {
        ViewBag.TransientId = _transient.Id;
        ViewBag.TransientHashCode = _transient.GetHashCode();

        ViewBag.ScopedId = _scoped.Id;
        ViewBag.ScopedHashCode = _scoped.GetHashCode();

        ViewBag.SingletonId = _singleton.Id;
        ViewBag.SingletonHashCode = _singleton.GetHashCode();
        return View();
    }
}
  • VewBag display components
<table border="1">
    <tr><td colspan="3">Cotroller</td></tr>
    <tr><td>Lifetimes</td><td>Id</td><td>Hash Code</td></tr>
    <tr><td>Transient</td><td>@ViewBag.TransientId</td><td>@ViewBag.TransientHashCode</td></tr>
    <tr><td>Scoped</td><td>@ViewBag.ScopedId</td><td>@ViewBag.ScopedHashCode</td></tr>
    <tr><td>Singleton</td><td>@ViewBag.SingletonId</td><td>@ViewBag.SingletonHashCode</td></tr>
</table>

Can do their own testing, specifically refer to this blog

2.2.2 to achieve a decoupling between the presentation layer (the caller) and service categories

As above, the example is to call HomeController instance via the interface, and therefore only needs to be diverted to modify the program in the example, without the need to modify the call level.
This is consistent with the design principles depend on six major program Inversion Principle:
1. The high-level modules should not depend on low-level modules, both of which should rely on abstract
presentation layer Controller Model layer does not depend on the Sample class, both of which depend on the interface Sample abstract ISample, ISampleTransient, ISampleScoped, ISampleSingleton.
2. abstract should not rely on the details of
the interface specification defines only layer, does not define details.

public interface ISample
{
    int Id { get; }
}

public interface ISampleTransient : ISample
{
}

public interface ISampleScoped : ISample
{
}

public interface ISampleSingleton : ISample
{
}

3. details should depend on abstractions
DI fetch instance dependency on the interface:

ISampleTransient transient;

Implement the service class also depends on the interface:

public class Sample : ISampleTransient, ISampleScoped, ISampleSingleton
{
    private static int _counter;
    private int _id;

    public Sample()
    {
        _id = ++_counter;
    }

    public int Id => _id;
}

2.2.3 the developers did not have to consider the relationship between the dependent

Programmers do not have to consider the dependencies between the various DI instances, as well as a number of new instances interdependent.

2.3 Dependency Injection design pattern

2.3.1 proxy mode

Where the service call dependency injection container made from the hands of managers serviceProvider programmer control, service control required to instantiate and set up his life cycle, and then returned to the programmer.

2.3.2 Factory Pattern

The DI life cycle setting, or according to the interface type, an example of the production of the various life cycle, to be noted that there might be instances of the same (single request in scope, Transient or life cycle), each produced Transient It is a new instance.

3. Based on the dependency injection winform Net Core 3.1

3.1 Net Core 3.1 support for the winform.

I found in the latest release of VS can create winform project, but can not open the designer can not open winform toolbox. How to do?
In Microsoft's official blog mentioned support winform designer in VS16.5 Preview, depending mentioned in the blog, you need to download the link to download VS16.5 preview.

NetCore3.1 winform shots are as follows:


Controls can be seen clearly dot Net Framework is based on a lot of good-looking than the same time, very little toolbox controls, Microsoft has put some of the old alternative controls removed, and the future will slowly add the necessary controls.

3.2 winform rely on different injection of the net core MVC?

net core MVC container is automatically created good, you only need to configure the service in the ConfigureServices method. After a while on the Net Core3.1 winform project to create a new instance of the form is in the form of a single case of running. Configuration container is created, you need to do it themselves.

    static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

If you need to inject that service to Form the form you need to pass arguments when the new instance.

[STAThread]
  static void Main()
  {
      Application.SetHighDpiMode(HighDpiMode.SystemAware);
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
 
      var services = new ServiceCollection();
 
      ConfigureServices(services);
 
      using (ServiceProvider serviceProvider = services.BuildServiceProvider())
      {
          var logg = services.BuildServiceProvider().GetRequiredService<ILogger<Form1>>();
 
          var businessObject = services.BuildServiceProvider().GetRequiredService<IBusinessLayer>();
 
          Application.Run(new Form1(logg, businessObject));
      }
  }

Service interface to call when invoked with the constructor of the form.

public partial class Form1 : Form
    {
        private readonly ILogger _logger;
 
        private readonly IBusinessLayer _business;
        public Form1(ILogger<Form1> logger, IBusinessLayer business)
        {
            _logger = logger;
            _business = business;
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                _logger.LogInformation("Form1 {BusinessLayerEvent} at {dateTime}", "Started", DateTime.UtcNow);
 
                // Perform Business Logic here 
                _business.PerformBusiness();
 
                MessageBox.Show("Hello .NET Core 3.0 . This is First Forms app in .NET Core");
 
                _logger.LogInformation("Form1 {BusinessLayerEvent} at {dateTime}", "Ended", DateTime.UtcNow);
 
            }
            catch (Exception ex)
            {
                //Log technical exception 
                _logger.LogError(ex.Message);
                //Return exception repsponse here
                throw;
 
            }
 
        }
    }

This method is taken from this article

This has at least two disadvantages:

  1. Dependence on Form1 constructor injection example calls leak in his call level, it does not meet the design principles rely on six major program Inversion Principle;
  2. When Form1 need to increase the call from the interface DI instance, it is also necessary to increase the corresponding argument in the calling code as follows. And more arguments, will be very lengthy.
    Application.Run(new Form1(logg, businessObject));

    3.3 Solutions 3.2

    The form is also injected in the form of a single type of embodiment of the container, when invoked, obtain MainForm type of service. Such examples of this service depends on other services. ServiceProvider container manager will automatically resolve dependencies between services and corresponding services instantiate and set up according to the life cycle, to the programmer to use. Problem perfect solution.

此思路有借鉴于以下两篇文章
微软MSDN
stackoverflow
这里向大家重点推荐下stackoverflow,这个基于世界级的程序员论坛,在我遇到很多的疑难杂症,孤立无援的时候,他都会给予我解决问题的思路,方向甚至方案,再次致敬感谢stackoverflow,同时也感谢谷歌。

3.4代码实现

3.4.1 在Program.cs中建立服务注册静态方法

        private static void ConfigureServices(ServiceCollection services)
        {
            //App
            services.ApplicationServiceIoC();
            //Infra

            //Repo
            services.InfrastructureORM<DapperIoC>();


            //Presentation 其他的窗体也可以注入在此处
            services.AddSingleton(typeof(MainForm));
        }

这里需要说明的是,笔者这里的IoC是应用层,展现层,仓储层分层注入了,每层都写了ServiceCollection服务容器的静态方法,所以服务可以在各层注入,读者可以不去追究,将自己的服务注入在此即可。
分层注入:

分层注入简单实现
CameraDM_Service注册在了ApplicationServiceIoC,ApplicationServiceIoC注册在了ConfigureServices。这就是我刚说的分层注入每层的依赖。

    public static class ServicesIoC
    {
        public static void ApplicationServiceIoC(this IServiceCollection services)
        {
            services.AddScoped(typeof(IServiceBase<>), typeof(ServiceBase<>));
            services.AddScoped<ICameraDM_Service, CameraDM_Service>();
        }
    }

重点关注
将窗体类型注入,当然后续加入其它窗体也可用同样方法进行注入。

services.AddSingleton(typeof(MainForm));

3.4.2 创建服务容器对象

var services = new ServiceCollection();

3.4.3 添加服务注册

 ConfigureServices(services);

此步骤调用的就是3.4.1中的方法。

3.4.4 构建ServiceProvider对象

  var serviceProvider = services.BuildServiceProvider();

3.4.5 运行MainForm服务

向服务管理者请求MainForm类型的实例服务,具体调用过程详见2.1。

Application.Run(serviceProvider.GetService<MainForm>()); 

这一步是重点,也是winform跟MVC使用上的区别,但是本质却是相同的,都是由serviceProvider管理着WPF,winform或者MVC这些实例以及他们对应的类型,只不过MVC容器已经创建好了,容器管理者serviceProvider也已经创建好了,直接往容器里Add服务即可,而winform,WPF,net core控制台程序需要我们自己去往容器里添加注册服务,并且创建容器管理者serviceProvider。因为ServiceCollection容器是死的,只有创建了serviceProvider容器管理者这个代理角色,容器才能体现出他的价值。而只有serviceProvider,没有ServiceCollection里的服务也是毫无意义的。

3.4.1到3.4.5整体代码如下:

    static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //创建服务容器对象
            var services = new ServiceCollection();

            //添加服务注册
            ConfigureServices(services);
            //构建ServiceProvider对象
            var serviceProvider = services.BuildServiceProvider();
            //向服务管理者请求MainForm类型的实例服务
            Application.Run(serviceProvider.GetService<MainForm>());    
        }
        private static void ConfigureServices(ServiceCollection services)
        {
            //App
            services.ApplicationServiceIoC();
            //Infra

            //Repo
            services.InfrastructureORM<DapperIoC>();


            //Presentation 其他的窗体也可以注入在此处
            services.AddSingleton(typeof(MainForm));
        }
    }

3.4.6构造函数法调用DI实例

    public partial class MainForm : Form
    {
        ICameraDM_Service _cameraDM_Service;
        public MainForm(ICameraDM_Service cameraDM_Service)
        {
            _cameraDM_Service = cameraDM_Service;
            InitializeComponent();          
        }
        private async void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(_cameraDM_Service.GetAllCameraInfo().ToList().Count().ToString());
            var _camera  =await _cameraDM_Service.GetAllIncludingTasksAsync();
            //textBox1.Text = _camera.ToList().Count().ToString();
            var _cameraNo3 = await _cameraDM_Service.GetByIdAsync(3);
            textBox1.Text = _cameraNo3.InstallTime.ToString();
        }
    }

3.5演示效果

点击按钮之后从摄像头服务中获取到了摄像头的数量。

点击确定之后从摄像头服务中获取到了3号摄像头的安装时间。

4.最后

本来就想写篇短文,谁知道洋洋洒洒还写得有点长。本文如果大家读了有疑惑,请提出来,我会耐心解答;如果知识点上有不妥当不正确或者不同见解的地方,也恳请指出,我同时也很渴望进步。最后祝大家冬至安康,阖家幸福。

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/12080956.html