传统开发中,对象都是开发者创建组装,开发者必须了解各类的使用方法且某些类的耦合度较高,例如想把sql serve数据库改为MySql数据库则需要更改某些代码。
控制反转的目的是让框架完成对象的创建和组装。从“我创建对象”编程“我要对象”,实现控制反转主要有两种方式:
假设框架中有个ServiceLocator类,可以直接调用GetService方法便可以获得想要的对象。
IDbConnection con = ServiceLocator.GetService
一个对象中需要包含其他类型的对象,则在创建该对象的时候,框架会自动创建所需类型的对象。
容器:负责提供对象的注册和获取功能的框架
服务:注册到容器中的对象
声明周期:获取服务的时候是创建一个新对象还是用之前对象
使用建议:
如果一个类无状态,建议设置为单例;否则,框架环境有范围控制,则周期设置为范围;使用瞬态周期时要尽可能在自范围中使用,否则容易造成内存泄漏。
不同服务之间具有依赖关系,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
类型