• RESTful日#3:在ASP中使用控制反转和依赖注入解决依赖关系的依赖关系。NET Web api与Unity容器和管理扩展框架(MEF)


    表的内容 表内容介绍路线图现有设计和问题管理可扩展性框架(MEF)创建一个依赖解析器与Unity和MEF设置业务服务设置数据模型设置REST端点/ WebAPI项目运行的应用优势本设计结论 介绍 在我的前两篇文章中,我解释了如何使用ASP创建RESTful服务。NET Web API与实体框架和解决依赖使用Unity容器。在本文中,我将解释如何使用控制反转使用Unity容器和MEF(托管可扩展性框架)创建松散耦合的系统。我不会解释太多理论,而是更多地关注实际的实现。对于本系列的读者来说,他们可以使用自己之前创建的现有解决方案。对于本文的新读者,我还提供了以前的源代码和当前源代码的下载链接。 对于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中进行单元测试和集成测试(第二部分)。净Web api。RESTful日#10:创建自托管的ASP。NET WebAPI与CRUD操作在Visual Studio 2010 我有意使用Visual Studio 2010和。net Framework 4.0,因为在。net Framework 4.0中很少有很难找到的实现,但我将通过演示如何实现来简化它。 现有设计及问题 我们已经有了一个现有的设计。如果你打开解决方案,你会看到下面提到的结构, 我们试图按照以下方式设计一个松散耦合的架构, DataModel(负责与数据库通信):只与服务层通信。服务(充当REST端点和数据访问之间的业务逻辑层):在REST端点和数据模型之间进行通信。REST API,即控制器:只通过公开的接口与服务对话。 但是当我们试图从服务中解决UnitOfWork的依赖时,我们不得不在我们的WebAPI项目中引用DataModel dll;这违反了我们的系统,如下图所示, 在本文中,我们将尝试从现有解决方案中解析依赖项(服务)的依赖项(数据模型)。我的控制器依赖于服务,我的服务依赖于数据模型。现在,我们将设计一个体系结构,其中的组件将在对象创建和实例化方面相互独立。为了实现这一点,我们将使用MEF(Managed Extensibility Framework)以及Unity容器和反射。 图片来源:http://o5.com/wp-content/uploads/2010/08/dreamstime_15583711-550x371.jpg 理想情况下,我们不应该在Bootstrapper类中包含以下代码, 隐藏,复制Code

    container.RegisterType<IProductServices, ProductServices>().RegisterType<UnitOfWork>(new HierarchicalLifetimeManager());

    MEF (MEF) 你可以从msdn链接了解Unity。我只是引用了msdn中的几行, MEF是一个用于创建轻量级、可扩展应用程序的库。它允许应用程序开发人员在不需要配置的情况下发现和使用扩展。它还允许扩展开发人员轻松封装代码,避免脆弱的硬依赖关系。MEF不仅允许在应用程序内重用扩展,而且还允许跨应用程序重用扩展。” MEF是。net Framework 4不可分割的一部分,在任何使用。net Framework的地方都可以使用。你可以在你的客户端应用程序中使用MEF,不管他们使用的是Windows窗体、WPF或任何其他技术,或者在使用ASP.NET的服务器应用程序中。” 使用Unity和MEF创建依赖解析器 打开你的Visual studio,我使用的是VS 2010,你可以使用VS 2010或以上版本。加载解决方案。 步骤1:右键单击解决方案资源管理器,并添加一个名为Resolver的新项目, 我故意选择了这个名字,你们已经知道为什么是J 步骤2:右键单击解析器项目点击ManageNugetPackage,在添加新包的界面,搜索Unity。MVC3在网上图书馆, 将包安装到您的解决方案中。 步骤3:右键单击解析器项目并添加对System.ComponentModel.Composition的引用。 您可以在GAC中找到dll。我使用的是框架4.0,所以引用的是相同版本的dll。 这个DLL是MEF的一部分,并且已经与。net Framework 4.0一起安装在系统GAC中。这个DLL提供了MEF的核心类。 步骤4:在解析器项目中添加一个名为IComponent的接口,该接口包含名为Setup的初始化方法。我们将尝试在Resolver类中实现这个接口,这个类将在其他项目(如DataModel、Services和WebApI)中创建。 隐藏,复制Code

    namespace Resolver
    {
        /// <summary>
        /// Register underlying types with unity.
        /// </summary>
        public interface IComponent
        {
          
        }
    }

    步骤5:在声明Setup方法之前,只需再添加一个接口,该接口负责充当注册类型的契约。我将这个接口命名为IRegisterComponent, 隐藏,复制Code

    namespace Resolver
    {
        /// <summary>
        /// Responsible for registering types in unity configuration by implementing IComponent
        /// </summary>
        public interface IRegisterComponent
        {
            /// <summary>
            /// Register type method
            /// </summary>
            /// <typeparamname="TFrom"></typeparam>
            /// <typeparamname="TTo"></typeparam>
            /// <paramname="withInterception"></param>
            void RegisterType<TFrom, TTo>(bool withInterception = false) where TTo : TFrom;
     
            /// <summary>
            /// Register type with container controlled life time manager
            /// </summary>
            /// <typeparamname="TFrom"></typeparam>
            /// <typeparamname="TTo"></typeparam>
            /// <paramname="withInterception"></param>
            void RegisterTypeWithControlledLifeTime<TFrom, TTo>(bool withInterception = false) where TTo : TFrom;
        }
    }

    在这个接口中,我声明了两个方法,一个是RegisterType,另一个是控制对象生命周期的In to RegisterType,也就是说,对象的生命周期在方式上是分层的。这有点像我们在Unity中所做的。 步骤6:现在在之前创建的IComponent接口上声明设置方法,该接口以IRegisterComponent的实例作为参数, 隐藏,复制Code

    void SetUp(IRegisterComponent registerComponent);

    所以我们的交互界面变成, 隐藏,复制Code

    namespace Resolver
    {
        /// <summary>
        /// Register underlying types with unity.
        /// </summary>
        public interface IComponent
        {
            void SetUp(IRegisterComponent registerComponent);
        }
    }

    第6步:现在我们将编写一个包装器,或者你可以说一个包装器在MEF和Unity上注册类型/组件。这是核心的MEF实现。创建一个名为ComponentLoader的类,并向其中添加以下代码, 隐藏,收缩,复制Code

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Practices.Unity;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Reflection;
     
    namespace Resolver
    {
        public static class ComponentLoader
        {
            public static void LoadContainer(IUnityContainer container, string path, string pattern)
            {
                var dirCat = new DirectoryCatalog(path, pattern);
                var importDef = BuildImportDefinition();
                try
                {
                    using (var aggregateCatalog = new AggregateCatalog())
                    {
                        aggregateCatalog.Catalogs.Add(dirCat);
     
                        using (var componsitionContainer = new CompositionContainer(aggregateCatalog))
                        {
                            IEnumerable<Export> exports = componsitionContainer.GetExports(importDef);
     
                            IEnumerable<IComponent> modules =
                                exports.Select(export => export.Value as IComponent).Where(m => m != null);
     
                            var registerComponent = new RegisterComponent(container);
                            foreach (IComponent module in modules)
                            {
                                module.SetUp(registerComponent);
                            }
                        }
                    }
                }
                catch (ReflectionTypeLoadException typeLoadException)
                {
                    var builder = new StringBuilder();
                    foreach (Exception loaderException in typeLoadException.LoaderExceptions)
                    {
                        builder.AppendFormat("{0}
    ", loaderException.Message);
                    }
     
                    throw new TypeLoadException(builder.ToString(), typeLoadException);
                }
            }
     
            private static ImportDefinition BuildImportDefinition()
            {
                return new ImportDefinition(
                    def => true, typeof(IComponent).FullName, ImportCardinality.ZeroOrMore, false, false);
            }
        }
     
        internal class RegisterComponent : IRegisterComponent
        {
            private readonly IUnityContainer _container;
     
            public RegisterComponent(IUnityContainer container)
            {
                this._container = container;
                //Register interception behaviour if any
            }
     
            public void RegisterType<TFrom, TTo>(bool withInterception = false) where TTo : TFrom
            {
                if (withInterception)
                {
                    //register with interception
                }
                else
                {
                    this._container.RegisterType<TFrom, TTo>();
                }
            }
     
            public void RegisterTypeWithControlledLifeTime<TFrom, TTo>(bool withInterception = false) where TTo : TFrom
            {
                this._container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
            }
        }
    }

    步骤7:现在解析器包装器准备好了。构建项目并将其引用添加到DataModel、BusinessServices和WebApi项目,如下所示, 设置业务服务 我们已经在BusinessServices项目中添加了解析器的引用。我们同意在我们的每个项目中实现一个集成接口。 因此创建一个名为DependencyResolver的类并在其中实现IComponent接口,我们也使用反射来导入IComponent类型。因此,添加一个类并向DependencyResolver类添加以下代码, 隐藏,复制Code

    using System.ComponentModel.Composition;
    using DataModel;
    using DataModel.UnitOfWork;
    using Resolver;
     
    namespace BusinessServices
    {
        [Export(typeof(IComponent))]
        public class DependencyResolver : IComponent
        {
            public void SetUp(IRegisterComponent registerComponent)
            {
                registerComponent.RegisterType<IProductServices, ProductServices>();
     
            }
        }
    }

    注意,我们已经实现了SetUp方法,并且在相同的方法中为我的ProductService注册了类型。 所有现有的代码库保持不变。我们不需要触及IProductServices接口或ProductServices类。 设置模型 我们还向DataModel项目添加了解析器项目引用。因此,我们将尝试在这个项目中注册UnitOfWork的类型。我们以同样的方式继续,只需要添加一个DependencyResolver类并实现它的Setup方法来注册UnitOfWork类型。为了使代码更具可读性和标准性,我做了一些更改。我刚刚为UnitOfWork添加了一个接口并将其命名为IUnitOfWork。现在我的UnitOfWork类就是由此派生的,您可以在我们在前两篇文章中讨论的项目的早期版本中做这个练习。 所以我的IUnitOfWork包含了在UnitOfWork中声明的一个公共方法, 隐藏,复制Code

    namespace DataModel.UnitOfWork
    {
        public interface IUnitOfWork
        {
            /// <summary>
            /// Save method.
            /// </summary>
            void Save();
        }
    }

    现在在DepenencyResolver类中注册UnitOfWork的类型,我们的类如下所示, 隐藏,复制Code

    using System.ComponentModel.Composition;
    using System.Data.Entity;
    using DataModel.UnitOfWork;
    using Resolver;
     
    namespace DataModel
    {
        [Export(typeof(IComponent))]
        public class DependencyResolver : IComponent
        {
            public void SetUp(IRegisterComponent registerComponent)
            {
                registerComponent.RegisterType<IUnitOfWork,UnitOfWork.UnitOfWork>();
            }
        }
    }

    同样,不需要修改这个项目的任何现有代码。 设置REST端点/ WebAPI项目 我们90%的工作都完成了。 我们现在需要设置WebAPI项目。我们不会在这个项目中添加任何DependencyResolver类。我们会倒转Bootstrapper类中已经有的层调用机制,所以当你打开Bootstrapper类时,你会得到这样的代码, 隐藏,收缩,复制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;
            }
        }
    }

    现在,我们需要稍微更改代码基,使我们的系统松散耦合。只需从WebAPI项目中删除DataModel的引用。 我们不希望我们的数据模型暴露给WebAPI项目,这是我们的目标,所以我们现在减少了对数据模型项目的依赖。 将以下Bootstrapper类的代码添加到现有的Bootstarpper类中, 隐藏,收缩,复制Code

    using System.Web.Http;
    //using DataModel.UnitOfWork;
    using Microsoft.Practices.Unity;
    using Resolver;
    using Unity.Mvc3;
     
    namespace WebApi
    {
        public static class Bootstrapper
        {
            public static void  Initialise()
            {
                var container = BuildUnityContainer();
     
                System.Web.Mvc.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());
     
                RegisterTypes(container);
     
                return container;
            }
     
            public static void RegisterTypes(IUnityContainer container)
            {
     
                //Component initialization via MEF
                ComponentLoader.LoadContainer(container, ".\bin", "WebApi.dll");
                ComponentLoader.LoadContainer(container, ".\bin", "BusinessServices.dll");
     
            }
        }
    }

    这是一种重新定义Bootstrapper类而不触及我们现有的控制器方法的方法。我们现在甚至不需要为ProductServices注册类型,我们已经在BusinessServices项目中完成了这一点。 注意,在RegisterTypes方法中,我们使用ComponentLoader通过反射加载组件/dll。我们编写了两行代码,第一行用于加载WebAPI.dll,另一行用于加载Business Services.dll。 将BusinessServicess.dll的名称改为WebAPI.Services。那么我们只需要编写一行代码来加载WebAPI和BusinessService dll,如下所示, 隐藏,复制Code

    ComponentLoader.LoadContainer(container, ".\bin", "WebApi*.dll");

    是的,我们可以使用正则表达式。 运行应用程序 运行这个应用程序,我们得到, 我们已经添加了测试客户端,但是对于新的读者,只需去管理Nuget包,右击WebAPI项目,在搜索框中输入WebAPITestClient, 您将获得“一个用于ASP的简单测试客户端”。NET Web API”,只要添加它。你将在以下区域得到一个帮助控制器->如下图所示, 我在上一篇文章中已经提供了数据库脚本和数据,您可以使用相同的脚本和数据。 在应用程序url中添加“/help”,您将获得测试客户端, 您可以通过单击每个服务来测试它。单击服务链接后,您将被重定向到测试该特定服务的服务页面。在这个页面的右下角有一个按钮测试API,只要按下那个按钮就可以测试你的服务, GetAllProduct服务, 为了创造新产品, 在数据库中,我们有新产品, 更新产品: 我们进入数据库, 删除产品: 在数据库: 这种设计的优点 在我之前的文章中,我更多地关注于设计缺陷,但是我们目前的设计几乎没有额外的优势, 我们得到了一个可扩展的、松散耦合的应用程序设计,可以用同样的方式添加更多的新组件。通过反射自动注册类型。假设我们想注册任何接口实现REST端点,我们只需要加载dll在我们启动加载器类,或者如果dll是常见的后缀名称然后我们只需要地方dll bin文件夹中,在运行时,将自动加载。 图片来源:http://a.pragprog.com/magazines/2013-07/images/iStock_000021011143XSmall__1mfk9b__.jpg 数据库事务或任何这样的模块现在都不公开给服务端点,这使得我们的服务更安全,也维护了设计结构。 结论 我们现在也知道了如何使用Unity容器来解决依赖关系,以及如何使用MEF来执行控制反转。在下一篇文章中,我将尝试解释如何在我的WebAPI中以真正的REST方式为我们的REST服务打开多个端点并创建自定义url。你也可以从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/news407.html

  • 相关阅读:
    Mysql 免密码登录,修改密码及忘记密码操作
    CentOS 6.9 升级MySQL 5.6.36到5.7.18
    【转载】详解布隆过滤器的原理、使用场景和注意事项
    AssemblyVersion、AssemblyFileVersion、AssemblyInformationalVersion 区别
    为什么要有财务自由【转】
    CacheManager.Core
    雪花算法,生成分布式唯一ID
    监控
    什么是并行、并发, 两者的区别是什么
    Emit用法
  • 原文地址:https://www.cnblogs.com/Dincat/p/13447290.html
Copyright © 2020-2023  润新知