zoukankan      html  css  js  c++  java
  • 依赖注入和控制器

    依赖注入和控制器

    原文: Dependency Injection and Controllers
    作者: Steve Smith
    翻译: 刘浩杨
    校对: 孟帅洋(书缘)

    ASP.NET Core MVC 控制器应通过它们的构造器明确的请求它们的依赖关系。在某些情况下,单个控制器的操作可能需要一个服务,在控制器级别上的请求可能没有意义。在这种情况下,你也可以选择将服务作为 action 方法的参数。

    章节:

    查看或下载示例代码

     

    依赖注入

    依赖注入(Dependency injection,DI)是一种如 Dependency Inversion Principle 所示的技术,允许应用程序由松散耦合的模块组成。ASP.NET Core 内置了 dependency injection,这使得应用程序更容易测试和维护。

     

    构造器注入

    ASP.NET Core 内置的基于构造器的依赖注入支持扩展到 MVC 控制器。通过只添加一个服务类型作为构造器参数到你的控制器中,ASP.NET Core 将会尝试使用内置的服务容器解析这个类型。服务通常是,但不总是使用接口来定义。例如,如果你的应用程序存在取决于当前时间的业务逻辑,你可以注入一个检索时间的服务(而不是对它硬编码),这将允许你的测试通过一个使用设置时间的实现。

    复制代码
    using System;
    
    namespace ControllerDI.Interfaces
    {
        public interface IDateTime
        {
            DateTime Now { get; }
        }
    }
     

    实现这样一个接口,它在运行时使用的系统时钟是微不足道的:

    复制代码
    using System;
    using ControllerDI.Interfaces;
    
    namespace ControllerDI.Services
    {
        public class SystemDateTime : IDateTime
        {
            public DateTime Now
            {
                get { return DateTime.Now; }
            }
        }
    }
     

    有了这些代码,我们可以在我们的控制器中使用这个服务。在这个例子中,我们在 HomeController 的 Index 方法中加入一些根据一天中的时间向用户显示问候的逻辑。

    复制代码
    using ControllerDI.Interfaces;
    using Microsoft.AspNetCore.Mvc;
    
    namespace ControllerDI.Controllers
    {
        public class HomeController : Controller
        {
            private readonly IDateTime _dateTime;   //手动高亮
    
            public HomeController(IDateTime dateTime)   //手动高亮
            {
                _dateTime = dateTime;   //手动高亮
            }
    
            public IActionResult Index()
            {
                var serverTime = _dateTime.Now; //手动高亮
                if (serverTime.Hour < 12)       //手动高亮
                {
                    ViewData["Message"] = "It's morning here - Good Morning!";  //手动高亮
                }
                else if (serverTime.Hour < 17)  //手动高亮
                {
                    ViewData["Message"] = "It's afternoon here - Good Afternoon!";  //手动高亮
                }
                else    //手动高亮
                {
                    ViewData["Message"] = "It's evening here - Good Evening!";  //手动高亮
                }
                return View();  //手动高亮
            }
        }
    }
     

    如果我们现在运行应用程序,我们将可能遇到一个异常:

    复制代码
    An unhandled exception occurred while processing the request.
    
    InvalidOperationException: Unable to resolve service for type 'ControllerDI.Interfaces.IDateTime' while attempting to activate 'ControllerDI.Controllers.HomeController'.
    Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
     

    这个错误发生时,我们没有在我们的 Startup 类的 ConfigureServices 方法中配置服务。要指定请求 IDateTime 时使用 SystemDateTime 的实例来解析,添加下面的清单中高亮的行到你的 ConfigureServices 方法中:

    复制代码
    public void ConfigureServices(IServiceCollection services)
    {
        // Add application services.
        services.AddTransient<IDateTime, SystemDateTime>();     //手动高亮
    }
     

    注意
    这个特殊的服务可以使用任何不同的生命周期选项来实现(TransientScoped或 Singleton)。参考 dependency injection 来了解每一个作用域选项将如何影响你的服务的行为。

    一旦服务被配置,运行应用程序并且导航到首页应该显示预期的基于时间的消息:

    提示
    参考 Testing Controller Logic 学习如何在控制器中显示请求依赖关系http://deviq.com/explicit-dependencies-principle 让代码更容易测试。

    ASP.NET Core 内置的依赖注入支持用于请求服务的类型只有一个构造器。如果你有多于一个构造器,你可能会得到一个异常描述:

    复制代码
    An unhandled exception occurred while processing the request.
    
    InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'ControllerDI.Controllers.HomeController'. There should only be one applicable constructor.
    Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& parameterMap)
     

    作为错误消息状态,你可以纠正只有一个构造器的问题。你也可以参考 replace the default dependency injection support with a third party implementation 支持多个构造器。

     

    Action 注入和 FromServices

    有时候在你的控制器中你不需要为超过一个 Action 使用的服务。在这种情况下,将服务作为 Action 方法的一个参数是有意义的。这是通过使用特性 [FromServices] 标记参数实现,如下所示:

    复制代码
    public IActionResult About([FromServices] IDateTime dateTime)   //手动高亮
    {
        ViewData["Message"] = "Currently on the server the time is " + dateTime.Now;
    
        return View();
    }
     
     

    从控制器访问设置

    在控制器中访问应用程序设置或配置设置是一个常见的模式。此访问应当使用在 configuration 所描述的访问模式。你通常不应该从你的控制器中使用依赖注入直接请求设置。更好的方式是请求 IOptions<T> 实例, T 是你需要的配置类型。

    要使用选项模式,你需要创建一个表示选项的类型,如:

    复制代码
    namespace ControllerDI.Model
    {
        public class SampleWebSettings
        {
            public string Title { get; set; }
            public int Updates { get; set; }
        }
    }
     

    然后你需要配置应用程序使用选项模型,在 ConfigureServices 中添加你的配置类到服务集合中:

    复制代码
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()    //手动高亮
            .SetBasePath(env.ContentRootPath)       //手动高亮
            .AddJsonFile("samplewebsettings.json"); //手动高亮
        Configuration = builder.Build();            //手动高亮
    }
    
    public IConfigurationRoot Configuration { get; set; }   //手动高亮
    
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        // Required to use the Options<T> pattern
        services.AddOptions();      //手动高亮
    
        // Add settings from configuration
        services.Configure<SampleWebSettings>(Configuration);   //手动高亮
    
        // Uncomment to add settings from code
        //services.Configure<SampleWebSettings>(settings =>
        //{
        //    settings.Updates = 17;
        //});
    
        services.AddMvc();
    
        // Add application services.
        services.AddTransient<IDateTime, SystemDateTime>();
    }
     

    注意
    在上面的清单中,我们配置应用程序程序从一个 JSON 格式的文件中读取设置。你也可以完全在代码中配置设置,像上面的代码中所显示的。参考 configuration 更多的配置选项。

    一旦你指定了一个强类型的配置对象(在这个例子中,SampleWebSettings),并且添加到服务集合中,你可以从任何控制器或 Action 方法中请求 IOptions<T> 的实例(在这个例子中, IOptions<SampleWebSettings>)。下面的代码演示了如何从控制器中请求设置。

    复制代码
        public class SettingsController : Controller
        {
            private readonly SampleWebSettings _settings;   //手动高亮
    
            public SettingsController(IOptions<SampleWebSettings> settingsOptions)  //手动高亮
            {
                _settings = settingsOptions.Value;  //手动高亮
            }
    
            public IActionResult Index()
            {
                ViewData["Title"] = _settings.Title;
                ViewData["Updates"] = _settings.Updates;
                return View();
            }
        }
     

    遵循选项模式允许设置和配置互相分离,确保控制器遵循 separation of concerns ,因为它不需要知道如何或者在哪里找到设置信息。由于没有 static cling 或在控制器中直接实例化设置类,这也使得控制器更容易单元测试 。

    返回目录

    由于水平有限,错漏之处在所难免,欢迎大家批评指正,不胜感激,我们将及时修正。
    dotNet Core Studying Group:436035237
     
    分类: ASP.NET CORE
  • 相关阅读:
    OutputCache 缓存key的创建 CreateOutputCachedItemKey
    Asp.net Web Api源码调试
    asp.net mvc源码分析DefaultModelBinder 自定义的普通数据类型的绑定和验证
    Asp.net web Api源码分析HttpParameterBinding
    Asp.net web Api源码分析HttpRequestMessage的创建
    asp.net mvc源码分析ActionResult篇 RazorView.RenderView
    Asp.Net MVC 项目预编译 View
    Asp.net Web.config文件读取路径你真的清楚吗?
    asp.net 动态创建TextBox控件 如何加载状态信息
    asp.net mvc源码分析BeginForm方法 和ClientValidationEnabled 属性
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5948688.html
Copyright © 2011-2022 走看看