分享一个 .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
    {
    	// 执行查询语句并返回 DataTable
        public 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;
        }
    
    	// 创建 Command
        private 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);
}

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/691566.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vitis HLS 学习笔记--global_array_RAM初始化及复位

目录 1. 简介 2. 示例代分析 2.1 源代码 2.2 URAM 不可用 2.3 代码功能解释 2.4 综合报告 2.4.1 顶层控制接口 2.4.2 软件 IO 信息 2.4.3 存储绑定 3. 对比两种 solution 3.1 solution_A 3.2 solution_B 4. 总结 1. 简介 在C程序中&#xff0c;数组是一种基本的…

每日5题Day18 - LeetCode 86 - 90

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;86. 分隔链表 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;…

为下一波创新做准备:人工智能和元宇宙

人工智能和元宇宙的发展带来了独特的可能性和挑战。随着这些技术的发展&#xff0c;我们进入了一个沉浸式虚拟体验和智能系统的时代&#xff0c;我们正站在一个历史性的时刻。为迎接下一波创新&#xff0c;采取必要的措施是很重要的。 我们正在见证两项变革性技术的激动人心的发…

【TB作品】msp430f5529单片机,dht22,温湿度传感器,OLED显示屏

使用DHT22温湿度传感器和OLED显示屏的单片机项目 博客名称 利用MSP430单片机读取DHT22并显示温湿度 作品功能 本项目利用MSP430单片机读取DHT22温湿度传感器的数据&#xff0c;并将温湿度信息显示在OLED显示屏上。通过这个项目&#xff0c;您可以学习如何使用单片机与传感器…

计算机二级Access操作题总结——简单应用

查询设计 创建一个查询&#xff0c;能够在客人每次结账时根据客人的姓名提示统计这个客人已住天数和应交金额&#xff0c;并显示“姓名”、“房间号”、“已住天数”和“应交金额”&#xff0c;所建查询命名为“qT2”。 注&#xff1a;输入姓名时应提示“请输入姓名”。已住天…

vscode输出控制台中文显示乱码最有效解决办法

当VSCode的输出控制台中文显示乱码时&#xff0c;一个有效的解决办法是通过设置环境变量来确保编码的正确性。以下是解决方式&#xff1a; 首先&#xff0c;设置环境变量以修正乱码问题&#xff1a; 如果上述方法没有解决乱码问题&#xff0c;请继续以下步骤&#xff1a; 右键…

编译原理-语法分析(实验 C语言)

语法分析 1. 实验目的 编制一个递归下降分析程序&#xff0c;实现对词法分析程序所提供的单词序列的语法检查和结构分析 2. 实验要求 利用C语言编制递归下降分析程序&#xff0c;并对简单语言进行语法分析 2.1 待分析的简单语言的语法 用扩充的BNF表示如下&#xff1a; …

Spring AOP(实现,动态原理)详解版

Spring AOP 1.什么是AOP&#xff1f;1.1引入AOP依赖1.2编写AOP程序 2.Spring AOP核⼼概念2.1 切点(Pointcut)2.2连接点(Join Point)2.3通知(Advice)2.4 切⾯(Aspect) 3.通知类型3.1顺序3.2切⾯优先级 Order3.3 ⾃定义注解 MyAspect 4. Spring AOP 原理5 动态代理怎么实现5.1 JD…

BeagleBone Black入门总结

文章目录 参考连接重要路径系统镜像下载访问 BeagleBone 参考连接 镜像下载启动系统制作&#xff1a;SD卡烧录工具入门书籍推荐&#xff1a;BeagleBone cookbookBeagleBone概况&#xff1f; 重要路径 官方例程及脚本路径&#xff1a;/var/lib/cloud9BeagleBone Cookbook例程…

03-3.5.1~4 特殊矩阵的压缩存储

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

最新区块链论文速读--CCF A会议 CCS 2023 共25篇 附pdf下载(3/4)

Conference&#xff1a;ACM Conference on Computer and Communications Security (CCS) CCF level&#xff1a;CCF A Categories&#xff1a;network and information security Year&#xff1a;2023 Num&#xff1a;25 第1~7篇区块链文章请点击此处查看 第8~13篇区块链文…

【介绍下什么是Kubernetes编排系统】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

java线程池介绍

在Java中&#xff0c;线程池是用来管理和复用线程的一种机制&#xff0c;它可以显著提升程序性能&#xff0c;特别是在大量短期异步任务的场景下。以下是创建和使用线程池的基本步骤&#xff1a; 1.创建线程池: 使用java.util.concurrent.Executors类的静态工厂方法创建线程池&…

【c语言】自定义类型----结构体

结构体是c语言的一种自定义类型&#xff0c;自定义类型对于开发者及其重要的类型&#xff0c;它可以随意由开发者进行谱写功能&#xff0c;而今天的结构体可以用来表示一种变量的单个或多种具体属性&#xff0c;再编写代码时有着不可替代的作用&#xff01;&#xff01;&#x…

编译原理-词法分析(实验 C语言)

编译原理-词法分析 1. 实验目的 设计、编写并调试一个词法分析程序&#xff0c;加深对词法分析原理的理解 2. 实验要求 2.1 待分析的简单语言的词法 关键字&#xff1a;begin&#xff0c;if&#xff0c;then&#xff0c;while&#xff0c;do&#xff0c;end 所有关键字都是…

Nginx(openresty) 查看连接数和并发送

1 通过浏览器查看 #修改nginx配置文件 location /status {stub_status on;access_log off;allow 192.168.50.0/24;deny all;} #重新加载 sudo /usr/local/openresty/nginx/sbin/nginx -s reloadActive connections //当前 Nginx 当前处理的活动连接数。 server accepts handl…

HPC: perf入门

如果你想查看你的程序在cpu上运行时&#xff0c;耗时时如何分布的&#xff0c;那么perf是一个合理的选择。 准备工作 为了支持使用perf&#xff0c;首先你要安装相关的库 sudo apt install linux-tools-5.15.0-67-generic此外&#xff0c;因为使用perf进行benchmark&#xf…

四种跨域解决方案

文章目录 1.引出跨域1.基本介绍2.具体演示1.启动之前学习过的springboot-furn项目2.浏览器直接访问 [localhost:8081/furns](http://localhost:8081/furns) 可以显示信息3.启动前端项目&#xff0c;取消请求拦截器&#xff0c;这样设置&#xff0c;就会出现跨域4.跨域原因 2.跨…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…

零基础入门学用Arduino 第二部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…