重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章将为大家详细讲解有关ASP.NET 5中怎么实现依赖注入,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
创新互联主营桦南网站建设的网络公司,主营网站建设方案,app软件开发,桦南h5微信小程序开发搭建,桦南网站营销推广欢迎桦南等地区企业咨询
类型注册与示例
依赖注入类型的注册一般是在程序启动的入口中,如Startup.cs中的ConfigureServices中,该类的主要目的就是注册依赖注入的类型。由于依赖注入的主要体现是接口编程,所以本例中,我以接口和实现类的方式来举例。
首先声明一个接口ITodoRepository和实现类TodoRepository1,代码如下:
public interface ITodoRepository { IEnumerableAllItems { get; } void Add(TodoItem item); TodoItem GetById(int id); bool TryDelete(int id); } public class TodoItem { public int Id { get; set; } public string Name { get; set; } } public class TodoRepository : ITodoRepository { readonly List_items = new List(); public IEnumerableAllItems { get { return _items; } } public TodoItem GetById(int id) { return _items.FirstOrDefault(x => x.Id == id); } public void Add(TodoItem item) { item.Id = 1 + _items.Max(x => (int?)x.Id) ?? 0; _items.Add(item); } public bool TryDelete(int id) { var item = GetById(id); if (item == null) { return false; } _items.Remove(item); return true; } }
为了演示不同的声明周期类型,建议多实现几个类,比如TodoRepository2、TodoRepository3、TodoRepository4等,以便进行演示。然后在ConfigureServices方法内注册接口ITodoRepository类型和对应的实现类,本例中根据不同的生命周期注册了不同的实现类,具体示例如下:
//注册单例模式,整个应用程序周期内ITodoRepository接口的示例都是TodoRepository1的一个单例实例 services.AddSingleton
依赖注入的在MVC中的使用方式目前有三种,分别是Controller的构造函数、属性以及View中的Inject形式。其中构造函数注入和之前的MVC中的是一样的,示例代码如下:
public class TodoController : Controller { private readonly ITodoRepository _repository; /// 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该构造函数 public TodoController(ITodoRepository repository) { _repository = repository; } [HttpGet] public IEnumerable GetAll() { return _repository.AllItems; //这里就可以使用该对象了 } }
属性注入,则是通过在属性上加一个[Activate]属性即可实现自动获取实例。
public class TodoController : Controller { // 依赖注入框架会自动找到ITodoRepository实现类的示例,赋值给该属性 [Activate] public ITodoRepository Repository { get; set; } [HttpGet] public IEnumerable GetAll() { return Repository.AllItems; } }
注意:这种方式,目前只适用于Controller以及子类,不适用于普通类
同时:通过这种方式,你可以获取到更多的系统实例对象,如ActionContext、HttpContext、HttpRequest、HttpResponse、 ViewDataDictionary、以及ActionBindingContext。在视图中,则可以通过@inject关键字来实现注入类型的实例提取,示例如下:
@using WebApplication1 @inject ITodoRepository repository @repository.AllItems.Count()
而最一般的使用方式,则是获取IServiceProvider的实例,获取该IServiceProvider实例的方式目前有如下几种(但范围不同):
var provider1 = this.Request.HttpContext.ApplicationServices; 当前应用程序里注册的Service var provider2 = Context.RequestServices; // Controller中,当前请求作用域内注册的Service var provider3 = Resolver; //Controller中
然后通过GetService和GetRequiredService方法来获取指定类型的实例,示例如下:
var _repository1 = provider1.GetService(typeof(ITodoRepository)); var _repository2 = provider1.GetService();//等价形式 //上述2个对象可能为空 var _repository3 = provider1.GetRequiredService(typeof(ITodoRepository)); var _repository4 = provider1.GetRequiredService();//等价形式 //上述2个对象肯定不为空,因为如果为空的话,会自动抛异常出来
普通类的依赖注入
在新版的ASP.NET5中,不仅支持上面我们所说的接口类的依赖注入,还支持普通的类型的依赖注入,比如我们生命一个普通类,示例如下:
public class AppSettings { public string SiteTitle { get; set; } }
上述普通类要保证有无参数构造函数,那么注册的用法,就应该像如下这样:
services.Configure(app => { app.SiteTitle = "111"; });
使用的时候,则需要获取IOptions类型的实例,然后其Options属性即是AppSettings的实例,代码如下:
var appSettings = app.ApplicationServices.GetRequiredService
当然,我们也可以在视图中,使用@inject语法来获取实例,示例代码如下:
@inject IOptionsAppSettings @AppSettings.Options.SiteTitle
基于Scope生命周期的依赖注入
普通的Scope依赖注入
基于Scope作用域的实例在创建的时候需要先创建作用域,然后在该作用域内再获取特定的实例,我们看看一个示例并对其进行验证。首先,注册依赖注入类型,代码如下:
services.AddScoped
然后创建作用域,并在该作用域内获取实例:
var serviceProvider = Resolver; var scopeFactory = serviceProvider.GetService(); //获取Scope工厂类 using (var scope = scopeFactory.CreateScope()) // 创建一个Scope作用域 { var containerScopedService = serviceProvider.GetService(); //获取普通的实例 var scopedService1 = scope.ServiceProvider.GetService(); //获取当前Scope的实例 Thread.Sleep(200); var scopedService2 = scope.ServiceProvider.GetService(); //获取当前Scope的实例 Console.WriteLine(containerScopedService == scopedService1); // 输出:False Console.WriteLine(scopedService1 == scopedService2); //输出:True }
另外,Scope也可以进行嵌套,嵌套的内外作用域所获取的实例也是不相同的,实例代码如下:
var serviceProvider = Resolver; var outerScopeFactory = serviceProvider.GetService(); using (var outerScope = outerScopeFactory.CreateScope()) //外部Scope作用域 { var innerScopeFactory = outerScope.ServiceProvider.GetService(); using (var innerScope = innerScopeFactory.CreateScope()) //内部Scope作用域 { var outerScopedService = outerScope.ServiceProvider.GetService(); var innerScopedService = innerScope.ServiceProvider.GetService(); Console.WriteLine(outerScopedService == innerScopedService); // 输出:False } }
基于HTTP请求的Scope依赖注入
在之前很多流行的DI容器中,针对每个请求,在该请求作用域内保留一个单实例对象是很流行的,也就是在每次请求期间一个类型的对象实例只会创建一次,这样可以大大提高性能。
在ASP.NET5中,基于HTTP请求的Scope依赖注入是通过一个ContainerMiddleware来实现的,调用该Middleware时,会创建一个限定作用域的DI容器,用于替换当前请求中已有的默认DI容器。在该管线中,所有后续的Middleware都会使用这个新的DI容器,在请求走完整个Pipeline管线以后,该ContainerMiddleware的作用就结束了,此时作用域会被销毁,并且在该作用域内创建的实例对象也都会销毁释放。
ContainerMiddleware的时序图如下所示:
具体的使用方式如下:
app.Use(new Func
普通类的依赖注入处理
目前普通类的依赖注入,只支持构造函数,比如我们定于一个TestService类,代码如下:
public class TestService { private ITodoRepository _repository; public TestService(ITodoRepository r) { _repository = r; } public void Show() { Console.WriteLine(_repository.AllItems); } }
通过在构造函数里传入ITodoRepository类的参数来使用该实例,使用的时候需要先将该类注册到DI容器中,代码如下:
services.AddScoped
然后调用如下语句即可使用:
var service = serviceProvider.GetRequiredService();
另外,需要注意,在目前的情况下,不能使用[Activate]来使用依赖注入功能,比如,如下代码在获取TestService2实例的过程中会出现错误:
public class TestService2 { [Activate] public ITodoRepository Repository { get; set; } public void Show() { Console.WriteLine(Repository.AllItems); } }
普通类中获取HttpContext实例
在MVC6中,我们没办法通过HttpContent.Current来获取上下文对象了,所以在普通类中使用的时候就会出问题,要想在普通类中使用该上下文对象,需要通过依赖注入来获取HttpContext实例,微软在ASP.NET5中,提供了IHttpContextAccessor接口用于获取该上下文对象。也就是说,我们可以将该类型的参数放在构造函数中,以获取上下文实例,代码如下:
public class TestService3 { private IHttpContextAccessor _httpContextAccessor; public TestService3(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void Show() { var httpContext = _httpContextAccessor.HttpContext;//获取上下文对象实例 Console.WriteLine(httpContext.Request.Host.Value); } }
而使用的时候,则直接通过如下语句就可以了,代码如下:
var service = serviceProvider.GetRequiredService(); service.Show();
提示:普通类的构造函数中,可以传入多个DI容器支持的数据类似作为参数。
使用第三方DI容器
目前,.NETCore不支持,只能在全功能版的.NET framework上才能使用,所以使用的时候需要注意一下。第三方DI容器的替换通常是在Startup.cs的Configure方法中进行的,在方法的开始处进行替换,以便后续的Middleware会使用相关的依赖注入功能。
首先要引入第三方的容器,以Autofac为例,引入Microsoft.Framework.DependencyInjection.Autofac,然后加入如下示例中的替换代码即可:
app.UseServices(services => { services.AddMvc();// AddMvc要在这里注册 var builder = new ContainerBuilder();// 构造容器构建类 builder.Populate(services);//将现有的Services路由到Autofac的管理集合中 IContainer container = builder.Build(); return container.Resolve();//返回AutoFac实现的IServiceProvider });
注意,使用上述方法的时候,要把Mvc的注册代码services.AddMvc();必须要从ConfigureServices中挪到该表达式内,否则会报异常,等待微软解决。另外,还有一个方式,微软目前的实例项目中还没有公开,通过分析一些代码,我们可以发现,在Microsoft.AspNet.Hosting程序中的StartupLoader.cs负责程序入口点的执行,在该文件中,我们知道首先是调用Startup.cs中的ConfigureServices方法,然后再调用Configure方法;我们可以看到示例中的ConfigureServices的返回值是void类型的,但在源码分析中发现,在根据约定解析ConfigureServices方法的时候,其首先判断有没有返回类型是IServiceProvider的,如果有则执行该方法,用使用该返回中返回的新IServiceProvider实例;没有的话,再继续查找void类型的ConfigureServices方法。所以,我们可以通过这种方式,来替换第三方的DI容器,实例代码如下:
// 需要先删除void类型的ConfigureServices方法 public IServiceProvider ConfigureServices(IServiceCollection services) { var builder = new ContainerBuilder(); // 构造容器构建类 builder.Populate(services); //将现有的Services路由到Autofac的管理集合中 IContainer container = builder.Build(); return container.Resolve(); //返回AutoFac实现的IServiceProvider }
这样,你就可以像以往一样,使用Autofac的方式进行依赖类型的管理了,示例如下:
public class AutofacModule : Module { protected override void Load(ContainerBuilder builder) { builder.Register(c => new Logger()) .As() .InstancePerLifetimeScope(); builder.Register(c => new ValuesService(c.Resolve())) .As() .InstancePerLifetimeScope(); } }
另外一个关于Autofac集成的案例:http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/
***实践
在使用依赖注入的的时候,我们应该遵守如下***实践。
做任何事情之前,务必在程序入口点提前注册所有的依赖类型。
避免直接使用IServiceProvider接口,相反,在构造函数里显式添加需要依赖的类型即可,让依赖注入引擎自己来解析实例,一旦依赖很难管理的话,就使用抽象工厂。
关于ASP.NET 5中怎么实现依赖注入就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。