zoukankan      html  css  js  c++  java
  • 如何在.NET Core控制台程序中使用依赖注入

    背景介绍

    依赖注入(Dependency Injection), 是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。在.NET Core MVC中
    我们可以在Startup.cs文件的ConfigureService方法中使用服务容器IServiceCollection注册接口及其实现类的映射。

    例如,当我们需要访问Http上下文时,我们需要配置IHttpContextAccessor接口及其实现类HttpContextAccessor

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }
    

    那么当我们编写一个.NET Core控制台程序的时候,我们该如何使用依赖注入呢?

    使用内置依赖注入

    在.NET Core中,内置依赖注入模块使用的程序集是Microsoft.Extensions.DependencyInjection

    所以如果希望在控制台程序中使用内置依赖注入,我们首先需要使用NUGET添加对Microsoft.Extensions.DependencyInjection程序集的引用。

    PM> Install-Package Microsoft.Extensions.DependencyInjection
    

    这里为了说明如何使用.NET Core内置的依赖注入模块, 我们创建以下2个服务接口。

        public interface IFooService
        {
            void DoThing(int number);
        }
    
        public interface IBarService
        {
            void DoSomeRealWork();
        }
    
    

    然后我们针对这2个服务接口,添加2个对应的实现类

        public class BarService : IBarService
        {
            private readonly IFooService _fooService;
            public BarService(IFooService fooService)
            {
                _fooService = fooService;
            }
    
            public void DoSomeRealWork()
            {
                for (int i = 0; i < 10; i++)
                {
                    _fooService.DoThing(i);
                }
            }
        }
    
        public class FooService : IFooService
        {
            private readonly ILogger<FooService> _logger;
            public FooService(ILoggerFactory loggerFactory)
            {
                _logger = loggerFactory.CreateLogger<FooService>();
            }
    
            public void DoThing(int number)
            {
                _logger.LogInformation($"Doing the thing {number}");
            }
        }
    
    

    代码解释

    • BarService类构造函数依赖了一个IFooService接口的实现
    • FooService类构造函数依赖一个ILoggerFactory接口的实现
    • FooService中,我们输出了一个Information级别的日志

    在以上实现类代码中,我们使用了.NET Core内置的日志模块, 所以我们还需要使用NUGET添加对应的程序集Microsoft.Extensions.Logging.Console

    PM> Install-Package Microsoft.Extensions.Logging.Console
    

    最后我们来修改Program.cs, 代码如下

    
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    
        public class Program
        {
            public static void Main(string[] args)
            {
                //setup our DI
                var serviceProvider = new ServiceCollection()
                    .AddLogging()
                    .AddSingleton<IFooService, FooService>()
                    .AddSingleton<IBarService, BarService>()
                    .BuildServiceProvider();
    
                //configure console logging
                serviceProvider
                    .GetService<ILoggerFactory>()
                    .AddConsole(LogLevel.Debug);
    
                var logger = serviceProvider.GetService<ILoggerFactory>()
                    .CreateLogger<Program>();
                logger.LogInformation("Starting application");
    
                //do the actual work here
                var bar = serviceProvider.GetService<IBarService>();
                bar.DoSomeRealWork();
    
                logger.LogInformation("All done!");
    
            }
        }
    
    

    代码解释

    • 这里我们手动实例化了一个ServiceCollection类, 这个类是IServiceCollection>接口的一个实现类,它就是一个.NET Core内置服务容器。
    • 然后我们在服务容器中注册了IFooService接口的实现类FooService以及IBarService接口的实现类BarService
    • 当时需要从服务容器中获取接口类的对应实现类时,我们只需要调用服务容器类的GetSerivce方法。

    最终效果

    运行程序,我们期望的日志,正确的输出了

    info: DIInConsoleApp.Program[0]
          Start application.
    info: DIInConsoleApp.FooService[0]
          Doing the thing 0
    info: DIInConsoleApp.FooService[0]
          Doing the thing 1
    info: DIInConsoleApp.FooService[0]
          Doing the thing 2
    info: DIInConsoleApp.FooService[0]
          Doing the thing 3
    info: DIInConsoleApp.FooService[0]
          Doing the thing 4
    info: DIInConsoleApp.FooService[0]
          Doing the thing 5
    info: DIInConsoleApp.FooService[0]
          Doing the thing 6
    info: DIInConsoleApp.FooService[0]
          Doing the thing 7
    info: DIInConsoleApp.FooService[0]
          Doing the thing 8
    info: DIInConsoleApp.FooService[0]
          Doing the thing 9
    info: DIInConsoleApp.Program[0]
          All done!
    

    使用第三方依赖注入

    除了使用内置的依赖注入模块,我们还可以直接使用一些第三方的依赖注入框架,例如Autofac, StructureMap。

    这里我们来使用StructureMap来替换当前的内置的依赖注入框架。

    首先我们需要先添加程序集引用。

    PM> Install-Package StructureMap.Microsoft.DependencyInjection
    

    然后我们来修改Program.cs文件,代码如下

    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using StructureMap;
    using System;
    
    namespace DIInConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                var services = new ServiceCollection().AddLogging();
    
                var container = new Container();
                container.Configure(config =>
                {
                    config.Scan(_ =>
                    {
                        _.AssemblyContainingType(typeof(Program));
                        _.WithDefaultConventions();
                    });
    
                    config.Populate(services);
                });
    
                var serviceProvider = container.GetInstance<IServiceProvider>();
    
                serviceProvider.GetService<ILoggerFactory>().AddConsole(LogLevel.Debug);
    
                var logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger<Program>();
                logger.LogInformation("Start application.");
    
                var bar = serviceProvider.GetService<IBarService>();
                bar.DoSomeRealWork();
    
                logger.LogInformation("All done!");
                Console.Read();
            }
        }
    }
    

    代码解释

    • 这里我们实例化了一个StructureMap的服务容器Container, 并在其Configure方法中配置了接口类及其实现类的自动搜索。这里使用的是一种约定,接口类必须以字母“I”开头, 实现类的名字和接口类只相差一个字母“I”, 例IFooService, FooService, IBarService, BarService
    • 后续代码和前一个例子基本一样。虽然看起来代码多了很多,但是实际上这种使用约定的注入方式非常强力,可以省去很多手动配置的代码。

    最终效果

    运行程序,代码和之前的效果一样

    info: DIInConsoleApp.Program[0]
          Start application.
    info: DIInConsoleApp.FooService[0]
          Doing the thing 0
    info: DIInConsoleApp.FooService[0]
          Doing the thing 1
    info: DIInConsoleApp.FooService[0]
          Doing the thing 2
    info: DIInConsoleApp.FooService[0]
          Doing the thing 3
    info: DIInConsoleApp.FooService[0]
          Doing the thing 4
    info: DIInConsoleApp.FooService[0]
          Doing the thing 5
    info: DIInConsoleApp.FooService[0]
          Doing the thing 6
    info: DIInConsoleApp.FooService[0]
          Doing the thing 7
    info: DIInConsoleApp.FooService[0]
          Doing the thing 8
    info: DIInConsoleApp.FooService[0]
          Doing the thing 9
    info: DIInConsoleApp.Program[0]
          All done!
    

    本篇源代码

  • 相关阅读:
    241. Different Ways to Add Parentheses java solutions
    89. Gray Code java solutions
    367. Valid Perfect Square java solutions
    46. Permutations java solutions
    116. Populating Next Right Pointers in Each Node java solutions
    153. Find Minimum in Rotated Sorted Array java solutions
    判断两颗树是否相同
    求二叉树叶子节点的个数
    求二叉树第k层的结点个数
    将二叉排序树转换成排序的双向链表
  • 原文地址:https://www.cnblogs.com/lwqlun/p/9736391.html
Copyright © 2011-2022 走看看