控制反转(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: }

IoC在ASP.NET Web API中的应用的更多相关文章

  1. 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建

    目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建 通过上面的介绍我们知道利用HttpControllerSelector可以根据 ...

  2. ASP.NET Web API 中的返回数据格式以及依赖注入

    本篇涉及ASP.NET Web API中的返回数据合适和依赖注入. 获取数据 public IEnumerable<Food> Get() { var results = reop.Get ...

  3. ASP.NET Web API中的Controller

    虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...

  4. 在ASP.NET Web API中使用OData

    http://www.alixixi.com/program/a/2015063094986.shtml 一.什么是ODataOData是一个开放的数据协议(Open Data Protocol)在A ...

  5. ASP.NET Web API 中的异常处理(转载)

    转载地址:ASP.NET Web API 中的异常处理

  6. 【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化

    谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予“推荐”.但前几天有人询 ...

  7. Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET W ...

  8. ASP.NET WEB API 中的路由调试与执行过程跟踪

    路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...

  9. 能省则省:在ASP.NET Web API中通过HTTP Headers返回数据

    对于一些返回数据非常简单的 Web API,比如我们今天遇到的“返回指定用户的未读站内短消息数”,返回数据就是一个数字,如果通过 http response body 返回数据,显得有些奢侈.何不直接 ...

随机推荐

  1. Beginning Scala study note(5) Pattern Matching

    The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...

  2. Chrome 中的彩蛋,一款小游戏,你知道吗?

    今天看到一篇文章,介绍chrome中的彩蛋,带着好奇心进去看了一眼,没想到发现了一款小游戏,个人觉得还不错,偶尔可以玩一下,放松放松心情!^_^ 当 Chrome 无法连接到互联网时, 或者上着网突然 ...

  3. 《DSP using MATLAB》示例Example5.19

    代码: n = 0:9; x = n+1; h = [1,0,-1]; N = 6; y = ovrlpsav(x,h,N); nh = 0:1:length(h)-1; ny = 0:1:lengt ...

  4. 揭开Java IO流中的flush()的神秘面纱

    大家在使用Java IO流中OutputStream.PrintWriter --时,会经常用到它的flush()方法. 与在网络硬件中缓存一样,流还可以在软件中得到缓存,即直接在Java代码中缓存. ...

  5. 朴素贝叶斯算法下的情感分析——C#编程实现

    这篇文章做了什么 朴素贝叶斯算法是机器学习中非常重要的分类算法,用途十分广泛,如垃圾邮件处理等.而情感分析(Sentiment Analysis)是自然语言处理(Natural Language Pr ...

  6. Flask下如何处理Requests 上传中文文件名的问题

    一.问题的由来     最近有个项目,叫做文档服务资源中心,类似于七牛,为各个业务系统提供统一的文件资源服务,包括文件的存储.操作管理.下载.预览等.在做文件存储的时候,遇到了这个当指定上传的文件名为 ...

  7. php中echo(),print(),print_r(),var_dump()间的区别

    echo()函数:输出一个或多个字符串.实际上它并不是一个函数,所以不必对它使用括号,直接用echo就行.然而,如果您希望向echo()传递一个以上的参数,使用括号将会生成解析错误.echo()函数比 ...

  8. Linux 升级glibc-2.14 失败 我遇到的问题

    直接说步骤和流程: 1.到http://www.gnu.org/software/libc/下载最新版本,我这里下载了glibc-2.14.tar.gz 这个版本,解压到任意目录准备编译(/usr/l ...

  9. winfrom自定义滚动条

    panel或图片什么的跟着鼠标走,这里panel自己可以加背景图或直接搞个图就行了.为了演示清楚,有个滚动条控件做对比,与自定义的同步. using System; using System.Coll ...

  10. 了解vmware tools

    了解vmware tools vmware tools是虚拟机VMware Workstation自带的一款工具,它的作用就是使用户可以从物理主机直接往虚拟机里面拖文件.如果不安装它,我们是无法进行虚 ...