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!
    

    本篇源代码

  • 相关阅读:
    命令行解释器(shell)
    TensorFlow经典案例2:实现最近邻算法
    TensorFlow经典案例1:基本操作
    TensorBoard在谷歌浏览器显示找不到网页
    Pandas中的DataFrame.filter()
    【转】保证训练集和测试集取到和数据集中相同比例的类别
    数据分析小实践:统计每个国家存在心理健康问题的平均年龄
    TabActivity 切换Activity界面
    Android获取屏幕实际高度跟显示高度,判断Android设备是否拥有虚拟功能键
    非常简单的XML解析(SAX解析、pull解析)
  • 原文地址:https://www.cnblogs.com/lwqlun/p/9736391.html
Copyright © 2011-2022 走看看