RESTful日#2:使用Unity容器和載入程式在Web api中使用依賴注入實現控制反轉
- 下載databasescript.zip - 1 KB
- 下載PdfArticle.zip - 3 MB
- 下載WebApiSourceCodeDay1 - 4 MB
- 下載WebApi.zip source code - 4.4 MB
表的內容 目錄介紹路線圖現有設計及問題介紹unity setup控制器setup services執行應用程式設計缺陷總結 介紹 我的文章將解釋如何使我們的Web API服務體系結構鬆散耦合並更加靈活。在我的上一篇文章中,我們已經瞭解瞭如何使用Asp.net Web API和實體框架建立RESTful服務。如果您還記得我們以一個帶有設計缺陷的解決方案結束,我們將通過解決依賴元件的依賴關係來克服該缺陷。對於那些沒有讀過我上一篇文章的人,他們可以通過將我的第一篇文章中的示例專案作為測試應用程式來學習。 圖片來源:https://www.pehub.com/wp-content/uploads/2013/06/independing-300x200.jpg 您可以使用各種方法來解決元件的依賴關係。在我的文章中,我將解釋如何在微軟的Unity應用程式塊提供的Unity容器的幫助下解決依賴關係。 我們不會深入到非常詳細的理論,對於DI和IOC的理論和理解,您可以遵循以下連結:統一和控制反轉(IOC)。我們將直接進入實際的實現。 路線圖 我們學習RESTful api的路線圖保持不變, RESTful日#1:企業級應用程式架構,使用實體框架、通用儲存庫模式和工作單元的Web api。RESTful日#2:使用Unity容器和載入程式在Web api中使用依賴注入實現控制反轉。RESTful日#3:使用Unity容器和可管理擴充套件框架(MEF)在Asp.net Web api中使用控制反轉和依賴注入來解決依賴關係的依賴關係。RESTful日#4:使用MVC 4 Web api中的屬性路由自定義URL重寫/路由。RESTful日#5:使用操作過濾器的Web api中基於基本身份驗證和令牌的自定義授權。RESTful日#6:使用操作過濾器、異常過濾器和NLog在Web api中進行請求日誌記錄和異常處理/日誌記錄。RESTful日#7:使用NUnit和Moq框架在WebAPI中進行單元測試和整合測試(第一部分)。使用NUnit和Moq框架在WebAPI中進行單元測試和整合測試(第二部分)。asp.net Web api restful日#10:建立自託管的ASP。NET WebAPI與CRUD操作在Visual Studio 2010 我有意使用Visual Studio 2010和。net Framework 4.0,因為在。net Framework 4.0中很少有很難找到的實現,但我將通過演示如何實現來簡化它。 現有設計及問題 我們已經有了一個現有的設計。如果你開啟解決方案,你會看到下面提到的結構, 模組在某種程度上是相互依賴的, 結構上沒有問題,但它們相互作用的方式確實有問題。您一定已經注意到,我們正在嘗試與層通訊,使類的物理物件。 如。 控制器建構函式使服務層物件通訊, 隱藏,複製Code
/// <summary> /// Public constructor to initialize product service instance /// </summary> public ProductController() { _productServices =new ProductServices(); }
服務建構函式又使UnitOfWork物件與資料庫通訊, 隱藏,複製Code
/// <summary> /// Public constructor. /// </summary> public ProductServices() { _unitOfWork = newUnitOfWork(); }
問題在於這些程式碼片段。控制器依賴於服務的例項化,而服務依賴於UnitOfWork來獲得例項化。我們的各層不應該緊密耦合,也不應該相互依賴。 建立物件的工作應該分配給其他人。我們的層不應該擔心建立物件。 我們將把這個角色分配給第三方,該第三方將稱為我們的容器。幸運的是,Unity為我們提供了幫助,通過注入依賴關係(不是通過新建物件,而是通過建構函式或屬性)來擺脫依賴問題,並反轉控制流。還有其他方法,但我不打算詳細介紹。 介紹了統一 你可以從這個連結瞭解Unity;我只是引用了幾句話, 圖片來源:http://img.tradeindia.com/fp/1/669/664.jpg Unity Application Block (Unity)是一個輕量級的、可擴充套件的依賴注入容器,支援建構函式注入、屬性注入和方法呼叫注入。它為開發人員提供了以下優點: 它簡化了物件的建立,特別是針對分層物件結構和依賴關係,從而簡化了應用程式程式碼。它支援需求的抽象;這允許開發人員在執行時或配置中指定依賴項,並簡化橫切關注點的管理。它通過將元件配置延遲到容器來增加靈活性。它具有服務定位能力;這允許客戶端儲存或快取容器。這在ASP中特別有用。NET Web應用程式,其中開發人員可以持久化容器在ASP。網路會話或應用程式。” 設定統一的 開啟你的Visual studio,我使用的是VS 2010,你可以使用VS 2010或以上版本。載入解決方案。 第一步:瀏覽到Tools->圖書館包管理器- >包管理器控制檯, 我們將為Unity應用程式塊新增包。 在Visual Studio的左下角,您將找到編寫命令的位置。 鍵入命令Install-Package Unity。在啟動命令之前,選擇“WebApi”專案。 然後鍵入命令安裝包Unity。WebAPI -版本0.10.0來安裝Unity。選擇WebApi專案,就像你對Unity.MVC3所做的那樣。 你可以使用Unity。WebAPI和統一。但是您需要糾正我們得到的bootstrapper類,這樣它應該同時使用這兩個庫。在安裝了兩個軟體包後,將bootstrap .cs檔案中的初始化方法更改為: 隱藏,複製Code
public static void Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new Unity.Mvc3.UnityDependencyResolver(container)); GlobalConfiguration.Configuration.ServiceResolver.SetResolver( new Unity.WebApi.UnityDependencyResolver(container)); }
步驟2:Bootstrapper類 團結。MVC3附帶了一個Bootstrapper類,一旦您執行該命令,Bootstrapper類就會在您的解決方案- WebAPI專案中生成, 隱藏,收縮,複製Code
using System.Web.Http; using System.Web.Mvc; using BusinessServices; using DataModel.UnitOfWork; using Microsoft.Practices.Unity; using Unity.Mvc3; namespace WebApi { public static class Bootstrapper { public static void Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); // register dependency resolver for WebAPI RC GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); } private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); // register all your components with the container here // it is NOT necessary to register your controllers // e.g. container.RegisterType<ITestService, TestService>(); container.RegisterType<IProductServices, ProductServices>().RegisterType<UnitOfWork>(new HierarchicalLifetimeManager()); return container; } } }
這個類帶有用於設定容器的初始配置。所有的功能都是內建的,我們只需要在“BuildUnityContainer”中指定需要解析的依賴項,就像在註釋語句中說的那樣, 隱藏,複製Code
// register all your components with the container here // it is NOT necessary to register your controllers // e.g. container.RegisterType<ITestService, TestService>();
步驟3:只指定需要解析的註釋行下面的元件。在我們的例子中,它是ProductServices和UnitOfWork, 隱藏,複製Code
container.RegisterType<IProductServices, ProductServices>().RegisterType<UnitOfWork>(new HierarchicalLifetimeManager());
“HierarchicalLifetimeManager”,對於這個生命週期管理器,就像對於ContainerControlledLifetimeManager一樣,Unity會在每次你呼叫Resolve或ResolveAll方法或者當依賴機制向其他類注入例項時返回註冊型別或物件的相同例項。區別在於,當存在子容器時,每個子容器解析它自己的物件例項,並且不與父容器共享一個例項。在父節點中解析時,行為就像一個容器控制的生命週期;在解析父節點和子節點時,您擁有不同的例項,每個例項充當容器控制的生存期。如果您有多個子節點,每個子節點將解析自己的例項。 如果沒有找到“UnitOfWork”,只需在WebAPI專案中新增對DataModel專案的引用。 所以Bootstrapper類就變成了, 隱藏,複製Code
public static class Bootstrapper { public static void Initialise() { var container = BuildUnityContainer(); DependencyResolver.SetResolver(new UnityDependencyResolver(container)); // register dependency resolver for WebAPI RC GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); } private static IUnityContainer BuildUnityContainer() { var container = new UnityContainer(); // register all your components with the container here // it is NOT necessary to register your controllers // e.g. container.RegisterType<ITestService, TestService>(); container.RegisterType<IProductServices, ProductServices>().RegisterType<UnitOfWork>(new HierarchicalLifetimeManager()); return container; }
像這樣,我們還可以在BuildUnityContainerMethod中指定其他依賴物件。 步驟4:現在我們需要呼叫Bootstrapper類的初始化方法。注意,我們需要在模組載入時立即載入物件,因此我們要求容器在載入應用程式時執行它的工作,因此它是全域性的。asax檔案,並新增一行呼叫初始化方法,因為這是一個靜態方法,我們可以直接使用類名呼叫它, 隱藏,複製Code
Bootstrapper.Initialise();
我們的全球。asax就 隱藏,收縮,複製Code
using System.Linq; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using Newtonsoft.Json; using WebApi.App_Start; namespace WebApi { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //Initialise Bootstrapper Bootstrapper.Initialise(); //Define Formatters var formatters = GlobalConfiguration.Configuration.Formatters; var jsonFormatter = formatters.JsonFormatter; var settings = jsonFormatter.SerializerSettings; settings.Formatting = Formatting.Indented; // settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); var appXmlType = formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml"); formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType); //Add CORS Handler GlobalConfiguration.Configuration.MessageHandlers.Add(new CorsHandler()); } } }
工作已經完成了一半。現在我們需要修改控制器和服務類建構函式,以便在載入應用程式時利用已經為它們建立的例項。 設定控制器 我們已經在應用程式中設定了unity。有很多方法可以通過服務定位器注入依賴項,比如建構函式注入、屬性注入。我在這裡使用建構函式注入,因為我發現它是與Unity容器一起解決依賴關係的最好方法。 到ProductController,你會發現建構函式寫為, 隱藏,複製Code
/// <summary> /// Public constructor to initialize product service instance /// </summary> public ProductController() { _productServices =new ProductServices(); }
只需向建構函式新增一個引數,它接受ProductServices引用,就像下面所做的那樣 隱藏,複製Code
/// <summary> /// Public constructor to initialize product service instance /// </summary> public ProductController(IProductServices productServices) { _productServices = productServices; }
並使用引數初始化您的“productServices”變數。在這種情況下,當控制器的建構函式被呼叫時,它將被預先例項化的服務例項服務,並且不需要建立服務的例項,我們的unity容器完成了物件建立的工作。 設定服務 在服務方面,我們也採用同樣的方式。只要開啟ProductServices類,我們就可以看到這裡UnitOfWork的依賴關係, 隱藏,複製Code
/// <summary> /// Public constructor. /// </summary> public ProductServices() { _unitOfWork = new UnitOfWork(); }
同樣,我們執行相同的步驟,並將型別為UnitOfWork的引數傳遞給建構函式, 我們的程式碼, 隱藏,複製Code
/// <summary> /// Public constructor. /// </summary> public ProductServices(UnitOfWork unitOfWork) { _unitOfWork = unitOfWork; }
這裡我們還將在UnitOfWork上獲得預例項化的物件。因此service確實需要擔心建立物件。還記得我們在Bootstrapper類中使用了. registertype <UnitOfWork>()嗎? 現在我們已經使元件獨立了。 圖片來源:http://4.bp.blogspot.com/-q-o5SXbf3jw/T0ZUv0vDafI/AAAAAAAAAY4/_O8PgPNXIKQ/s320/h1.jpg 執行應用程式 我們的工作快完成了。我們需要執行應用程式,只需按F5。讓我們驚訝的是,我們最終會出現一個錯誤頁面, 您還記得我們在專案中添加了一個測試客戶端來測試我們的API嗎測試客戶端也有一個控制器,我們需要覆蓋它的設定,使我們的應用程式工作。只需在WebAPI專案中找到area - HelpPage- Controllers- HelpController,如下所示, 註釋掉現有的建構函式並新增一個配置屬性,如下所示, 隱藏,複製Code
//Remove constructors and existing Configuration property. //public HelpController() // : this(GlobalConfiguration.Configuration) //{ //} //public HelpController(HttpConfiguration config) //{ // Configuration = config; //} //public HttpConfiguration Configuration { get; private set; } /// <summary> /// Add new Configuration Property /// </summary> protected static HttpConfiguration Configuration { get { return GlobalConfiguration.Configuration; } }
我們的控制器程式碼變成, 隱藏,收縮,複製Code
using System; using System.Web.Http; using System.Web.Mvc; using WebApi.Areas.HelpPage.Models; namespace WebApi.Areas.HelpPage.Controllers { /// <summary> /// The controller that will handle requests for the help page. /// </summary> public class HelpController : Controller { //Remove constructors and existing Configuration property. //public HelpController() // : this(GlobalConfiguration.Configuration) //{ //} //public HelpController(HttpConfiguration config) //{ // Configuration = config; //} //public HttpConfiguration Configuration { get; private set; } /// <summary> /// Add new Configuration Property /// </summary> protected static HttpConfiguration Configuration { get { return GlobalConfiguration.Configuration; } } public ActionResult Index() { return View(Configuration.Services.GetApiExplorer().ApiDescriptions); } public ActionResult Api(string apiId) { if (!String.IsNullOrEmpty(apiId)) { HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); if (apiModel != null) { return View(apiModel); } } return View("Error"); } } }
執行這個應用程式,我們得到, 我們已經添加了測試客戶端,但是對於新的讀者,我只是再次解釋如何將測試客戶端新增到我們的API專案中。 只要去管理Nuget包,通過右擊WebAPI專案,並輸入WebAPITestClient在搜尋框的線上包, 您將獲得“一個用於ASP的簡單測試客戶端”。NET Web API”,只要新增它。你將在以下區域得到一個幫助控制器->如下圖所示, 我在上一篇文章中已經提供了資料庫指令碼和資料,您可以使用相同的指令碼和資料。 在應用程式url中新增“/help”,您將獲得測試客戶端, 您可以通過單擊每個服務來測試它。單擊服務連結後,您將被重定向到測試該特定服務的服務頁面。在這個頁面的右下角有一個按鈕測試API,只要按下那個按鈕就可以測試你的服務, GetAllProduct服務, 為了創造新產品, 在資料庫中,我們有新產品, 更新產品: 我們進入資料庫, 刪除產品: 在資料庫: 工作。 圖片來源:http://codeopinion.com/wp-content/uploads/2015/02/injection.jpg 設計缺陷 如果我說在這個設計中還有缺陷,這個設計仍然不是鬆散耦合的。 您還記得我們在編寫第一個應用程式時的決定嗎? 我們的API與服務對話,服務與資料模型對話。出於安全原因,我們永遠不會允許資料模型與api對話。但是您是否注意到,當我們在Bootstrapper類中註冊型別時,我們還註冊了UnitOfWork的型別,這意味著我們添加了DataModel作為API專案的引用。這是一個設計漏洞。我們試圖通過破壞我們的設計並危及安全性來解決依賴關係中的依賴關係。 在我的下一篇文章中,我們將克服這種情況,我們將嘗試在不破壞設計和損害安全性的情況下解決依賴性及其依賴性。事實上,我們將使其更安全、更鬆耦合。 在下一篇文章中,我們將使用託管可擴充套件性框架(MEF)來實現同樣的功能。 結論 我們現在知道了如何使用Unity容器來解決依賴關係和執行控制反轉。 但是這個設計還是有一些缺陷。在下一篇文章中,我將嘗試使系統更強大。你也可以從GitHub下載原始碼。如果原始碼中缺少所需的包,則新增它們。 我的其他系列文章: MVC: http://www.codeproject.com/Articles/620195/Learning-MVC-Part-Introduction-to-MVC-Architectu OOP: http://www.codeproject.com/Articles/771455/Diving-in-OOP-Day-Polymorphism-and-Inheritance-Ear 本文轉載於:http://www.diyabc.com/frontweb/news401.html