在asp.net mvc4控制器中使用Autofac来解析依赖

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

在asp.net mvc4控制器中使用Autofac来解析依赖

我们的代码中打破(抽离)依赖无非是为了增强可维护性、可测试性和灵活性。在前面的帖子我在MVC应用程序创建了一些严重的依赖性。HomeController实例化一个存储库。这个存储库调用外部系统(来获取)数据。其情形是位于Windows Azure的存储表。过于依赖于外部系统,单元测试将变得冗长繁琐(甚至不可靠)。Jeremy Miller 关于一个好的单元测试的质量写了一篇不错的博客中,他说:

但是在我们能做到这一点之前,我们首先需要重构出所有这些依赖项。为此,我们在这些依赖对象之间创建了一些小接口。然后我们配置一个IoC容器,负责注入适当的依赖关系以便于在需要的时候用到。在这个示例中,我们使用Autofac,但它也可以是任何其他的.NET的loc容器。

在构造函数中明确声明依赖

在这个例子中,我们有一个简单的MVC 4应用程序用来列举出奥运金牌的得主。这些都从一个SQL数据库用Entity Framework检索到的数据。数据检索过程封装在一个存储库中。但是在HomeController中通过新建一个GoldMedalWinnersRepository实例, 我们已经创建了一个永久的隐藏的依赖。

using System.Web.Mvc;
using TestableMvcApp.DataContext;
using TestableMvcApp.Models;
using TestableMvcApp.Repositories;
 
namespace TestableMvcApp.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index(GoldMedalWinnersModel model)
        {
            ViewBag.Message = "Gold Medal Winners";
 
            var repository = new GoldMedalWinnersRepository(
                new GoldMedalWinnersContext());
 
            model.Winners = repository.GetAll();
 
            return View(model);
        }
    }
}

测试该控制器迫使我们使用/测试存储库逻辑。让我们先解决这个依赖。为了打破与外部类之间的依赖,可以让构造函数应该要求这些依赖项(作为参数传入)。所以我们可以从HomeController中移除存储库的实例化,而改用构造函数传入参数:

using System.Web.Mvc;
using TestableMvcApp.Models;
using TestableMvcApp.Repositories;
 
namespace TestableMvcApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly GoldMedalWinnersRepository _repository;
 
        public HomeController(GoldMedalWinnersRepository repository)
        {
            _repository = repository;
        }
 
        public ActionResult Index(GoldMedalWinnersModel model)
        {
            ViewBag.Message = "Gold Medal Winners";
 
            model.Winners = _repository.GetAll();
 
            return View(model);
        }
    }
}
这是往正确方向的一小步。

调用抽象(接口/抽象类)

好的,现在构造函数明确阐述了它的所有依赖项。但是如果我们测试控制器我们仍然需要处理存储库调用数据库的(逻辑)。为了使控制器的测试独立于存储库(逻辑),我们需要传递一个虚拟存储库版本。这种方式我们完全控制对象的内部运作,而测试真正的控制器。要做到这一点,我们需要创建一个存储库的抽象;下面是接口:

using System.Collections.Generic;
using TestableMvcApp.Pocos;
 
namespace TestableMvcApp.Repositories
{
    public interface IGoldMedalWinnersRepository
    {
        GoldMedalWinner GetById(int id);
        IEnumerable<GoldMedalWinner> GetAll();
        void Add(GoldMedalWinner goldMedalWinner);
    }
}
让我们改变控制器的构造函数要求传入一个抽象IGoldMedalWinnersRepository接口而不是实际GoldMedalWinnersRepository对象。
 
using System.Web.Mvc;
using TestableMvcApp.Models;
using TestableMvcApp.Repositories;
 
namespace TestableMvcApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly IGoldMedalWinnersRepository _repository;
 
        public HomeController(IGoldMedalWinnersRepository repository)
        {
            _repository = repository;
        }
 
        public ActionResult Index(GoldMedalWinnersModel model)
        {
            ViewBag.Message = "Gold Medal Winners";
 
            model.Winners = _repository.GetAll();
 
            return View(model);
        }
    }
}
 
稍后我们将看到,存储库的构造函数也有一个依赖项。它需要一个Entity Framework DbContext来正常运作。所以我们需要为此创建一个抽象(接口);IGoldMedalWinnersContext:
using System.Data.Entity;
using TestableMvcApp.Pocos;
 
namespace TestableMvcApp.DataContext
{
    public interface IGoldMedalWinnersContext
    {
        DbSet<GoldMedalWinner> GoldMedalWinners { get; set; }
        DbSet<Country> Countries { get; set; }
        int SaveChanges();
    }
}
在让我们的存储库(类)的构造函数要求一个这个接口的实现(作为传入阐述),这样我们就成功的解耦了。
using System.Collections.Generic;
using System.Linq;
using TestableMvcApp.DataContext;
using TestableMvcApp.Pocos;
 
namespace TestableMvcApp.Repositories
{
    public class GoldMedalWinnersRepository : IGoldMedalWinnersRepository
    {
        private readonly IGoldMedalWinnersContext _goldContext;
 
        public GoldMedalWinnersRepository(IGoldMedalWinnersContext goldContext)
        {
            _goldContext = goldContext;
        }
 
        #region IGoldMedalWinnersRepository Members
 
        public GoldMedalWinner GetById(int id)
        {
            return _goldContext.GoldMedalWinners.Find(id);
        }
 
        public IEnumerable<GoldMedalWinner> GetAll()
        {
            return _goldContext.GoldMedalWinners.ToList();
        }
 
        public void Add(GoldMedalWinner goldMedalWinner)
        {
            _goldContext.GoldMedalWinners.Add(goldMedalWinner);
            _goldContext.SaveChanges();
        }
 
        #endregion
    }
这时我们可以传入实现了接口的任何存储库类。在我们的单元测试中,很容易伪造,复制或模拟存储库和dbcontext(来做一个伪库以便于排除数据库的因素)。

使用IoC容易来解析依赖

这是很好的,但是我们在哪里实例化存储库?为此,我们可以使用一个IoC容器。这个神奇的东西可以通过配置,在必要时,可为控制器提供合适的存储库库时。在这个示例中,我们使用Autofac。我们可以很容易的从Nuget安装 Autofac ASP.NET MVC3 Integration package。它还适用于MVC4并负责安装所需的所有的我们的MVC应用程序需要的核心依赖。
 
一旦安装,我们可以根据需要配置Autofac来解析所有依赖项。为了演示,我们在MVC项目的App_start文件夹下创建一个配置类。它有一个静态的名为RegisterDependencies Autofac的方法,在这里我们可以启用Autofac。
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using TestableMvcApp.DataContext;
using TestableMvcApp.Repositories;
 
namespace TestableMvcApp.App_Start
{
    public class IocConfig
    {
        public static void RegisterDependencies()
        {
            var builder = new ContainerBuilder();
            builder.RegisterControllers(typeof (MvcApplication).Assembly);
 
            builder.RegisterType<GoldMedalWinnersRepository>()
                .As<IGoldMedalWinnersRepository>()
                .InstancePerHttpRequest();
 
            builder.RegisterType<GoldMedalWinnersContext>()
                .As<IGoldMedalWinnersContext>()
                .InstancePerHttpRequest();
 
            IContainer container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        }
    }
}
我们新建一个Autofac ContainerBuilder,然后调用RegisterControllers来(注册整个) MVC程序集。这种方法用AutofacDependencyResolver注册所有在应用程序的控制器。每当MVC要求访问控制器时,这个解析器就返回适当的控制器。
 
然后我们调用RegisterType注册 GoldMedalWinnersRepository。在这里我们配置成:每当我们需要一个IGoldMedalWinnersRepository, Autofac必须返回一个GoldMedalWinnersRepository的实例。我们将生命周期设置为了InstancePerHttpRequest。我们同样的方式注册了GoldMedalWinnersContext。我们构建的容器,并为我们的容器的AutofacDependencyResolver设置了解析器MVC DependencyResolver 。
 
在MVC应用程序中的全局的.asax文件中,在所有其他MVC注册之前,我们调用了IoC配置类的RegisterDependencies方法,这样就可以了(我们准备好了)。
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using TestableMvcApp.App_Start;
 
namespace TestableMvcApp
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            IocConfig.RegisterDependencies();
 
            AreaRegistration.RegisterAllAreas();
 
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}
这就是全部(目前为止)。当我们启动应用程序,Autofac会做一个事情然后为控制器提供适当的实例化了带有适当的dbcontext的存储库。如果我们编写单元测试,然而,Autofac不会为我们的控制器提供存储库,因此我们可以注入一些假的,副本或仿制版本。这正是我们需要的。
相关文章
|
2月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
138 3
|
2月前
|
测试技术 API 开发者
精通.NET单元测试:MSTest、xUnit、NUnit全面解析
【10月更文挑战第15天】本文介绍了.NET生态系统中最流行的三种单元测试框架:MSTest、xUnit和NUnit。通过示例代码展示了每种框架的基本用法和特点,帮助开发者根据项目需求和个人偏好选择合适的测试工具。
41 3
|
2月前
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:实现过程与关键细节解析an3.021-6232.com
随着互联网技术的快速发展,ASP.NET作为一种广泛使用的服务器端开发技术,其与数据库的交互操作成为了应用开发中的重要环节。本文将详细介绍在ASP.NET中如何连接SQL数据库,包括连接的基本概念、实现步骤、关键代码示例以及常见问题的解决方案。由于篇幅限制,本文不能保证达到完整的2000字,但会确保
|
3月前
|
监控 网络协议 API
.NET WebSocket 技术深入解析,你学会了吗?
【9月更文挑战第4天】WebSocket 作为一种全双工协议,凭借低延迟和高性能特点,成为实时应用的首选技术。.NET 框架提供了强大的 WebSocket 支持,使实时通信变得简单。本文介绍 WebSocket 的基本概念、.NET 中的使用方法及编程模型,并探讨其在实时聊天、监控、在线游戏和协同编辑等场景的应用,同时分享最佳实践,帮助开发者构建高效实时应用。
163 12
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
44 7
|
4月前
|
JSON 开发框架 JavaScript
【Azure Developer】使用.Net Core解析JSON的笔记
【Azure Developer】使用.Net Core解析JSON的笔记
|
3月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
71 0
|
4月前
|
C# Windows 开发者
超越选择焦虑:深入解析WinForms、WPF与UWP——谁才是打造顶级.NET桌面应用的终极利器?从开发效率到视觉享受,全面解读三大框架优劣,助你精准匹配项目需求,构建完美桌面应用生态系统
【8月更文挑战第31天】.NET框架为开发者提供了多种桌面应用开发选项,包括WinForms、WPF和UWP。WinForms简单易用,适合快速开发基本应用;WPF提供强大的UI设计工具和丰富的视觉体验,支持XAML,易于实现复杂布局;UWP专为Windows 10设计,支持多设备,充分利用现代硬件特性。本文通过示例代码详细介绍这三种框架的特点,帮助读者根据项目需求做出明智选择。以下是各框架的简单示例代码,便于理解其基本用法。
204 0
|
4月前
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
|
4月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
53 0

推荐镜像

更多