摘要

提供者是特殊的工厂类,Ninject使用它来实例化解析类型。任何时候我们绑定一个服务类型到一个组件,我们都隐式地关联那个服务类型到一个可以实例化那个组件的提供者。这个隐藏的提供者被称为StandardProvider,是一个通用的工厂,他可以创建每一个给定类型的实例。尽管我们可以经常依赖StandardProvider而不用对他在背后做了什么费心,Ninject也允许我们创建和注册我们自己自定义的提供者,只要我们需要自定义这个下面的激活过程:

 Bind<IService>().ToProvider<MyService>();
public class MyServiceProvider : Provider<MyService>
{
protected override MyService CreateInstance(IContext context)
{
return new MyService();
}
}

尽管继承这个Provider<T>类是推荐的方法来创建一个自定义提供者。作为一个提供者,为一个类继承这个IProvider接口在Ninject就足够了:

 public interface IProvider
{
Type Type { get; }
object Create(IContext context);
}

回顾上一篇文章的ShippersSqlRepository类和ShippersXmlRepository类,他们的的构造函数都有一个字符串类型的参数。

ShippersSqlRepository构造函数参数northwindConnectionString提供连接字符串:

 public class ShippersSqlRepository : IShippersRepository
{
private readonly NorthwindContext objectContext;
public ShippersSqlRepository(string northwindConnectionString)
{
objectContext = new NorthwindContext(northwindConnectionString);
}
public IEnumerable<Business.Model.Shipper> GetShippers()
{ ... }
public void AddShipper(Business.Model.Shipper shipper)
{ ... }
}

ShippersXmlRepository构造函数参数xmlRepositoryPath提供xml文件路径:

 public class ShippersXmlRepository : IShippersRepository
{
private readonly string documentPath;
public ShippersXmlRepository(string xmlRepositoryPath)
{
this.documentPath = xmlRepositoryPath;
}
public IEnumerable<Shipper> GetShippers()
{ ... }
public void AddShipper(Shipper shipper)
{ ... }
}

在这个情况下,这些参数阻止了Ninject实例化我们的repository,因为这个kernel对怎样解析字符串参数没有任何主意。因此,下面几行对于注册我们的repository还不够:

   Bind<IShippersRepository>().To<ShippersSqlRepository>()
  .When(r => r.Target.Name.StartsWith("source"));
  Bind<IShippersRepository>().To<ShippersXmlRepository>()
  .When(r => r.Target.Name.StartsWith("target"));

一个提供需要的参数的方法是使用这个WithConstructorArgument方法:

     connection = ConfigurationManager.AppSettings["northwindConnectionString"];
Bind<IShippersRepository>()
.To<ShippersSqlRepository>()
.When(r => r.Target.Name.StartsWith("source"))
.WithConstructorArgument("NorthwindConnectionString", connection);
path = ConfigurationManager.ConnectionStrings["xmlRepositoryPath"];
Bind<IShippersRepository>()
.To<ShippersXmlRepository>()
9 .When(r => r.Target.Name.StartsWith("target"))
.WithConstructorArgument("XmlRepositoryPath",path);

它看起来很好,这时候我们不需要注册很多需要这样的配置的repository。然而,在更复杂的情况下,我们需要以某种方式自动注入这些参数。这里所有的这些设置是字符串的实例。因此,我们可以为字符串类型创建一个提供者,基于参数名称来生成我们的配置字符串。这个提供者将在应用程序配置文件(web.config或者app.config)的键里查找参数名,如果这样的一个配置是定义好的(像下面代码一样),它就返回它的值:

 using Ninject.Activation;
using System;
using System.Configuration; namespace DataMigration.Business.Provider
{
public class ConfigurationProvider : Provider<string>
{
protected override string CreateInstance(IContext context)
{
if (context.Request.Target == null)
{
throw new Exception("Target required.");
}
var paramName = context.Request.Target.Name;
string value = ConfigurationManager.AppSettings[paramName];
if (string.IsNullOrEmpty(value))
{
value = ConfigurationManager.ConnectionStrings[paramName] == null ? ""
: ConfigurationManager.ConnectionStrings[paramName].ConnectionString;
}
return value;
}
}
}

ConfigurationProvider被提供了一个包含了当前激活过程所有信息的context对象,包含在这篇文章之前提到过的请求对象。这个请求对象包含目标信息。在这种情况下,目标信息是哪个注入的字符串对象作为构造函数参数。如果被请求的字符串类型是直接从kernel使用Get<string>()方法请求的,目标对象将为空。因为我们需要参数名称作为配置键,我们首先检查目标。使用目标名称,我们可以查找AppSettings,如果我们没有找到这样一个配置,我们将继续在ConnectionStrings部分查找。最后,返回得到的值。

唯一的问题是这个提供者将被注册为字符串类型,他将影响到将要被Ninject解析的任何字符串。为了明确规定是那些将要被认为是应用程序配置的字符串,我们定义一个自定义特性,像下面这样在那些参数上运用它:

 using System;

 namespace DataMigration.Business.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class ConfigurationAttribute : Attribute { }
}

我们已经声明了这个特性只能运用在属性和参数上。下面是这个特性怎样运用到我们repository类构造函数参数上:

      public ShippersSqlRepository([Configuration]string connectionString)
{
_context = new NorthwindContext(connectionString);
}     public ShippersXmlRepository([Configuration]string xmlRepositoryPath)
{
this.documentPath = xmlRepositoryPath;
}

最后,绑定代码像下面这样:

          Bind<string>().ToProvider<ConfigurationProvider>().WhenTargetHas<ConfigurationAttribute>();

激活上下文

当我们的提供者重载这个CreateInstance方法,我们使用这个上下文context对象,通过方法参数传入。这个对象的类继承IContext接口,这个接口包含了非常多所有跟当前激活过程相关的信息。使用这个对象,我们可以访问当前绑定对象,正在被解析的类型,正在被注入的类型,我们当前在依赖图的什么位置,谁请求了这个解析,等等。在解析一个依赖图的时候,为每一个正在解析的类型创建一个上下文对象,这就导致了一个激活上下文图。从每一个上下文对象开始,我们也可以从他的父上下文节点导航,直到到达图的根,就是最初的发起请求的点。在使用Niinject时,任何我们需要决定怎样解析依赖的地方上下文对象都是可以得到的。

工厂方法

工厂方法是另一个通知Ninject怎样解析一个依赖。像创建一个提供者一样,我们已经访问了这个激活上下文对象,来帮助我们做出决定怎样解析请求类型。然而,我们不需要创建一个新类,我们可以只是内联地写出我们的解析逻辑。工程方法是提供者类的一个很好的替代,在这里解析逻辑是简单和简短的。一个好的使用工厂方法的例子是在一个类里实例化一个日志对象。下面是不使用DI实例化一个日志对象的代码:

 class ConsumerClass
{
private ILog log = LogManager.GetLogger(typeof(ConsumerClass));
}

我们可以在前面的类中使用下面的代码实现DI:

 class ConsumerClass
{
private ILog log;
public ConsumerClass(ILog log)
{
this.log = log;
}
}

为ILogger接口用To<T>()方法注册一个类型绑定是不合适的,因为具体的日志对象必须通过调用LogManager.GetLogger方法来创建,而不是通过具体日志类的构造函数。在这种情况下,我们可以使用一个工厂方法来通知Ninject创建一个新日志对象:

 Bind<ILog>().ToMethod(ctx => LogManager.GetLogger(ctx.Request.ParentRequest.Service));

这个ctx的类型是IContext,我们从这个Ninject激活上下文父请求的服务属性中,得到消费者类的类型。

Ninject之旅之十:Ninject自定义提供者的更多相关文章

  1. Ninject之旅之五:Ninject XML配置

    摘要 使用XML配置,需要添加Ninject XML扩展的引用.下一步是添加一个或多个包含类型注册的XML文件.记得这些文件应该跟应用程序一起发布.因此不要忘记将XML文件的属性设置成“Copy if ...

  2. Ninject之旅之十二:Ninject在Windows Form程序上的应用(附程序下载)

    摘要: 下面的几篇文章介绍如何使用Ninject创建不同类型的应用系统.包括: Windows Form应用系统 ASP.NET MVC应用系统 ASP.NET Web Form应用系统 尽管对于不同 ...

  3. Ninject之旅之十四:Ninject在ASP.NET Web Form程序上的应用(附程序下载)

    摘要 ASP.NET Web Forms没有像MVC那样的可扩展性,也不可能使它创建UI页面支持没有构造函数的的激活方式.这个Web Forms应用程序的的局限性阻止了它使用构造函数注入模式,但是仍能 ...

  4. Ninject之旅之三:Ninject对象生命周期

    摘要 DI容器的一个责任是管理他创建的对象的生命周期.他应该决定什么时候创建一个给定类型的对象,什么时候使用已经存在的对象.他还需要在对象不需要的时候处理对象.Ninject在不同的情况下管理对象的生 ...

  5. Ninject之旅之八:Ninject插件模型(附程序下载)

    摘要 在前面的章节中,我们看了在单一的绑定条件下Ninject能够处理依赖类型,就是说,每个服务类型只绑定到单一的实现类型.然而,有些情况下我们需要绑定一个抽象服务类型到多个实现,这叫多个绑定.多个绑 ...

  6. Ninject之旅之七:Ninject依赖注入

    摘要 可以使用不同的模式向消费者类注入依赖项,向构造器里注入依赖项是其中一种.有一些遵循的模式用来注册依赖项,同时有一些需要避免的模式,因为他们经常导致不合乎需要的结果.这篇文章讲述那些跟Ninjec ...

  7. Ninject之旅之六:Ninject约定

    摘要 在小的应用系统中一个一个注册一些服务类型不怎么困难.但是,如果是一个实际的有上百个服务的应用程序呢?约定配置允许我们使用约定绑定一组服务,而不用一个一个分别绑定. 要使用约定配置,需要添加Nin ...

  8. Ninject之旅之四:Ninject模块

    摘要 随着应用程序的增长,注册的服务列表跟着变长,管理这个列表将变得困难.Ninject模块是一个好的将我们的类型绑定分离到不同的绑定组的方式,它很容易地将分组组织到不同的文件中.将一个类变成一个Ni ...

  9. Ninject之旅目录

    第一章:理解依赖注入 Ninject之旅之一:理解DI 第二章:开始使用Ninject Ninject之旅之二:开始使用Ninject(附程序下载) Ninject之旅之三:Ninject对象生命周期 ...

随机推荐

  1. 关于回溯与n个数的全排列

    今天要讲的题目是全排列的问题:有1.2.3.....n这样一个数列,要求输出其全排列. 那么,显然,这道题目非常之简单,用一个标志数组变量,标记数字的使用情况,然后根据它挑选数字即可.由于题目很简单, ...

  2. 用Windows PowerShell 控制管理 Microsoft Office 365

    如果想要通过PowerShell控制管理Office365,首先要安装Microsoft Online Services Sign-In Assistant 7.0,链接如下 Microsoft On ...

  3. (转)LSTM NEURAL NETWORK FOR TIME SERIES PREDICTION

    LSTM NEURAL NETWORK FOR TIME SERIES PREDICTION Wed 21st Dec 2016   Neural Networks these days are th ...

  4. httpie工具介绍

    github:https://github.com/jkbrzt/httpie 一:HTTPie用途 HTTPie (读aych-tee-tee-pie)是一个 HTTP 的命令行客户端.其目标是让 ...

  5. myeclipse激活时cracker2015.jar打不开

    myeclipse激活时cracker2015.jar打不开 可能是jdk版本的问题 这是我用的myeclipse版本 myeclipse-2016-ci-7-offline-installer-wi ...

  6. MySql中常用的hint

    对于经常使用Oracle的朋友可能知道,oracle的hint功能种类很多,对于优化sql语句提供了很多方法.同样,在MySQL里,也有类似的hint功能.下面介绍一些常用的. 强制索引 FORCE ...

  7. CSS 伪类 (Pseudo-classes)

    CSS 伪类用于向某些选择器添加特殊的效果. CSS 伪类 (Pseudo-classes)实例: 超链接 本例演示如何向文档中的超链接添加不同的颜色. 超链接 2 本例演示如何向超链接添加其他样式. ...

  8. [PHP] - Laravel - 用户登陆中间件

    前言 Laravel 4中,可以使用Route::filter,而在Laravel 5中,没有了filter.php文件,官方建议使用中间件做. 下面是用户登陆的测试例子,涉及到的一些方法和使用,先参 ...

  9. svn 权限配置

    svnserve.conf开启权限 auth中以下设置 重启SVN服务

  10. day4 函数

    面向对象:-->类-->class面向过程:-->过程-->def函数式编程:-->函数-->def 函数是逻辑结构化和过程化的一种编程方法 使用函数的三大优点:减 ...