ASP.NET Web API中的依赖注入什么是依赖注入ASP.NET Web API依赖解析器使用Unity解析依赖配置依赖解析

什么是依赖注入

    依赖,就是一个对象需要的另一个对象,比如说,这是我们通常定义的一个用来处理数据访问的存储,让我们用一个例子来解释,首先,定义一个领域模型如下:

namespace Pattern.DI.MVC.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

然后是一个用于实例的简单存储类:

namespace Pattern.DI.MVC.Models
{
    public class ProductContext
    {
        public List<Product> Products { get; internal set; }


        public ProductContext()
        {
            Products.Add(new Product() {Id = 1, Name = "苍阿姨", Price = 100});
            Products.Add(new Product() {Id = 2, Name = "武藤奶奶", Price = 200});
            Products.Add(new Product() {Id = 3, Name = "小泽姐姐", Price = 300});
        }
    }



    public class ProductRepository
    {
        private ProductContext context=new ProductContext();


        public IEnumerable<Product> GetAll()
        {
            return context.Products;
        }

        public Product GetById(int id)
        {
            return context.Products.FirstOrDefault(p => p.Id == id);
        }
    }
}

现在,我们定义一个ASP.NET Web API控制器来支持对Product实体集的GET请求:

namespace Pattern.DI.MVC.Controllers
{
    public class ProductController : ApiController
    {
        private readonly ProductRepository productRepository=new ProductRepository();

        public IEnumerable<Product> Get()
        {
            return productRepository.GetAll();
        }

        public Product Get(int id)
        {
            return productRepository.GetById(id);
        }
    }
}

现在注意到,这个控制器依赖了“ProductRepository”这个类,我们在类中实例化了ProductRepository,这就是设计的“坏味道”了,因为如下几个原因:

  • 假如你想要使用另外一个实现替换ProductRepository,你还要去修改ProductController类;
  • 假如ProductRepository存在依赖,你必须在ProductController中配置他们,对于一个拥有很多控制器的大项目来说,你就配置工作将深入到任何可能的地方;
  • 这是很难去做单元测试的因为控制器中硬编码了对数据库的查询,对于一个单元测试,你可以在没有确切设计之前,使用一个仿制的桩存储体。

我们可以使用注入一个ProductRepsoitory来解决这个问题,首先重构ProductRepository的方法到一个接口中:

namespace Pattern.DI.MVC.Models
{

    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product GetById(int id);
    }


    public class ProductRepository:IProductRepository
    {
        private ProductContext context = new ProductContext();


        public IEnumerable<Product> GetAll()
        {
            return context.Products;
        }

        public Product GetById(int id)
        {
            return context.Products.FirstOrDefault(p => p.Id == id);
        }
    }
}

然后在ProductC0ntroller中使用参数传入IProductRepository:

namespace Pattern.DI.MVC.Controllers
{
    public class ProductController : ApiController
    {
        private readonly IProductRepository productRepository;


        public ProductController(IProductRepository productRepository)
        {
            this.productRepository = productRepository;
        }

        public IEnumerable<Product> Get()
        {
            return productRepository.GetAll();
        }

        public Product Get(int id)
        {
            return productRepository.GetById(id);
        }
    }
}

这个示例使用了构造器注入,你同样可以使用设置器注入的方式,ASP.NET Web API在为请求映射了路由之后创建控制器,而且现在他不知道任何关于IProductRepository的细节,这是通过API依赖器解析到的。

ASP.NET Web API依赖解析器

ASP.NET Web API定义了一个IDependencyResolever用来解析依赖项目,以下是这个接口的定义:

public interface IDependencyResolver : IDependencyScope, IDisposable
{
    IDependencyScope BeginScope();
}

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

这个接口有两个方法

  • GetService为一个类型创建一个实例;
  • GetServices为一个特定的类型创建一个实例集合

这个接口继承自IDependencyScope并且添加了BeginScope方法,在这篇文章接下来将讨论这个方法。

当ASP.NET Web API创建一个controller实例的时候,它首先调用IDependencyResolver的GetService方法,传回一个Controller实例,你可以使用一个扩展的钩子去创建控制器并且解析依赖。假如GetService方法返回NULL,ASP.NET Web API将查找一个无参的构造函数。

使用Unity解析依赖

虽然你可以重头开始写一个IDenpendencyResolver的实现,但是这个接口已经设计了可以作为ASP.NET Web API和IoC工具的桥梁。

IoC容器是一个用来管理依赖项目的组建,你可以在其中注册类型,在使用的时候创建对象,IoC容易自动解析出依赖的关系,许多IoC容器允许你在对象的生命周期中进行控制。

首先在项目中使用NuGet Package Manage Console安装Unity,关于Unity的介绍可以点击这里查看详细。

Install-Package Unity

以下是一个使用Unity容器对IDependencyResolver的实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using System.Web.Http.Dependencies;

namespace Pattern.DI.MVC.Models
{
    public class UnityResolver : IDependencyResolver
    {
        protected IUnityContainer container;

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

        public object GetService(Type serviceType)
        {
            try
            {
                return container.Resolve(serviceType);
            }
            catch (ResolutionFailedException)
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                return container.ResolveAll(serviceType);
            }
            catch (ResolutionFailedException)
            {
                return new List<object>();
            }
        }

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

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

配置依赖解析

在全局的HttpConfiguration对象中DependencyResolver属性上设置依赖解析器,以下的代码使用Unity注册IProductRepository接口并且创建一个UnityResolver,修改App_Start/WebApiConfig.cs中的Register方法

namespace Pattern.DI.MVC
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var container = new UnityContainer();
            container.RegisterType<IProductRepository, ProductRepository>();
            config.DependencyResolver = new UnityResolver(container);

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

至此完工,测试Api返回数据

原文地址:http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

ExtJs学习笔记(23)-ScriptTagProxy+XTemplate+WCF跨域取数据

ajax应用中跨域一直是一个非常麻烦的问题,目前也有一些解决办法,但要么比较麻烦,要么就不具备通用性,幸好ExtJs里的ScriptTagProxy提供了跨域读...

2338
来自专栏大内老A

ASP.NET Core的配置(1):读取配置信息

提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我...

2118
来自专栏张高兴的博客

张高兴的 Windows 10 IoT 开发笔记:无线收发芯片 nRF24L01

2898
来自专栏大内老A

事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]

最近这两天一直在忙着为一个项目检查内存泄漏(Memory Leak)的问题,对相关的知识进行了一下简单的学习和探索,其间也有了一些粗浅的经验积累,今天特意写一篇...

2366
来自专栏NetCore

ADO.NET 2.0 中的新增 DataSet 功能

ADO.NET 2.0 中的新增 DataSet 功能 发布日期: 1/13/2005 | 更新日期: 1/13/2005 Jackie Goldstein ...

18210
来自专栏跟着阿笨一起玩NET

VS2010在C#头文件中添加文件注释的方法

1.VS2010 中找到安装盘符(本人安装目录在D盘,所以以D盘为例)D:\Program Files (x86)\Microsoft Visual Studi...

801
来自专栏Porschev[钟慰]的专栏

asp.net生成静态页

做个生成静态页示例: 采用替换模版页的形式生成静态页 第一步:新建项目,创建一个简单模版页:TemplatePage.htm <!DOCTYPE html PU...

1966
来自专栏柠檬先生

Angularjs基础(四)

AngularJS过滤器     过滤器可以使用一个管道符(|)添加到表达式和指令中。       AngularJS过滤器可用于转换数据:    ...

1979
来自专栏程序员的SOD蜜

常见.NET功能代码汇总 (2)

常见.NET功能代码汇总 23,获取和设置分级缓存 获取缓存:首先从本地缓存获取,如果没有,再去读取分布式缓存 写缓存:同时写本地缓存和分布式缓存 priv...

2446
来自专栏张善友的专栏

自定义Unity对象生命周期管理集成ADO.NET Entity Framework

在Unity中,从Unity 取得的实例为 Transient。如果你希望使用多线程方式,就需要在组成时使用lifecycle参数,这时候取出的组件就不再是同一...

3158

扫码关注云+社区