Ninject
摘要: 在前面的章节中,我们看了在单一的绑定条件下Ninject能够处理依赖类型,就是说,每个服务类型只绑定到单一的实现类型。然而,有些情况下我们需要绑定一个抽象服务类型到多个实现,这叫多个绑定。多个绑定有两种情况。第一个是插件模型实现,另一个是上下文绑定。这篇文章介绍插件模型实现,下一篇文章介绍上下文绑定。阅读全文
posted @ 2016-11-16 21:46 丹尼大叔 阅读(121) | 评论 (0) 编辑
摘要: 可以使用不同的模式向消费者类注入依赖项,向构造器里注入依赖项是期中一种。有一些遵循的模式用来注册依赖项,同时有一些需要避免的模式,因为他们经常导致不合乎需要的结果。这篇文章讲述那些跟Ninject功能相关的模式和反模式。然而,全面的介绍可以在Mark Seemann的书《Dependency Injection in .NET》中找到。阅读全文
posted @ 2016-11-08 21:52 丹尼大叔 阅读(25) | 评论 (0) 编辑
摘要: 在小的应用系统中一个一个注册一些服务类型不怎么困难。但是,如果是一个实际的有上百个服务的应用程序呢?约定配置允许我们使用约定绑定一组服务,而不用一个一个分别绑定。阅读全文
posted @ 2016-11-07 22:03 丹尼大叔 阅读(14) | 评论 (0) 编辑
摘要: 使用XML配置,需要添加Ninject XML扩展的引用。下一步是添加一个或多个包含类型注册的XML文件。记得这些文件应该跟应用程序一起发布。因此不要忘记将XML文件的属性设置成“Copy if newer”。阅读全文
posted @ 2016-09-28 21:24 丹尼大叔 阅读(21) | 评论 (0) 编辑
摘要: 随着应用程序的增长,注册的服务列表跟着变长,管理这个列表将变得困难。Ninject模块是一个好的将我们的类型绑定分离到不同的绑定组的方式,它很容易地将分组组织到不同的文件中。将一个类变成一个Ninject模块只需要将这个类继承INinjectModule接口。每次创建一个Module的时候都需要实现这个接口,需要实现三个方法和两个属性。创建一个抽象类实现这个接口,之后每次创建Ninject模块的时候都继承这个抽象类,这是一个好主意。好消息是Ninject已经有一个这样的抽象类,名称是NinjectModule。阅读全文
posted @ 2016-09-26 20:26 丹尼大叔 阅读(42) | 评论 (0) 编辑
摘要: DI容器的一个责任是管理他创建的对象的生命周期。他应该决定什么时候创建一个给定类型的对象,什么时候使用已经存在的对象。他还需要在对象不需要的时候处理对象。Ninject在不同的情况下管理对象的生命周期提供了强大的支持。在我们定义一个绑定的时候,定义创建对象的范围。在那个范围内,对象将被重用,每次绑定只存在一次。注意,对象不允许依赖于生命周期短自己小的对象。阅读全文
posted @ 2016-08-07 14:20 丹尼大叔 阅读(166) | 评论 (0) 编辑
Ninject之旅之二:开始使用Ninject(附程序下载)
摘要: 这篇文章介绍怎样将Ninject添加到实际的项目中,使用Ninject框架最基本的功能。首先用一个Hello World例子介绍怎么添加和使用Ninject。然后用一个更复杂的例子,介绍Ninject绑定是怎样管理依赖的。阅读全文
posted @ 2016-08-04 21:43 丹尼大叔 阅读(294) | 评论 (0) 编辑
摘要: DI(IoC)是当前软件架构设计中比较时髦的技术。DI(IoC)可以使代码耦合性更低,更容易维护,更容易测试。现在有很多开源的控制反转的框架,Ninject是期中一个轻量级开源的.net DI(IoC)框架。目前已经非常成熟,已经在很多项目中使用。这篇文章讲DI概念以及使用它的优势。使用一个简单的例子,重构这个例子让他逐步符合DI设计原则。阅读全文
posted @ 2016-08-02 14:57 丹尼大叔 阅读(398) | 评论 (4) 编辑
摘要
在前面的章节中,我们看了在单一的绑定条件下Ninject能够处理依赖类型,就是说,每个服务类型只绑定到单一的实现类型。然而,有些情况下我们需要绑定一个抽象服务类型到多个实现,这叫多个绑定。多个绑定有两种情况。第一个是插件模型实现,另一个是上下文绑定。这篇文章介绍插件模型实现,下一篇文章介绍上下文绑定。
插件模型让一个应用程序获得很强的可扩展性而不用修改源代码。下面的例子,我们将实现一个音乐播放器应用程序,使用解码插件来支持不同的音乐格式。这个应用程序使用两个内置的解码器,也可以添加更多的解码器来扩展我们播放器支持的格式。请注意为了让应用程序尽可能简单,许多复杂的细节将不被实现。
先定义一个解码器接口:
1 public interface ICodec 2 { 3 string Name { get; } 4 bool CanDecode(string extension); 5 Stream Decode(Stream inStream); 6 }
添加两个解码器类实现解码器接口:
Mp3:
1 public class Mp3Codec : ICodec 2 { 3 public string Name 4 { 5 get 6 { 7 return "MP3 Audio"; 8 } 9 } 10 11 public bool CanDecode(string extension) 12 { 13 return extension == ".mp3"; 14 } 15 16 public Stream Decode(Stream inStream) 17 { 18 //some decode logic added here 19 return null; 20 } 21 }
Wma:
1 public class WmaCodec : ICodec 2 { 3 public string Name 4 { 5 get 6 { 7 return "Windows Media Auido"; 8 } 9 } 10 11 public bool CanDecode(string extension) 12 { 13 return extension == ".wma"; 14 } 15 16 public Stream Decode(Stream inStream) 17 { 18 //some decode logic added here 19 return null; 20 } 21 }
下一步是实现我们可插拔的播放器类。让播放器可扩展的是他依赖一系列的ICodec对象,而不是某些具体的解码器:
1 public class Player 2 { 3 private readonly ICodec[] codecs; 4 // Note that the constructor parameter is not a single ICodec. 5 public Player(IEnumerable<ICodec> codecs) 6 { 7 this.codecs = codecs.ToArray(); 8 } 9 }
然后添加一个Play方法到播放器类:
1 public void Play(FileInfo fileInfo) 2 { 3 ICodec supportingCodec = FindCodec(fileInfo.Extension); 4 using (var rawStream = fileInfo.OpenRead()) 5 { 6 var decodedStream = supportingCodec.Decode(rawStream); 7 PlayStream(decodedStream); 8 } 9 }
这个方法接收一个FileInfo对象,查找合适的解码器后,解码播放这个文件。
实现FindCodec方法:
1 private ICodec FindCodec(string extension) 2 { 3 foreach (ICodec codec in codecs) 4 if (codec.CanDecode(extension)) 5 return codec; 6 throw new Exception("File type not supported."); 7 }
FindCodec调用每个codec对象的CanDecode方法来找到支持这个文件扩展名的解码器。如果找不到任何合适的解码器,将抛出一个异常。我们需要记住的一件事是没有具体的解码器在foreach循环之前被解析。
最后在Main方法里添加下面的代码:
1 using (var kernel = new StandardKernel()) 2 { 3 kernel.Bind(b => b.FromAssembliesMatching("*") 4 .SelectAllClasses() 5 .InheritedFrom<ICodec>() 6 .BindAllInterfaces()); 7 }
前面的约定指示Ninject自动注册所有的ICodec接口的实现而不是为他们分别每个都声明绑定。
因为ICodec类型被绑定到多个实现,他只能被解析成一系列的对象而不是单一的对象。因此,使用下面的构造函数解析ICodec将导致一个运行时异常:
1 public Consumer(ICodec codec){}
下面的代码也将产生运行时异常:
1 ICodec codec = Kernel.Get<ICodec>();
上面两行代码,Ninject将试着解析ICodec接口,但是它发现多余一个具体的实现类型。代替使用Get<T>,我们可以调用GetAll<T>方法来得到ICodec的所有实现。下面的代码显示所有的支持的codec:
1 IEnumerable<ICodec> codecs = kernel.GetAll<ICodec>(); 2 foreach (ICodec codec in codecs) 3 System.Console.WriteLine(codec.Name);
现在,任何的在应用程序的根路径下,有ICodec实现的程序集都将被我们的播放器应用程序认为是一个codec插件。我们的应用程序甚至不需要添加对codec工程的引用。
下面是完整的播放器类代码:
1 public class Player 2 { 3 private readonly ICodec[] _codecs; 4 5 // Note that the constructor parameter is not a single ICodec. 6 public Player(IEnumerable<ICodec> codecs) 7 { 8 this._codecs = codecs.ToArray(); 9 } 10 11 public void Play(FileInfo fileInfo) 12 { 13 ICodec supportingCodec = FindCodec(fileInfo.Extension); 14 using (var rawStream = fileInfo.OpenRead()) 15 { 16 var decodedStream = supportingCodec.Decode(rawStream); 17 PlayStream(decodedStream); 18 } 19 } 20 21 private void PlayStream(Stream decodedStream) 22 { 23 return; 24 } 25 26 private ICodec FindCodec(string extension) 27 { 28 foreach (ICodec codec in _codecs) 29 { 30 if (codec.CanDecode(extension)) 31 { 32 return codec; 33 } 34 } 35 throw new Exception("File type not supported."); 36 } 37 }