IoC在ASP.NET Web API中的应用

简介:

控制反转(Inversion of Control,IoC),简单地说,就是应用本身不负责依赖对象的创建和维护,而交给一个外部容器来负责。这样控制权就由应用转移到了外部IoC容器,控制权就实现了所谓的反转。比如在类型A中需要使用类型B的实例,而B实例的创建并不由A来负责,而是通过外部容器来创建。通过IoC的方式实现针对目标HttpController的激活具有重要的意义。[本文已经同步到《How ASP.NET Web API Works?》]

一、 基于IoC的HttpControllerActivator

将IoC应用于HttpController激活系统的目的在于让一个预定义的IoC容器来提供最终的HttpController对象。通过《ASP.NET Web API的Controller是如何被创建的?》的介绍我们知道HttpController的激活最终由HttpControllerActivator对象来完成,所以将IoC与ASP.NET Web API的HttpController激活系统进行集成最为直接的方式莫过于自定义一个HttpControllerActivator。

我们通过一个简单实例来演示如何通过自定义HttpControllerActivator的方式实现与IoC的集成,我们采用的IoC框架是Unity。我们在一个ASP.NET Web API应用中定义了这个UnityHttpControllerActivator类型。UnityHttpControllerActivator具有一个表示Unity容器的属性UnityContainer,该属性在构造函数中被初始化。在用于创建的HttpController的Create方法中,我们调用此UnityContainer对象的Resolve方法创建目标HttpController对象。

   1: public class UnityHttpControllerActivator : IHttpControllerActivator
   2: {
   3:     public IUnityContainer UnityContainer { get; private set; }
   4:  
   5:     public UnityHttpControllerActivator(IUnityContainer unityContainer)
   6:     {        
   7:         this.UnityContainer = unityContainer;
   8:     }
   9:  
  10:     public  IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
  11:     {
  12:         return (IHttpController)this.UnityContainer.Resolve(controllerType);
  13:     }
  14: }

接下来我们定义了如下一个继承自ApiController的ContactsController来管理联系人信息。简单起见,我们只定义了唯一的Action方法Get用于获取联系人信息。该方法具有一个可缺省的参数id表示希望获取的联系人的ID,如果没有提供此参数则返回所有联系人列表。

   1: public class ContactsController : ApiController
   2: {
   3:     public IContactRepository Repository { get; private set; }
   4:     public ContactsController(IContactRepository repository)
   5:     {
   6:         this.Repository = repository;
   7:     }
   8:     public IEnumerable<Contact> Get(string id = "")
   9:     {
  10:         return this.Repository.GetContacts(contact => 
  11:             string.IsNullOrEmpty(id) || id == contact.Id);
  12:     }
  13: }
  14:  
  15: public class Contact
  16: {
  17:     public string Id { get; set; }
  18:     public string Name { get; set; }
  19:     public string PhoneNo { get; set; }
  20:     public string EmailAddress { get; set; }
  21:     public string Address { get; set; }
  22: }

Action方法利用Repository属性返回的对象来实施联系人的查询工作,这个IContactRepository接口类型的属性在构造函数中初始化。我们利用IContactRepository接口来抽象对联系人数据的存储,如下面的代码片断所示,我们在此接口中仅定义了唯一的GetContacts方法根据指定的添加来筛选对应的联系人列表。

   1: public interface IContactRepository
   2: {
   3:     IEnumerable<Contact> GetContacts(Predicate<Contact> predicate);
   4: }

我们定义了如下一个DefaultContactRepository类型作为IContactRepository接口的默认实现者,简单起见,我们采用一个静态字典来保存联系人列表。

   1: public class DefaultContactRepository : IContactRepository
   2: {
   3:     private static List<Contact> contacts = new List<Contact>
   4:     {
   5:         new Contact{ Id="001", Name = "张三",  PhoneNo="123", EmailAddress = "zhangsan@gmail.com"},
   6:         new Contact{ Id="002", Name = "李四",  PhoneNo="456",EmailAddress = "lisi@gmail.com"}
   7:     };
   8:  
   9:     public IEnumerable<Contact> GetContacts(Predicate<Contact> predicate)
  10:     {
  11:         return contacts.Where(contact=>predicate(contact));
  12:     }
  13: }

我们在Global.asax中对自定义的UnityHttpControllerActivator进行了注册。如下面的代码片断所示,我们在Application_Start方法中创建了一个UnityContainer对象,并通过调用泛型方法RegisterType<TFrom,TTo>注册了IContactRepository接口和DefaultContactRepository类型之间的匹配关系。我们最后根据这个UnityContainer创建一个UnityHttpControllerActivator对象,并将其注册到当前ServicesContainer上。

   1: public class WebApiApplication: System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         IUnityContainer unityContainer = new UnityContainer();
   7:         unityContainer.RegisterType<IContactRepository,   DefaultContactRepository>(); 
   8:         GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new UnityHttpControllerActivator(unityContainer));
   9:     }
  10: }

当此ASP.NET Web API应用运行之后,我们可以直接在浏览器中输入相应的地址获取所有联系人列表(“/api/contacts”)和针对某个ID为“001”(“/api/contacts/001”)的联系人信息,相应的联系人信息会以如下图所示的形式出现在浏览器上。

二、基于IoC的DependencyResolver

由于默认的DefaultHttpControllerActivator会先利用当前注册的DependencyResolver对象去激活目标HttpController,所以除了利用自定义的HttpControllerActivator将IoC引入HttpController激活系统之外,另一个有效的方案就是注册自定义的DependencyResolver。

接下来将要自定义的DependencyResolver基于另一个叫作“Ninject”的IoC框架。较之Unity,Ninject是一个更加轻量级的IoC框架。篇幅所限,我们不便对这个IoC框架作过多的介绍,有兴趣的读者可以访问其官网(“http://www.ninject.org/”)了解Ninject。

   1: public class NinjectDependencyResolver : IDependencyResolver
   2: {
   3:     private List<IDisposable> disposableServices = new List<IDisposable>();
   4:     public IKernel Kernel { get; private set; }
   5:  
   6:     public NinjectDependencyResolver(NinjectDependencyResolver parent)
   7:     {
   8:         this.Kernel = parent.Kernel;
   9: }
  10:  
  11:     public NinjectDependencyResolver()
  12:     {
  13:         this.Kernel = new StandardKernel();
  14:     }
  15:  
  16:     public void Register<TFrom, TTo>() where TTo : TFrom
  17:     {
  18:         this.Kernel.Bind<TFrom>().To<TTo>();
  19: }
  20:  
  21:     public IDependencyScope BeginScope()
  22:     {
  23:         return new NinjectDependencyResolver(this);
  24:     }
  25:  
  26:     public object GetService(Type serviceType)
  27:     {
  28:         return this.Kernel.TryGet(serviceType);
  29:     }
  30:  
  31:     public IEnumerable<object> GetServices(Type serviceType)
  32:     {
  33:         foreach (var service in this.Kernel.GetAll(serviceType))
  34:         {
  35:             this.AddDisposableService(service);
  36:             yield return service;
  37:         }
  38: }    
  39:  
  40:     public void Dispose()
  41:     {
  42:         foreach (IDisposable disposable in disposableServices)
  43:         {
  44:             disposable.Dispose();
  45:         }
  46: }
  47:  
  48:     private void AddDisposableService(object servie)
  49:     {
  50:         IDisposable disposable = servie as IDisposable;
  51:         if (null != disposable && !disposableServices.Contains(disposable))
  52:         {
  53:             disposableServices.Add(disposable);
  54:         }
  55:     }
  56: }

我们创建了如上一个类型为NinjectDependencyResolver的自定义DependencyResolver。NinjectDependencyResolver的核心是类型为IKernel的只读属性Kernel,用于获取服务实例的GetService和GetServices方法分别通过调用此Kernel属性的TryGet和GetAll方法来实现。BeginScope方法返回一个新的NinjectDependencyResolver对象,它与自身拥有同一个Kernel对象。我们定义了额外的方法Register<TFrom,TTo>来注册接口与实现类型之间的映射关系。为了确保获取的服务实例能够被正常地释放,我们定义了一个元素类型为IDisposable的列表。如果获取的对象实现了IDisposable接口,它会被放入这个列表中,我们在实现的Dispose方法中释放该列表中的所有对象。

现在我们将这个自定义的NinjectDependencyResolver应用到上一个演示实例中。我们只需要将Global.asax中针对自定义HttpControllerActivator的注册替换成针对NinjectDependencyResolver的注册即可。运行此ASP.NET Web API应用后通过浏览器试图获取联系人信息,我们依然会得到如上图所示的结果。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         NinjectDependencyResolver dependencyResolver = new NinjectDependencyResolver();
   7:         dependencyResolver.Register<IContactRepository, DefaultContactRepository>();
   8:         GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver; 
   9:     }
  10: }

作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
25天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
38 4
|
27天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
86 3
|
4天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
16 1
|
11天前
|
JSON API 数据格式
如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架
本文介绍了如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架,适合小型项目和微服务。文章从环境准备、创建基本Flask应用、定义资源和路由、请求和响应处理、错误处理等方面进行了详细说明,并提供了示例代码。通过这些步骤,读者可以快速上手构建自己的RESTful API。
23 2
|
21天前
|
监控 负载均衡 API
Web、RESTful API 在微服务中有哪些作用?
在微服务架构中,Web 和 RESTful API 扮演着至关重要的角色。它们帮助实现服务之间的通信、数据交换和系统的可扩展性。
43 2
|
1月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
85 9
|
1月前
|
存储 开发框架 .NET
.NET 8 实现无实体库表 API 部署服务
【10月更文挑战第12天】在.NET 8中,可通过以下步骤实现无实体库表的API部署:首先安装.NET 8 SDK及开发工具,并选用轻量级Web API框架如ASP.NET Core;接着创建新项目并设计API,利用内存数据结构模拟数据存储;最后配置项目设置并进行测试与部署。此方法适用于小型项目或临时解决方案,但对于大规模应用仍需考虑持久化存储以确保数据可靠性与可扩展性。
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
48 2
|
24天前
|
移动开发 前端开发 JavaScript
前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
117 0
|
1月前
|
监控 安全 API
Docker + .NET API:简化部署和扩展
Docker + .NET API:简化部署和扩展
35 0
下一篇
无影云桌面