一 概要
StructureMap是.net最早使用的IOC/DI容器,2004年7月首次发布和使用在.net11版本中。目前的版本4*中从过去十二年中structureMap和.net community中吸取经验,同时去掉了对于现在没有多大意义的遗留决策。
特色:StructureMap是一个功能丰富的IoC工具,它支持拦截、对象生命周期和智能处理模式、开放泛型类型、模块注册、常规注册、自定义策略以及所有您期望在现代的注入模式支持。净IoC容器。
诊断:IoC工具可以是配置密集型的,也可能属于“不可思议”的工具类别。这是可以的,因为StructureMap已经覆盖了大量的诊断和智能异常消息,以帮助您解决可能遇到的任何问题。
集成:structureMap已经成功地集成到大多数常用的.net 框架中。StructureMap还可以帮助您管理生命周期和资源清理,使团队能够更有效地使用诸如 Entity Framework之类的持久性工具。
2014年初发布的StructureMap 3.0标志着StructureMap internals and public API的巨大变化。4.0版本是一个更小的版本,主要改进了类型扫描、诊断和性能。从3到4的唯一改变是在自定义类型扫描约定、删除ObjectFactory和消除一些晦涩的配置API。
首先你要知道的是,结构地图(以及其他的IoC工具,类似于它)的设计是为了让结构和模块化的软件设计更容易通过卸载那些复杂的解决依赖的机制,读取配置数据,并将对象图表组装到IoC的工具中而不是把你的应用代码写在上面。在开始使用StructureMap之前,建议您先熟悉依赖注入和控制反转的软件设计概念。重要的是,您需要在头脑中构建并构建应用程序,以充分利用结构映射的功能。假设您已经熟悉这些概念,或者你真的,而跳过迂腐和跳转到具体的代码,首先要做的是,去StructureMap和进入使用。
二 简单实例
总的来说,你真的只有做两种事情StructureMap:
1。配置容器,通过注册什么和结构映射应该如何构建或查找基于类型和/或名称的请求服务。
2。解析服务或依赖项的所有依赖项的对象实例。
假设你有一个简单的对象模型
public interface IBar { } public class Bar : IBar { } public interface IFoo { } public class Foo : IFoo { public IBar Bar { get; private set; } public Foo(IBar bar) { Bar = bar; } }
您可以显式地构建一个StructureMap容器对象来构建这样的类型:
// Configure and build a brand new // StructureMap Container object var container = new Container(_ => { _.For<IFoo>().Use<Foo>(); _.For<IBar>().Use<Bar>(); }); // Now, resolve a new object instance of IFoo container.GetInstance<IFoo>() // should be type Foo .ShouldBeOfType<Foo>() // and the IBar dependency too .Bar.ShouldBeOfType<Bar>();
或者利用StructureMap的类型扫描约定来配置关系,并做类似的事情:
var container = new Container(_ => { _.Scan(x => { x.TheCallingAssembly(); x.WithDefaultConventions(); }); }); container.GetInstance<IFoo>() .ShouldBeOfType<Foo>() .Bar.ShouldBeOfType<Bar>();
整合StructureMap在您的应用程序
在某个时刻,您将希望将StructureMap集成到应用程序中。无论您使用的是windowspresentation Foundation(WPF),FubuMVC,ASP。净WebForms,ASP。NET MVC或其他框架或技术,您将不得不做一些管道和引导。根据使用的技术或框架可以有重要的集成点,您必须使用完全启用StructureMap的力量。
虽然StructureMap并没有为所有的框架和技术提供集成支持,但是我们发现帮助您开始将StructureMap集成到应用程序中是非常重要的。也就是说,StructureMap确实为FubuMVC提供了集成支持(一个web框架,它与StructureMap是同一个家族的一部分)。
三 术语表
有一些术语在文档中重新出现,并出现在StructureMap API中。了解这些术语以及它们与使用StructureMap StructureMap不是一个先决条件,但也有帮助。
1.容器
像StructureMap这样的工具通常称为反转控制(IoC)容器或依赖注入(DI)容器。在Java世界中,它们也被称为轻量级容器,以区别于旧的EJB容器。容器是一种工具,可以帮助您编写对象图并管理其范围(生命周期)。您可以手动执行控制反转和依赖注入,使用像StructureMap这样的工具可以使您的工作更加高效和成功。显然,容器比解决服务和管理它们的范围更重要,但在核心中这就是它的本质。在此之前,您需要告诉StructureMap,容器,它必须如何组成这些对象图以及它们的生命周期。这叫做注册,可以用各种不同的方式进行。强烈建议使用注册表DSL。在您的注册中,您基本上是将抽象映射到具体类型并定义它们的生命周期。
一个使用注册中心DSL的容器的简单示例:
public class FooBarRegistry : Registry { public FooBarRegistry() { For<IFoo>().Use<Foo>(); For<IBar>().Use<Bar>(); } }
var container = new Container(c => { c.AddRegistry<FooBarRegistry>(); });
因为我们没有为这两个注册程序指定生命周期,所以将使用默认的临时生命周期。这将指示容器为插件类型IFoo或IBar的每个请求创建一个新实例。容器能做的更高级的功能是:截取、自动连接、转发类型。
2.嵌套的容器
嵌套容器用于标记短时间的事务或web请求的范围,并跟踪和清理实现该操作的IDisposable接口的对象。您可以让一个现有的容器为您创建一个嵌套的容器,例如:
using (var nested = someExistingContainer.GetNestedContainer()) { // pull other objects from the nested container and do work with those services var service = nested.GetInstance<IService>(); service.DoSomething(); }
有关嵌套容器及其特殊属性的更详细信息,您可以阅读嵌套容器(每个请求/事务)主题。
3.PluginType和PluggedType
“插件”一词在整个代码和文档中被用来表示您想要注册或解析的类型。通常,这种类型称为服务类型。这种类型可以是一个具体的类,或者在大多数情况下,它将是抽象类或接口的一种抽象形式。
“插入”一词意味着你在请求插件类型时所得到的实际的具体类型。这个类型显然必须实现插件类型的契约。在你的注册中你可以有这样的东西:
public class FooRegistry : Registry { public FooRegistry() { For<IFoo>().Use<Foo>(); } }
//For<PLUGINTYPE>().Use<PLUGGEDTYPE>() var container = new Container(c => { c.AddRegistry<FooRegistry>(); }); container.GetInstance<IFoo>(); //container.GetInstance<PLUGINTYPE>()
如果请求一个IFoo对象,就会得到Foo类的一个实例。在这种情况下,IFoo是插件类型(你想要的是什么)和Foo是插入类型(你将得到的具体类,实现了插件类型的契约)。
4.PluginFamily
这个术语你不会经常看到,因为它主要是由StructureMap本身使用的。一个PluginFamily表示一个CLR类型(插件类型),结构映射可以构建,并且所有可能的插入类型都实现了CLR类型。
在以下代码StructureMap内部创建一个插件类型的PluginFamily IFoo Foo和SomeOtherFoo两个实例,实例Foo是默认的,因为它的注册通过< PLUGIN_TYPE >().Use < PLUGGED_TYPE >()。
var container = new Container(c => { c.For<IFoo>().Use<Foo>(); c.For<IFoo>().Add<SomeOtherFoo>(); });
在StructureMap 3.0之前,当您请求一个没有定义默认实例的插件类型时,您可能已经看到了在异常消息中使用的术语。
StructureMap Exception Code: 202 No Default Instance defined for PluginFamily [plugin type]
这个特殊的异常消息已经在3.0中消失了,因为异常消息已经实现了现代化。
5. 插件图
PluginGraph是配置模型的运行时配置StructureMap容器。可以直接在StructureMap 3.0中对PluginGraph模型进行操作,用于任何不符合现有常规支持的特殊约定。
6.实例
StructureMap而言,一个“实例”是一个配置和命名的策略构建或定位为一个请求插件类型命名对象实例。“实例”并不自动等同于具体类型。例如,假设我们正在构建一个自动化仓库的系统。我们的系统可能使用一个接口称为IShippingService充当网关包装箱的各种方式从我们的仓库。
public interface IShippingService { void ShipIt(); }
我们的仓库系统可能需要与三种类型的海运进行交互:国内的、国际的、公司内部的或内部的运输。内部运输服务在仓库的应用程序中运行,但国内和国际运输是通过调用外部web服务来完成的。登记IShippingService实例可能看起来像这样:
public class ShippingRegistry : Registry { public ShippingRegistry() { For<IShippingService>().AddInstances(x => { x.Type<ShippingWebService>() .Ctor<string>("url").Is("a url") .Named("Domestic"); x.Type<ShippingWebService>() .Ctor<string>("url").Is("a different url") .Named("International"); x.Type<InternalShippingService>().Named("Internal"); }); } }
在上面的注册代码中,有三个“实例”。“你可以访问各种IShippingService实例的名称:
var container = new Container(new ShippingRegistry()); // Accessing the IShippingService Instance's by name var internationalService = container.GetInstance<IShippingService>("International"); var domesticService = container.GetInstance<IShippingService>("Domestic"); var internalService = container.GetInstance<IShippingService>("Internal");
要求“国际”或“国内”IShippingService都将返回一个对象的实例ShippingWebService类型,但两个对象将是不同的配置与独特的Url。在StructureMap中有一个实际的类表示一个“实例”。当你调用容器。GetInstance < T >("我想要的实例")或容器,GetInstance < T >(),内部容器对象正在定位正确的实例对象然后使用实例的内部构建计划来解析或构造实际的对象。
一个StructureMap“实例”与许多其他IoC工具所称的“组件”非常相似。
7.生命周期(或范围)
IoC容器的功能不仅仅是为您构建对象图,它还涉及到将对象图映射到结构映射调用的生命周期。这样想:当你向StructureMap请求服务或(通常是这样),当StructureMap填充依赖在幕后,你想要:
•每一次都有一个全新的、独特的对象?
•与图的其余部分完全相同的对象是什么?
•在整个应用程序中每一次都有相同的对象?
8.注册表 Registry
注册或注册表是一个类的子类,让你创建可重用配置StructureMap容器。
9.配置文件
StructureMap 3.0的特点是完整地重写了这个古老的概要文件功能,在这里您可以创建基本的容器配置,并使用附加的配置文件配置来覆盖一个或多个父容器的默认值。Profile功能最初是用来处理开发、测试和生产环境之间的差异,但在多租户情况下更常用。将概要文件视为应用程序或租户模式。
10.自动布线
如果您必须告诉StructureMap如何构建每个构造函数或setter依赖于每个具体类,那么您将无法完成任何工作。幸运的是,像大多数IoC容器工具一样,结构映射支持自动连接的概念——这意味着结构映射可以很好地从构造函数和setter规则中推断依赖性需求,并使用声明的依赖项的默认配置来填充这些依赖项。让我们来看看它的作用:
public interface Xman { } public class Cyclops : Xman { } public interface Avenger { } public class IronMan : Avenger { } public class CrossoverEvent { public Xman Xman { get; set; } public Avenger Avenger { get; set; } public CrossoverEvent(Xman xman, Avenger avenger) { Xman = xman; Avenger = avenger; } } public class UsingCrossover { [Fact] public void showing_auto_wiring() { var container = new Container(x => { x.For<Xman>().Use<Cyclops>(); x.For<Avenger>().Use<IronMan>(); }); // Notice that at no point did we define how to // build CrossoverEvent. var @event = container.GetInstance<CrossoverEvent>(); @event.Avenger.ShouldBeOfType<IronMan>(); @event.Xman.ShouldBeOfType<Cyclops>(); } }