Ninject之旅之十:Ninject自定义提供者
摘要
提供者是特殊的工厂类,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自定义提供者的更多相关文章
- Ninject之旅之五:Ninject XML配置
摘要 使用XML配置,需要添加Ninject XML扩展的引用.下一步是添加一个或多个包含类型注册的XML文件.记得这些文件应该跟应用程序一起发布.因此不要忘记将XML文件的属性设置成“Copy if ...
- Ninject之旅之十二:Ninject在Windows Form程序上的应用(附程序下载)
摘要: 下面的几篇文章介绍如何使用Ninject创建不同类型的应用系统.包括: Windows Form应用系统 ASP.NET MVC应用系统 ASP.NET Web Form应用系统 尽管对于不同 ...
- Ninject之旅之十四:Ninject在ASP.NET Web Form程序上的应用(附程序下载)
摘要 ASP.NET Web Forms没有像MVC那样的可扩展性,也不可能使它创建UI页面支持没有构造函数的的激活方式.这个Web Forms应用程序的的局限性阻止了它使用构造函数注入模式,但是仍能 ...
- Ninject之旅之三:Ninject对象生命周期
摘要 DI容器的一个责任是管理他创建的对象的生命周期.他应该决定什么时候创建一个给定类型的对象,什么时候使用已经存在的对象.他还需要在对象不需要的时候处理对象.Ninject在不同的情况下管理对象的生 ...
- Ninject之旅之八:Ninject插件模型(附程序下载)
摘要 在前面的章节中,我们看了在单一的绑定条件下Ninject能够处理依赖类型,就是说,每个服务类型只绑定到单一的实现类型.然而,有些情况下我们需要绑定一个抽象服务类型到多个实现,这叫多个绑定.多个绑 ...
- Ninject之旅之七:Ninject依赖注入
摘要 可以使用不同的模式向消费者类注入依赖项,向构造器里注入依赖项是其中一种.有一些遵循的模式用来注册依赖项,同时有一些需要避免的模式,因为他们经常导致不合乎需要的结果.这篇文章讲述那些跟Ninjec ...
- Ninject之旅之六:Ninject约定
摘要 在小的应用系统中一个一个注册一些服务类型不怎么困难.但是,如果是一个实际的有上百个服务的应用程序呢?约定配置允许我们使用约定绑定一组服务,而不用一个一个分别绑定. 要使用约定配置,需要添加Nin ...
- Ninject之旅之四:Ninject模块
摘要 随着应用程序的增长,注册的服务列表跟着变长,管理这个列表将变得困难.Ninject模块是一个好的将我们的类型绑定分离到不同的绑定组的方式,它很容易地将分组组织到不同的文件中.将一个类变成一个Ni ...
- Ninject之旅目录
第一章:理解依赖注入 Ninject之旅之一:理解DI 第二章:开始使用Ninject Ninject之旅之二:开始使用Ninject(附程序下载) Ninject之旅之三:Ninject对象生命周期 ...
随机推荐
- 时间戳 JavaScript parse() 方法 处理技巧
返回1970/01/01至2012/3/21之间的毫秒数: var d = Date.parse("March 21, 2012"); d 输出结果: 1332259200000 ...
- oracle数据库从入门到精通
oracle产品线围绕企业开发平台的企业开发平台四大组件:unix,weblogic中间件,java编程语言,oracle数据库oracle 开发主要分两类数据库管理:dba数据库编程:分两部分 ...
- 模拟discuz发帖的类实现
一直想弄个discuz的数据采集程序,这2天研究了下discuz发帖涉及的几个数据库表,这里分享一下自己的处理方法. discuz发表主题设计的几个表:(这里列出了主要的几个相关的表) 1.主题表 p ...
- JavaScript使用XMLHttpRequest 發送GET/Post 請求
<!DOCTYPE HTML> <html> <head> <title>Demo</title> <script type=&quo ...
- .NET设计规范————命名规范
NET设计规范:约定.惯用法与模式———命名规范 前言: 最近在看<.NET设计规范:约定.惯用法与模式>一书,主要还是讲.NET的设计规范,以前对这一块也不是特别在意, ...
- TFS下的源代码控制
以下主要描述了: TFS源代码控制系统的基本场景 如何把一个项目添加到源代码管理中 如何与服务器同步 如何做Check-In 如何做分支与合并 什么是上架与下架 我们知道工作项是项目管理的基本元素,但 ...
- java同一个类不同方法间的同步
对象的方法中一旦加入synchronized修饰,则任何时刻只能有一个线程访问synchronized修饰的方法.假设有个数据对象拥有写方法与读方法,多线程环境中要想保证数据的安全,需对该对象的读写方 ...
- AJAX-创建XMLHttpRequest对象
AJAX-创建XMLHttpRequest对象 1.XMLHttpRequest是AJAX的基础,所有现在浏览器都支持,用于在后台与服务器交换数据,也就意味着可以在不加载整个页面的情况下对整个页面进 ...
- 【SFTP】使用Jsch实现Sftp文件下载-支持断点续传和进程监控
参考上篇文章: <[SFTP]使用Jsch实现Sftp文件下载-支持断点续传和进程监控>:http://www.cnblogs.com/ssslinppp/p/6248763.html ...
- mybatis实战教程(mybatis in action)之一:开发环境搭建
mybatis 的开发环境搭建,选择: eclipse j2ee 版本,mysql 5.1 ,jdk 1.7,mybatis3.2.0.jar包.这些软件工具均可以到各自的官方网站上下载. 首先建立一 ...