• .Net DI(Dependency Injection)依赖注入机制


    1、简介

      DI:Dependency Injection,即依赖注入,他是IOC的具体实现。

      在DI中,底层服务对象不再负责依赖关系的创建,而是交由顶端调用进行管理注入

      好处:降低组件之间的耦合度,使代码更加灵活

    2、实例

      我们举个例子,有个User Login的功能,Login需要通过DB验证,DB需要读取Config和进行Log记录

      依赖关系如图

       

      DI的概念,就是把DB的依赖(Config&Log)提到User层,该怎么实现呢?

      接着往下走...

    3、代码结构

      通过代码我们来看一下原理。

      框架说明:

        1)Service类库:Standard2.1,类库推荐都使用Standard,这样可以在Framework、core、net之间通用

        2)UserSite:Net5 控制台程序

      

     

      编码内容:

      ConfigService,包含两种实现方式

    复制代码
    namespace ConfigService
    {
        public interface IConfig
        {
            public string GetValue(string key);//获取name的值
        }
    }
    
    
    using System;
    namespace ConfigService
    {
        public class EnvironmentConfig : IConfig //从环境变量里面读取配置信息,自行添加到电脑User里面
        {
            public string GetValue(string key)
            {
                return Environment.GetEnvironmentVariable(key, EnvironmentVariableTarget.User);
            }
        }
    }
    
    
    using System.IO;
    using System.Linq;
    namespace ConfigService
    {
        public class IniFileConfig : IConfig //从ini文件读取配置信息,UserSite如果注入此服务,需要创建ini文件:key=value   就行
        {
            string _filePath;
            public IniFileConfig(string filePath)
            {
                this._filePath = filePath;
            }
            public string GetValue(string key)
            {
                string str = "";
                var kv = File.ReadAllLines(_filePath)
                    .Select(x => x.Split("="))
                    .Select(x => new { key = x[0], value = x[1] })
                    .SingleOrDefault(x => x.key == key);
                return kv?.value;
            }
        }
    }
    复制代码

      LogService,简单实现

    复制代码
    namespace LogService
    {
        public interface ILog
        {
            public void LogInfo(string msg);
            public void LogError(string msg);
        }
    }
    
    
    using System;
    namespace LogService
    {
        public class ConsoleLog : ILog
        {
            public void LogInfo(string msg)
            {
                Console.WriteLine($"Info:{msg}");
            }
            public void LogError(string msg)
            {
                Console.WriteLine($"Error:{msg}");
            }
        }
    }
    复制代码

      DBService,只是做思路演示,这边只要读取到dblink就算访问数据库成功

    复制代码
    namespace DBService
    {
        public interface IDBHelper
        {
            public bool CheckUser(string acc, string pwd);
        }
    }
    
    using LogService;
    using ConfigService;
    namespace DBService
    {
        public class SqlServerHelper : IDBHelper
        {
            private IConfig _config;
            private ILog _log;
            public SqlServerHelper(IConfig config,ILog log) //Net默认从构造函数进行以来注入
            {
                this._config = config;
                this._log = log;
            }
            public bool CheckUser(string acc, string pwd)
            {
                var dblink = this._config.GetValue("dblink");
                this._log.LogInfo($"获取数据库链接={dblink}");
                if (string.IsNullOrWhiteSpace(dblink))
                {
                    this._log.LogError($"登录失败");
                    return false;
                }
                this._log.LogInfo($"登录成功-{acc}-{pwd}");
                return true;
            }
        }
    }
    复制代码

      主题来了,UserSite Program代码如下:

      我们需要注入SqlServerHelper服务,就需要将其依赖项一同注入,这样就将具体实现类提到了顶端注入,从而进行解耦

      比如:读取配置文件,我希望从哪读取,就注入哪一个(注意,都注入的话,IConfig会以后注入的为准)

    复制代码
    using System;
    using ConfigService;
    using LogService;
    using DBService;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace UserSite
    {
        class Program
        {
            static void Main(string[] args)
            {
                ServiceCollection services = new ServiceCollection();
                services.AddScoped();
                services.AddScoped(); //1、我希望从环境变量读取配置
                services.AddScoped(typeof(IConfig), x => new IniFileConfig("db.ini")); //2、我希望从ini读取配置
                services.AddScoped();
                using (var sp = services.BuildServiceProvider())
                {
                    var service = sp.GetRequiredService();
                    service.CheckUser("kxy", "123");
                };
                Console.ReadLine();
            }
        }
    }
    复制代码

      DI的简单原理就是这样。。

    4、Net Core Api DI如何实现

      依赖注入一般是在StartUp实现,Net6以后取消了StartUp,将builder迁移至Program

    builder.Services.AddScoped();

      正常是在构造函数注入

    复制代码
    using DIDemo.Services;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    namespace DIDemo.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class HomeController : ControllerBase
        {
            IDBHelper _dBHelper;
            public HomeController(IDBHelper dBHelper) { 
                this._dBHelper = dBHelper;
            }
            [HttpGet]
            public bool Get(string acc,string pwd)
            {
                return _dBHelper.CheckUser(acc, pwd);
            }
        }
    }
    复制代码

      当然,如果一些服务构造起来非常耗时,在一个控制器下面又只是一两个api需要使用到,

      如果使用构造函数注入,所有api的访问都会进行依赖注入,这是十分不友好的,即使首次注入后服务器会缓存资源

      可更换为函数注入

    复制代码
    using DIDemo.Services;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    namespace DIDemo.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class HomeController : ControllerBase
        {
            [HttpGet]
            public bool Get([FromServices] IDBHelper _dBHelper, string acc,string pwd)
            {
                return _dBHelper.CheckUser(acc, pwd);
            }
        }
    }
    复制代码

     

     

      

     

  • 相关阅读:
    Reading Notes For Introduction To Linux
    迷茫中翻滚
    报告下载|《云原生安全威胁分析报告》
    数据结构-栈和队列(一)
    Multisim14.0仿真(十四)电压跟随器
    高性能分布式No SQL数据库Aerospike——常用工具使用
    vite-plugin-vue-setup-extend和unplugin-auto-import让v3开发更丝滑
    关于Ubuntu ssh远程连接报错和无法root登录的解决方法
    【youcans 的 OpenCV 例程200篇】180.基于距离变换的分水岭算法
    【数据聚类】第八章第一节:谱聚类算法概述及拉普拉斯矩阵
  • 原文地址:https://www.cnblogs.com/wskxy/p/17172299.html