• 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: }
  • 相关阅读:
    C#文件读写常用类介绍
    C#实现注销、重启和关机代码
    Mybatis学习---基础知识考核
    Linux操作系统各版本ISO镜像下载
    Java学习---JDK的安装和配置
    Java学习---基础知识学习
    Java学习---常见的模式
    Java实例---黑白五子棋[单机版]
    Java实例---简单的超市管理系统
    Java实例---简单的个人管理系统
  • 原文地址:https://www.cnblogs.com/artech/p/ioc-4-asp-net-web-api.html
Copyright © 2020-2023  润新知