一 概要
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>(); } }