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对象生命周期 ...
随机推荐
- 井间数据polarization analysis 相关概念
1. 垂直分量上记录到的数据,无法记录SH波?这个有待考证,先记录于此~ 两点需要注意:1.层状介质中,P波和深度方向(Z轴)组成入射面;2.SH的定义为垂直于入射面的S波分量. 2.VSP的观测方式 ...
- mac快捷键
切换 1. 应用程序切换 command tab 2.应用程序中的窗口间切换 command ~ ===================== 通用 1. 隐藏窗口 command H 2. 最小化窗口 ...
- Arcgis Server 10.2默认服务端口号修改方法
本人安装Arcgis Server 10.2之后发布了一个地图服务,该服务默认使用的端口号是6080,本人使用的是教育网,使用教育网均能正常使用该服务,但是使用电信或者移动网络均不能正常访问该网站. ...
- EX:The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.
EX:The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secu ...
- C语言实现简单php自定义扩展
1.下载php源码 下载地址:http://cn2.php.net/get/php-5.6.29.tar.gz/from/this/mirror 传到/usr/local/src/下 上传命令:rz ...
- ng-if ng-show ng-hide 的区别
angularjs ng-if ng-show ng-hide区别 在使用anularjs开发前端页面时,常常使用ng-show.ng-hide.ng-if功能来控制页面元素的显示或隐藏,那他们之间有 ...
- [PHP] - Laravel - 修改laravel_session的cookie名称
修改Cookie laravel_session的名称方法: 打开文件:config\session.php 找到值:laravel_session 修改为你所需要的cookie名称即可. 当然,还有 ...
- About SSDT BI
Welcome to Microsoft Marketing Speak hell. With the 2012 release of SQL Server, the BIDS, Business I ...
- DELL_LCD错误提示代码
代码 文本 原因E1000 Failsafe voltage error. Contact support.(故障保护电压错误.请联络支持人员.) 查看系统事件记录以了解严重故障事件.E1114 Am ...
- java基础-servlet-2:生命周期
1.加载(class load) 2.实例化 3.init() 4.doGet() 5.destroy 只有一个对象存在于服务端提供服务.再次访问,不会再起新对象.