zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    用 ArcMap 发布 ArcGIS Server FeatureServer Feature Access 服务 PostgreSQL 版本
    ArcMap 发布 ArcGIS Server OGC(WMSServer,MapServer)服务
    ArcScene 创建三维模型数据
    ArcMap 导入自定义样式Symbols
    ArcMap 导入 CGCS2000 线段数据
    ArcMap 导入 CGCS2000 点坐标数据
    ArcGis Server manager 忘记用户名和密码
    The view or its master was not found or no view engine supports the searched locations
    python小记(3)操作文件
    pytest(2) pytest与unittest的区别
  • 原文地址:https://www.cnblogs.com/dwuge/p/5331709.html
Copyright © 2011-2022 走看看