Asp.Net Web API 2第十一課——在Web API中使用Dependency Resolver


前言

閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html

本文主要來介紹在Asp.Net Web API使用Web API的Decpendency Resolver在控制器中如何注入依賴。

本文使用VS2013。本文的示例代碼下載鏈接為http://pan.baidu.com/s/1BvFTs

為什么要使用Dependency Resolver

一個dependency 其實就是一個對象或者另外一個對象需要的一個接口。例如,在Asp.Net Web API 2第二課——CRUD操作 http://www.cnblogs.com/aehyok/p/3434578.html中,我們定義了一個ProductsController的類,這個類需要一個IProductRepository 的實例,這個實現看起來像這樣:

public class ProductsController : ApiController
{
    private static IProductRepository repository = new ProductRepository();

    // Controller methods not shown.
} 

這不是最好的設計,因為對於調用創建的ProductRepository 是通過在控制器中硬編碼的方式實現的。如果要使用IProductRepository的不同實例,我們將需要在ProductRepository改變代碼。如果ProductsController不依賴於任何具體實例的IProductRepository那會是比較好的。

Dependency injection解決了這個問題。在Dependency injection中,對象是不會負責創建自己的依賴項的。相反,當你創建一個對象,注入這個依賴的時候是通過構造函數參數或者setter方法。

這里是ProductsController中修改后的實現代碼:

public class ProductsController : ApiController
{
    private readonly IProductRepository repository;

    public ProductsController(IProductRepository repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }
        this.repository = repository;
    }

這樣是比較好的。現在可以切換到另外一個IProductRepository 的實例,而不用觸及到ProductsController的實現。

但是,在Asp.Net Web API中,你不能直接的創建一個控制器。相反,這個框架給你創建一個控制器,而且它並不知道IProductRepository 的相關信息。這個框架也只能通過調用無參數的構造函數來創建你的控制器。

就在這個時候dependency resolver來了。dependency resolver的工作就是創建這個框架所需要的對象,包含congtrollers對象。通過提供一個自定義的dependency resolver,你可以代表框架來創建控制器實例。

一個簡單的dependency resolver

 下面的代碼展示了一個簡單的dependency resolver。這個代碼主要只是展示了在Web API中依賴注入如何工作的。之后,我們將看到怎樣來合並一個Ioc的容器。

class SimpleContainer : IDependencyResolver
{
    static readonly IProductRepository respository = new ProductRepository();

    public IDependencyScope BeginScope()
    {
        // This example does not support child scopes, so we simply return 'this'.
        return this; 
    }

    public object GetService(Type serviceType)
    {
        if (serviceType == typeof(ProductsController))
        {
            return new ProductsController(respository);
        }
        else
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return new List<object>();
    }

    public void Dispose()
    {
        // When BeginScope returns 'this', the Dispose method must be a no-op.
    }
}

一個 dependency resolver實現了這個IDependencyResolver 接口。這個IDependencyResolver  接口繼承了另外的兩個接口IDependencyScope 、IDisposable。

namespace System.Web.Http.Dependencies
{
    public interface IDependencyResolver : IDependencyScope, IDisposable
    {
        IDependencyScope BeginScope();
    }

    public interface IDependencyScope : IDisposable
    {
        object GetService(Type serviceType);
        IEnumerable<object> GetServices(Type serviceType);
    }
}

IDependencyScope 接口定義了兩個方法:

  • GetService: 創建一個指定類型的實例
  • GetServices: 創建一個指定類型的集合對象

對於控制器,這個框架調用 GetService來獲得控制器的單個實例。這就是我們簡單的容器創建控制器和注入repository

對於你的dependency resolver不處理的任何類型,GetService 會返回null,GetServices 也會返回一個空的集合對象,尤其是,別拋出一個未知類型的異常。

這個IDependencyResolver 接口繼承了IDependencyScope ,添加了一個方法:

  • BeginScope: 創建一個嵌套的范圍

之后,我們將來討論嵌套的范圍內如何來管理我們對象的生命周期。現在,BeginScope 方法的實現我們簡單的返回一個this。

Setting the Dependency Resolver

現在在Web API全局配置對象中來設置Dependency Resolver。

主要是在Global.asax這個文件當中。然后在Application_Start 方法中,將GlobalConfiguration.Configuration.DependencyResolver設置為你的Dependency Reslover。

public class WebApiApplication : System.Web.HttpApplication
{
    void ConfigureApi(HttpConfiguration config)
    {
        config.DependencyResolver = new SimpleContainer();
    }

    protected void Application_Start()
    {
        ConfigureApi(GlobalConfiguration.Configuration);

        // ...
    }
}

 那么現在你可以正常運行程序了。

范圍和對象聲明周期

控制器被創建的每個請求。為了幫助管理對象的聲明周期,IDependencyResolver 使用了IDisposable接口。被添加到HttpConfiguration 上的dependency resolver對象擁有全局的范圍。當框架創建一個新的控制器實例的時候,它調用IDependencyResolver.BeginScope。這個方法返回一個IDependencyScope 。這個框架在IDependencyScope 上調用GetService 去獲得這個控制器。當框架處理完這個請求的時候,它在子范圍中調用Dispose 。你能通過Dispose 方法來釋放控制器的依賴。

Dependency Injection with IoC Containers

一個Ioc容器就是一個軟件組件,它負責創建依賴。Ioc容器為依賴注入提供公共的框架。如果你使用一個Ioc容器,你不需要在代碼中直接連同對象,幾個開源的.Net Ioc容器是可以利用的,例如Autofac, Castle Windsor, Ninject, Spring.NET, StructureMap 等等。

下面的例子我們來使用Unity,這個Ioc容器是由Microsoft patterns & practices開發的。

namespace ProductStore
{
    using System;
    using System.Collections.Generic;
    using System.Web.Http;
    using System.Web.Http.Dependencies;
    using Microsoft.Practices.Unity;

    class ScopeContainer : IDependencyScope
    {
        protected IUnityContainer container;

        public ScopeContainer(IUnityContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            this.container = container;
        }

        public object GetService(Type serviceType)
        {
            if (container.IsRegistered(serviceType))
            {
                return container.Resolve(serviceType);
            }
            else
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (container.IsRegistered(serviceType))
            {
                return container.ResolveAll(serviceType);
            }
            else
            {
                return new List<object>();
            }
        }

        public void Dispose()
        {
            container.Dispose();
        }
    }

    class IoCContainer : ScopeContainer, IDependencyResolver
    {
        public IoCContainer(IUnityContainer container)
            : base(container)
        {
        }

        public IDependencyScope BeginScope()
        {
            var child = container.CreateChildContainer();
            return new ScopeContainer(child);
        }
    }
}

這個ScopeContainer 類實現了IDependencyScope 代表了一個子范圍。這個IoCContainer 類實現了全局范圍內的依賴解析。並在BeginScope 方法中創建一個新的ScopeContainer對象。這個Unity 容器也有一個子容器的概念。因為我們可以用Unity 的子容器來初始化ScopeContainer 。這個ScopeContainer.Dispose方法釋放了Unity的子容器。

下面的代碼用Unity注冊了controller和repository,然后設置Dependency resolver.

void ConfigureApi(HttpConfiguration config)
{
    var unity = new UnityContainer();
    unity.RegisterType<ProductsController>();
    unity.RegisterType<IProductRepository, ProductRepository>(
        new HierarchicalLifetimeManager());
    config.DependencyResolver = new IoCContainer(unity);
}

每次HTTP請求的時候Web API 控制器被創建,然后請求被處理之后控制器被釋放。

現在同樣可以運行了。

總結

 對依賴注入的研究,還沒有那么深入,只知道簡單的怎么用。

對於文中

    public IDependencyScope BeginScope()
    {
        // This example does not support child scopes, so we simply return 'this'.
        return this; 
    }

如果不適用this,那么其他還可以使用什么,還有待進一步的深入。之后自己還要對依賴Unity的依賴注入進行研究。不過感覺好像沒MEF那么好用。

本文的參考鏈接為http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

本文以同步到Web API系列導航中 http://www.cnblogs.com/aehyok/p/3446289.html

本文的示例代碼下載鏈接為http://pan.baidu.com/s/1BvFTs


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM