对于刚开上手的朋友来说,可能不知道什么是依赖注入和控制反转;不管知道也好,不知道也罢,今天我们来看下依赖注入和控制反转的知识,以及在.Net Core上的使用。文章有点长,希望对小伙伴有点帮助,也希望大伙帮忙点赞、在看、转发和关注本公众号。
依赖注入
依赖注入(Dependency Injection,简称:DI),是一种设计模式,用于避免代码内部高耦合。根据软件开发的最佳实践和五大原则(S.O.L.I.D),一个类应该依赖于抽象而不是具体类。
依赖项是类需要完成工作的对象,换句话说,该对象不是在类内部创建的。依赖项被注入到使用它的类中,所以该依赖项必须是外部,对象的实例化不能在类内部,只能通过外部。我们来看个例子:public class TaskService
{
private readonly ITaskRepository _taskRepository;
public TaskService(ITaskRepository taskRepository)
{
if(taskRepository==null)
throw new ArgumentException(“service”);
_taskRepository = taskRepository;
}
public int Done(int taskId)
{
var task = _taskRepository.Get(taskId);//根编号获取Task对象
task.Status=1;//状态设置为完成
return _taskRepository.Execute(task);//提交到数据库,并返回结果
}
}
程序说明:
- 在此范例中,TaskService采用构造函数注入,要求外界传入一个符合ITaskRepository接口的对象。若外界传入不兼容于此接口的类型,代码将无法通过编译。
- 在定义私有成员变量_taskRepository时加上了readonly修饰,是为了确保依赖对象在TaskService的构造函数中设定完成后,就不能在改变了。
- 构造函数还检查了传入的依赖对象是否为null,以确保构造函数执行完毕,在此类中的任何方法都可以执行,而无需担心参数为null。
使用依赖注入的好处:
- 低耦合
- 简单
- 易于维护
- 易读且代码简洁
- 便于测试
注入方式
1)构造函数
构造函数注入(Constructor Injection)指的是类型所需要的对象是由外界通过该类型的公开构造函数传入。如果需要注入N个依赖对象,则构造函数通常至少要有N个自变量,且自变量类型为接口或抽象类型。用法及示例说明请看上面示例。
这里一个小提示,如果要使用依赖注入,建议优先考虑构造函数注入,因为该用法对调用端来说在实例化对象时就要一并传入所有依赖对象,所以调用端通过构造函数便可以知道对象依赖于哪些第三方组件。
2)属性注入
顾名思义,属性注入是通过对象的属性来注入依赖对象。属性注入的时机比构造函数注入来得晚,且外界不一定会设定属性。也就是说,如果采用属性注入,类型本身要能取得依赖对象的默认实现。这样一来,即使外界没有传入依赖对象,也能保证默认的对象可用,且能保持正常运行。我们来看下代码实现。public class TaskService
{
public ITaskRepository TaskRepository{ get; set;}
public int Done(int taskId)
{
var task = TaskRepository.Get(taskId);//根编号获取Task对象
task.Status=1;//状态设置为完成
return TaskRepository.Execute(task);//提交到数据库,并返回结果
}
}
3)方法注入
方法注入指的是客户端每次调用某对象的方法时都必须通过方法的自变量来传入依赖对象。使用此方式注入时需注意以下几点:
- 提供服务的类型不需要在整个类型中使用特定依赖对象,而只有在客户端调用某些方法时才需要传入那些对象。
- 客户端每次调用特定方法时可能 会传入不同的对象,而这些对象唯一相同之处是它们都实现了同一个接口(或继承自同一个抽象类型)。
我们来看个案例,比如说我们注册完用户后,要给当前用户发送一条注册成功的信息。我们来看下代码。public void Reg(User user,IMessageService service)
{
if(service==null)
throw new ArgumentException(“service”);
…
service.Send(…);
}
这里代码我就简写,相信大伙能看得懂。
方法注入我想大家多多少少有用过,举个栗子:记得在学ADO.NET查询数据用到的DbDataAdapter类的Fill方法,其原型如下:protected virtual int Fill(
DataTable dataTable,
IDbCommand command,
CommandBehavior behavior
)
这里只列其中一个,大家在项目中也可以慢慢体会。接下来我们谈谈控制反转。
控制反转
控制反转(或称为IoC)也是一种设计模式。在传统应用程序都是开发人员自己主动创建依赖对象,从而导致类与类之间的耦合过高,难于测试。有了控制反转,把创建和查找依赖对象的控制权交给了容器,由容器动态地将某种依赖关系注入到对象之中,通过这种方式来降低对象与对象之间的耦合性,这使得程序的整个体系架构变得灵活。所以 IoC 和 DI 是从不同的角度的描述的同一件事情,就是通过引入 IoC 容器,利用依赖注入的方式,实现对象之间的解耦。
IoC不仅在代码上带来了变化,在思想上从主动实例化变为被动,不需要再考虑如何实例化其他依赖的类。那么问题也就来了,怎么理解DI和IoC尼,它们之间是谁依赖谁,为什么需要依赖,谁注入谁,又注入了什么?
●谁依赖于谁:应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
好了,前面铺垫了一些基础知识点。接下我们来看下.NET Core中怎么实现?
注入自己的服务
先做一些基础工作,创建一个ITaskService接口如下:public interface ITaskService
{
void Done(int taskId);
}
接着,编写一个继承ITaskService接口类TaskServicepublic class TaskService : ITaskService
{
public void Done(int taskId)
{
Console.WriteLine(“任务编号:{0}已完成”,taskId)
}
}
ok,.NET Core在Startup类提供了ConfigureServices
的方法向IoC容器注册我们的应用程序服务。该ConfigureServices
方法包括IServiceCollection
用于注册应用服务的类型的参数。
让我们在ConfigureServices
方法中向IoC容器进行注册吧。如下所示:public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Add(new ServiceDescriptor(typeof(ITaskService), newTaskService()));
}
// 省略其它代码
}
正如上面看到,IServiceCollection实例的Add()方法用于注册IoC容器的服务。该ServiceDescriptor用于指定的服务类型和它的实例。我们已经指定ITaskService了服务类型TaskService及其实例。TaskService默认情况下,这会将服务注册为单例。现在,一个IoC容器将创建一个TaskService的单例对象,并将其注入到我们ITaskService在整个应用程序中作为构造函数或方法参数包括在内的类的构造函数中。
因此,我们可以在ASP.NET Core应用程序中向IoC容器注册自定义应用程序服务。还有其他扩展方法可用于快速方便地注册服务,我们稍后介绍。
服务生存期
在介绍其它扩展方法之前,我们先了解下内置的IoC容器管理已注册服务类型的生存期。它根据指定的生存期自动处理服务实例。
内置的IoC容器支持三种生存期:
- 单例:IoC容器将在应用程序的整个生存期内创建和共享服务的单个实例。
- 瞬态:每次您请求时,IoC容器都会创建一个指定服务类型的新实例。
- 范围:IoC容器将为每个请求创建一次指定服务类型的实例,并将在单个请求中共享。
下面的示例显示如何注册具有不同生存期的服务。public void ConfigureServices(IServiceCollection services)
{
services.Add(new ServiceDescriptor(typeof(ITaskService), newTaskService())); // singleton
services.Add(new ServiceDescriptor(typeof(ITaskService),typeof(TaskService), ServiceLifetime.Transient)); // Transient
services.Add(new ServiceDescriptor(typeof(ITaskService),typeof(TaskService), ServiceLifetime.Scoped)); // Scoped
}
注册扩展方法
ASP.NET Core框架包括每种生存期类型的扩展方法。AddSingleton(),AddTransient()以及AddScoped()分别用于单例生存期,瞬态生存期和作用域生存期的方法。
以下示例展示如何使用扩展方法注册类型(服务)。public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITaskService, TaskService>();
services.AddSingleton(typeof(ITaskService), typeof(TaskService));
services.AddTransient<ITaskService, TaskService>();
services.AddTransient(typeof(ITaskService), typeof(TaskService));
services.AddScoped<ITaskService, TaskService>();
services.AddScoped(typeof(ITaskService), typeof(TaskService));
}
构造函数注入
一旦我们注册了服务,如果服务类型作为参数包含在构造函数中,则IoC容器会自动执行构造函数注入。
例如,我们可以在任何MVC控制器中使用ITaskService
服务类型。如下示例。public class HomeController : Controller
{
ITaskService _taskService;
public HomeController(ITaskService taskService)
{
_taskService = taskService;
}
public IActionResult Done(int taskId)
{
_taskService.Done(taskId);
return View();
}
}
在上述示例中,IoC容器将自动将TaskService的实例传递给HomeController的构造函数。我们不需要做任何其他事情。
方法注入
有时我们可能只需要在单个操作方法中使用依赖项服务类型。为此,在请求方法中将[FromServices]属性与服务类型参数一起使用。using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
public HomeController()
{
}
public IActionResult Done([FromServices] ITaskService taskService,inttaskId)
{
taskService.Done(taskId);
return View();
}
}
属性注入
ASP.NET Core内置的IoC容器不支持属性注入。只能使用第三方IoC容器。
手动获取服务
public class HomeController : Controller
{
public HomeController()
{
}
public IActionResult Done(int taskId)
{
var services = this.HttpContext.RequestServices;
var taskService =(ITaskService)services.GetService(typeof(ITaskService));
taskService.Done(taskId);
return View();
}
}
以上就是ASP.NET Core的依赖注入和控制反转的使用。我们建议使用构造函数注入,最好不要使用RequestServices。
完毕,希望对大伙有帮助。
最后,祝大伙学习愉快!
声明:文中观点不代表本站立场。本文传送门:http://eyangzhen.com/218351.html