首页 > 代码库 > ASP.NET 5 牛刀小試(二):加入第三方 DI 容器
ASP.NET 5 牛刀小試(二):加入第三方 DI 容器
上回介绍了 ASP.NET vNext 自带容器的基本用法,这次要试试把 ASP.NET vNext 的自带容器换成 Autofac。
这一次,在编写范例程序的过程中,光是解决 KRE 与相关套件的版本不合的问题,就花了我个把小时。所以还是得先提醒一下,目前 ASP.NET vNext 还不是正式版,所以本文的操作步骤与代码不一定符合你的开发环境。
如果你有兴趣了解我最后一个碰到的问题,以及最后是怎么解决的。可以看这帖:https://github.com/aspnet/Home/issues/218(至于更早之前碰到的问题就略过不提了。不重要,因为是发生在比较早的 beta-1 版本。
本文范例所使用的开发环境:
- Windows Server 2012 R2
- Visual Studio 2015 Preview
- KRE-CLR-x86.1.0.0-rc1-10798
小引
上回提到,ASP.NET vNext 的自带容器支持四种生命周期模式:Instance、Singleton、Transient、Scoped。而且,上一次的范例程序也演示了自带容器的基本用法。这一次要试试把 ASP.NET vNext 的自带容器换成 Autofac。
在此之前,先补充一点基本观念。
在 ASP.NET vNext 之前,.NET Framework 对 DI(dependency injection)的支持并不那么彻底,比较像是附加功能。到了 ASP.NET vNext ,DI(dependency injection)摇身一变,已成为一级公民。明确地说,现在不仅自带了一个小巧的 DI 容器,同时也提供了适度的弹性,能够与其他第三方容器衔接。不过,自带的 DI 容器比较阳春,无法满足某些需求,例如欲解析之类型有多个构造函数时,自带的 DI 容器无法让我们指定使用特定构造函数。像这类更细致的控制,还是得靠其他第三方 DI 框架才行。
ASP.NET vNext 的自带 DI 容器
ASP.NET vNext 之所以能够搭配其他 DI 框架一起使用,是因为它在 DI 容器实现之上提供了一个抽象层。具体来说,这个抽象层就是 System.IServiceProvider 接口,它定义了自带 DI 容器应该具备哪些功能。.NET Framework 的其他组件(如 MVC、路由、SignalR、Entity Framework 等)都支持这个接口,而且也只会使用这个接口所定义的 DI 相关功能。所以基本上,ASP.NET vNext 的自带 DI 容器就等于是个全局的 Service Locator。
不过,这并不代表你的应用程序也受限于此接口——你可以写一个自定义的类型来封装你惯用的 DI 容器,然后把自带的 IServiceProvider 组件换掉。如此一来,所有服务解析的工作就会交给你指定的自定义容器来处理。此外,你也可以让自定义容器只解析特定类型的服务,而把其他不需要特别处理的服务类型丢回(fallback)给自带的容器来解析。
在 ASP.NET vNext 中,由于所有的内部框架/组件都是通过同一个容器来注册服务,相依对象便更容易跨越框架边界(四处流窜?),注入至以往不容易到达的地方。
自带的 DI 容器支援下列几种生命周期:
- Instance:解析特定服务类型时,总是返回由你自行建立的特定对象。
- Transient:每次解析时都建立新的对象。
- Singleton:每次解析时都返回先前已建立的同一个对象。该对象等于是全局(对整个容器而言)共享的单一对象。
- Scoped:针对特定范围共享同一个对象。作用等同于特定范围内的 Singleton。
接着要来试试把 Autofac 加入至 ASP.NET vNext 的 DI 框架。
BYOC to ASP.NET vNext
前面提过,ASP.NET vNext 允许你使用自定义容器来取代自带的 DI 容器,但不必是完全取代,而是可以让 ASP.NET 优先用你提供的容器,而让自带的容器退居二线,担任「备援」的角色。正因为如此,我们通常不说「抽换自带容器」,而说「把你的容器加入 ASP.NET vNext」,亦即 BYOC (Bring Your Own Container) to ASP.NET vNext。
延续上回的范例程序,原本的 Startup 类型是这么写:
public class Startup{ public void Configure(IApplicationBuilder app) { app.UseMvc(); } public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddScoped<ITimeService, TimeService>(); }}
其中 ConfigureServices 方法使用了自带容器来注册 TimeService 服务。这个部分要改用 Autofac 来做。
注:ASP.NET vNext 框架会先调用 Startup 类型的 ConfigureServices 方法,然后再调用 Configure 方法。
第一步,为项目加入必要的组件引用。这个部分可直接修改 project.json,如下所示:
{ "webroot": "wwwroot", "version": "1.0.0-*", "exclude": [ "wwwroot" ], "packExclude": [ "**.kproj", "**.user", "**.vspscc" ], "dependencies": { "Microsoft.AspNet.Server.IIS": "1.0.0.0-rc1-10790", "Microsoft.AspNet.Mvc": "6.0.0.0-rc1-12170", "Microsoft.Framework.DependencyInjection": "1.0.0.0-rc1-10655", "Microsoft.Framework.DependencyInjection.Autofac": "1.0.0.0-rc1-10655" }, "frameworks": { "aspnet50": { "dependencies": {} } }}
提醒:dependencies 区块中的各组件的版本号码可能会决定这个简单的范例程序要花你三分钟还是三小时才能完成。(等到 ASP.NET vNext 发布正式版本之后应该就不会有这些状况了)
接着修改 Startup 类型的 ConfigureServices 方法,改成这样:
public IServiceProvider ConfigureServices(IServiceCollection services){ services.AddMvc(); var builder = new ContainerBuilder(); builder.Populate(services); builder.RegisterType<TimeService>().As<ITimeService>(); IContainer container = builder.Build(); return container.Resolve<IServiceProvider>();}
上面这段代码有几个地方值得注意:
- Populate 是个扩展方法,由 Microsoft.Framework.DependencyInjection.Autofac 组件提供。此扩展方法会把传入的服务描述清单中的服务全部注册至 Autofac 容器。
- 注意 ConfigureServices 方法的返回类型从原先的 void 改成了 IServiceProvider。这里返回的对象实际类型会是 AutofacServiceProvider。如此一来,ASP.NET vNext 框架接收到你返回的 AutofacServiceProvider 对象之后,就会把它设定成默认的 DI 容器。
试执行应用程序,结果应该和前一个版本相同。
参考資料
- Dependency Injection in ASP.NET vNext by Robin Sedlaczek
- System.Exception: TODO: No service for type ‘Microsoft.Framework.DependencyInjection.ServiceLookup.IServiceManifest‘ has been registered.
ASP.NET 5 牛刀小試(二):加入第三方 DI 容器