当前位置: 首页 > news >正文

分享一个 .NET Core Console 项目使用依赖注入的详细例子

image

前言

依赖注入(Dependency Injection,简称DI)是一种软件设计模式,主要用于管理和组织一个软件系统中不同模块之间的依赖关系。

在依赖注入中,依赖项(也称为组件或服务)不是在代码内部创建或查找的,而是由外部系统提供给组件。

具体来说,当某个角色(如一个 C# 实例,调用者)需要另一个角色(如另一个 C# 实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。

但在依赖注入中,创建被调用者的工作不再由调用者来完成,而是由外部容器来完成,并注入给调用者。

依赖注入可以提高代码的可维护性、可测试性、可替换性和可扩展性,降低组件之间的耦合度,使得代码更加清晰和灵活,以前我们写过在 Asp.NET Core Web API 项目中如何通过内置的依赖注入容器使用依赖注入的文章《一个简单的 ASP.NET Core 依赖注入例子,提高代码的可维护性和可扩展性》,今天继续分享一个在 .NET Core Console 项目使用依赖注入的详细例子,大家可以比较两者之间的不同。

Step By Step 步骤

  1. 创建一个 .NET Core Console 项目

  2. 从 Nuget 安装以下包

    Microsoft.Extensions.DependencyInjection
    System.Data.SqlClient

  3. 创建 User 实体类:

    record User(long Id, string UserName, string Password);
    
  4. 创建接口 IUserBiz 和 IUserDAO 及其实现类(留意注释

    interface IUserBiz
    {/// <summary>/// 检查用户名、密码是否匹配/// </summary>/// <param name="userName"></param>/// <param name="password"></param>/// <returns></returns>public bool CheckLogin(string userName, string password);
    }interface IUserDAO
    {/// <summary>/// 查询用户名为userName的用户信息/// </summary>/// <param name="userName"></param>/// <returns></returns>public User? GetByUserName(string userName);
    }class UserBiz : IUserBiz
    {private readonly IUserDAO userDAO;// 通过构造方法要求注入 IUserDAO 服务public UserBiz(IUserDAO userDAO){this.userDAO = userDAO;}public bool CheckLogin(string userName, string password){var user = userDAO.GetByUserName(userName);if (user == null){return false;}else{return user.Password == password;}}
    }class UserDAO : IUserDAO
    {private readonly IDbConnection conn;// 通过构造方法要求依赖注入容器为其注入一个 IDbConnection 对象public UserDAO(IDbConnection conn){this.conn = conn;}public User? GetByUserName(string userName){using var dt = SqlHelper.ExecuteQuery(conn, $"select * from T_Users where UserName={userName}");if (dt.Rows.Count <= 0){return null;}DataRow row = dt.Rows[0];int id = (int)row["Id"];string uname = (string)row["UserName"];string password = (string)row["Password"];return new User(id, uname, password);}
    }	
    
  5. 创建数据库操作帮助类 SqlHelper

    using System.Data;static class SqlHelper
    {// 执行查询语句并返回 DataTablepublic static DataTable ExecuteQuery(this IDbConnection conn, FormattableString formattable){using IDbCommand cmd = CreateCommand(conn, formattable);DataTable dt = new DataTable();using var reader = cmd.ExecuteReader();dt.Load(reader);return dt;}// 执行查询语句并返回一个值public static object? ExecuteScalar(this IDbConnection conn, FormattableString formattable){using IDbCommand cmd = CreateCommand(conn, formattable);return cmd.ExecuteScalar();}// 执行 DML 语句public static int ExecuteNonQuery(this IDbConnection conn, FormattableString formattable){using IDbCommand cmd = CreateCommand(conn, formattable);int result = cmd.ExecuteNonQuery();return result;}// 创建 Commandprivate static IDbCommand CreateCommand(IDbConnection conn, FormattableString formattable){var cmd = conn.CreateCommand();string sql = formattable.Format;for (int i = 0; i < formattable.ArgumentCount; i++){sql = sql.Replace("{" + i + "}", "@p" + i);var parameter = cmd.CreateParameter();parameter.ParameterName = "@p" + i;parameter.Value = formattable.GetArgument(i);cmd.Parameters.Add(parameter);}cmd.CommandText = sql;return cmd;}
    }
    
  6. 打开 Program.cs,引入依赖注入命名空间

    using Microsoft.Extensions.DependencyInjection;
    
  7. 创建依赖注入容器

    // 2. 创建用于注册服务的容器
    ServiceCollection services = new ServiceCollection();
    ``
  8. 注入服务(留意注释

    // 3.1 注入 IDbConnection 服务(范围)
    services.AddScoped<IDbConnection>(sp => {string connStr = "Server=(localdb)\\mssqllocaldb;Database=TestDB;Trusted_Connection=True;MultipleActiveResultSets=true";var conn = new SqlConnection(connStr);conn.Open();return conn;
    });// 3.2 注入 IUserDAO 和 IUserBiz 服务(范围)
    // ----把UserDAO注册为IUserDAO服务的实现类
    // ----实现类的参数也会一起注入
    services.AddScoped<IUserDAO, UserDAO>();
    services.AddScoped<IUserBiz, UserBiz>();	
    
  9. 使用(留意注释

    // 调用 IServiceCollection 的 BuildServiceProvider 方法创建一个 ServiceProvider 对象
    using (ServiceProvider sp = services.BuildServiceProvider())
    {// 调用 GetRequiredService 方法获取服务var userBiz = sp.GetRequiredService<IUserBiz>();bool b = userBiz.CheckLogin("jacky", "123456");Console.WriteLine(b);
    }
    

结语

依赖注入的实现方式有多种,如构造注入、属性注入和接口注入等。

通过依赖注入,可以更容易地管理和维护系统的各个组件,轻松地将模拟依赖注入到单元测试中,更灵活地添加新功能或替换现有组件。

虽然依赖注入在软件开发中有很多优点,但在使用时也需要谨慎,以确保正确地管理和配置依赖关系,避免潜在的问题,比如违背单一职责原则等设计原则,导致代码结构混乱,维护成本增加等。

附录:完整的 Program.cs 代码(留意注释

using DI魅力渐显_依赖注入;// 1. 引用依赖注入命名空间
using Microsoft.Extensions.DependencyInjection;
using System.Data;
using System.Data.SqlClient;// 2. 创建用于注册服务的容器
ServiceCollection services = new ServiceCollection();// 3.1 注入 IDbConnection 服务(范围)
services.AddScoped<IDbConnection>(sp => {string connStr = "Server=(localdb)\\mssqllocaldb;Database=TestDB;Trusted_Connection=True;MultipleActiveResultSets=true";var conn = new SqlConnection(connStr);conn.Open();return conn;
});// 3.2 注入 IUserDAO 和 IUserBiz 服务(范围)
// ----把UserDAO注册为 IUserDAO 服务的实现类
// ----实现类的参数也会一起注入
services.AddScoped<IUserDAO, UserDAO>();
services.AddScoped<IUserBiz, UserBiz>();// 4. 使用
// 调用 IServiceCollection 的 BuildServiceProvider 方法创建一个 ServiceProvider 对象
using (ServiceProvider sp = services.BuildServiceProvider())
{// 调用 GetRequiredService 方法获取服务var userBiz = sp.GetRequiredService<IUserBiz>();bool b = userBiz.CheckLogin("jacky", "123456");Console.WriteLine(b);
}

我是老杨,一个奋斗在一线的资深研发老鸟,让我们一起聊聊技术,聊聊人生。

都看到这了,求个点赞、关注、在看三连呗,感谢支持。

相关文章:

  • 前后端实现文件上传进度条-实时进度
  • linux防止nmap扫描
  • Elasticsearch之写入原理以及调优
  • 数据结构--二叉树(二)
  • iOS 通过PacketLogger 抓包蓝牙数据包
  • 新能源汽车内卷真相
  • Redis常用命令——List篇
  • Spring Boot整合Redis
  • C#WPF数字大屏项目实战08--生产量/良品统计
  • FreeRTOS实时系统 在任务中增加数组等相关操作 导致单片机起不来或者挂掉
  • 四舍五入问题
  • 【ARM Cache 与 MMU 系列文章 7.6 -- ARMv8 MMU 配置 寄存器使用介绍】
  • HTML静态网页成品作业(HTML+CSS)—— 节日端午节介绍网页(5个页面)
  • GAT1399协议分析(8)--批量图像查询
  • 嵌入式学习——Linux高级编程复习(目录IO、软硬连接、makefile)——day38
  • 【个人向】《HTTP图解》阅后小结
  • Java知识点总结(JavaIO-打印流)
  • k8s 面向应用开发者的基础命令
  • laravel5.5 视图共享数据
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • Python socket服务器端、客户端传送信息
  • Rancher如何对接Ceph-RBD块存储
  • TypeScript实现数据结构(一)栈,队列,链表
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 服务器从安装到部署全过程(二)
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何合理的规划jvm性能调优
  • 如何实现 font-size 的响应式
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 物联网链路协议
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​【已解决】npm install​卡主不动的情况
  • ​人工智能书单(数学基础篇)
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • ​学习笔记——动态路由——IS-IS中间系统到中间系统(报文/TLV)​
  • # Redis 入门到精通(八)-- 服务器配置-redis.conf配置与高级数据类型
  • #传输# #传输数据判断#
  • (6)STL算法之转换
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (新)网络工程师考点串讲与真题详解
  • (一)项目实践-利用Appdesigner制作目标跟踪仿真软件
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)scrum常见工具列表
  • (转载)Linux 多线程条件变量同步
  • .NET 中让 Task 支持带超时的异步等待
  • .pop ----remove 删除
  • .vimrc 配置项
  • /bin/rm: 参数列表过长"的解决办法
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @Controller和@RestController的区别?
  • [ C++ ] STL---仿函数与priority_queue
  • [1525]字符统计2 (哈希)SDUT