3.1依赖注入

举报
步步为营 发表于 2023/02/14 11:21:27 2023/02/14
【摘要】 传统开发中,对象都是开发者创建组装,开发者必须了解各类的使用方法且某些类的耦合度较高,例如想把sql serve数据库改为MySql数据库则需要更改某些代码。控制反转的目的是让框架完成对象的创建和组装。从“我创建对象”编程“我要对象”

3.1依赖注入

控制反转

传统开发中,对象都是开发者创建组装,开发者必须了解各类的使用方法且某些类的耦合度较高,例如想把sql serve数据库改为MySql数据库则需要更改某些代码。

控制反转的目的是让框架完成对象的创建和组装。从“我创建对象”编程“我要对象”,实现控制反转主要有两种方式:

服务定位器

假设框架中有个ServiceLocator类,可以直接调用GetService方法便可以获得想要的对象。

IDbConnection con = ServiceLocator.GetService<IDbConnection>();

依赖注入

一个对象中需要包含其他类型的对象,则在创建该对象的时候,框架会自动创建所需类型的对象。

容器:负责提供对象的注册和获取功能的框架

服务:注册到容器中的对象

依赖注入的基本使用

声明周期:获取服务的时候是创建一个新对象还是用之前对象

  1. 瞬态(transient):每次请求都创建一个新对象。
  2. 范围(scoped):在给定范围内,多次请求共享一个对象;在不同范围内,服务每次被请求的时候返回不同对象。ASP.NET Core中,同一次http请求,不同的注入会获得同一对象。
  3. 单例(singleton):全局共享一个服务对象。

使用建议:

如果一个类无状态,建议设置为单例;否则,框架环境有范围控制,则周期设置为范围;使用瞬态周期时要尽可能在自范围中使用,否则容易造成内存泄漏。

不同服务之间具有依赖关系,A服务有一个B服务的属性,那么B的声明周期不能比A短

依赖注入框架中注册服务的时候,可以设定服务类型和实现类型,这两者可以不相同。例:

  • 服务和实现类型都是SqlConnection时,在获取SqlConnection服务时,会返回SqlConnection对象

  • 服务是IDbConnection接口类型,实现类型都是SqlConnection时,在获取IDbConnection接口服务时,会返回SqlConnection对象

服务定位器案例:

using Microsoft.Extensions.DependencyInjectiony;
    
  
public interface ITestService
{
    public string Name { get; set; }
    public void SayHi();
}

public class TestServiceImpl : ITestService
{
    public string Name { get; set; }
    public void SayHi()
    {
        Console.WriteLine($"Hi, I'm {Name}");
    }
}
//获取服务之前要先注册服务

ServiceCollection services = new ServiceCollection();//1.创建用于注册服务的容器
//AddTransient、AddScoped、和AddSingleton分别注册瞬态、范围、单例服务
services.AddTransient<TestServiceImpl>();//2.注册一个瞬时服务,注册的服务必须涵盖程序中所有的所需服务
//ServiceProvider服务定位器
using (ServiceProvider sp = services.BuildServiceProvider())//3.获取服务定位器
{
    //4.通过调用GetRequiredService方法获得对象
    TestServiceImpl testService = sp.GetRequiredService<TestServiceImpl>();
    testService.Name = "tom";
    testService.SayHi();
}

依赖注入案例

接口—要注册的服务

interface IUserBiz{public bool CheckLogin(string userName, string password);}
interface IUserDAO{public User? GetByUserName(string userName);}

实现类

class UserBiz : IUserBiz
{
    private readonly IUserDAO userDao;//所依赖的对象

    public UserBiz(IUserDAO userDao) //构造函数中要求容器中必须注入IUserDAO服务
    {
        this.userDao = userDao;
    }

    public bool CheckLogin(string userName, string password)
    {...
    }
}

class UserDAO: IUserDAO
{
    private readonly IDbConnection conn;//所依赖的对象

    public UserDAO(IDbConnection conn)//构造函数中要求容器中必须注入IDbConnection服务
    {
        this.conn = conn;
    }

    public User? GetByUserName(string userName)
    {...
    }
}

组装服务

ServiceCollection services = new ServiceCollection();//1.创建用于注册服务的容器
//2.注册的服务必须涵盖程序中所有的所需服务
//注册IDbConnection服务
services.AddScoped<IDbConnection>(sp => {
    string connStr = "Data Source=.;Initial Catalog=DI_DB;Integrated Security=true";
    var conn = new SqlConnection(connStr);
    conn.Open();
    return conn;
});
services.AddScoped<IUserDAO, UserDAO>();//2.1.上面指定需要注入IUserDAO服务,但是要用UserDAO类来实现
services.AddScoped<IUserBiz, UserBiz>();
using (ServiceProvider sp = services.BuildServiceProvider())//3.获取服务定位器
{
    var userBiz = sp.GetRequiredService<IUserBiz>();
    bool b = userBiz.CheckLogin("yzk", "123456");
    Console.WriteLine(b);
}

依赖注入的传染性:一个对象是通过依赖注入创建的,那么这个类的构造函数中所有的参数都是依赖注入赋值。但是如果一个类是手动创建,那么构造函数中所有的参数不是依赖注入。所以一旦使用依赖注入,则应避免使用new来创建。

如果一个服务有多个实现对象,可以被参数声明为IEnumerable<T>类型

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。