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 = new UnitOfWork(); }
问题在于这些代码片段。控制器依赖于服务的实例化,而服务依赖于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