ASP.NET Core 依赖注入基本用法

ASP.NET Core 依赖注入

ASP.NET Core从框架层对依赖注入提供支持。也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式。本文将介绍依赖注入的基本概念,并结合代码演示如何在 ASP.NET Core中使用依赖注入。

什么是依赖注入?

百度百科对于依赖注入的介绍:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

从百科的介绍中可以看出,依赖注入和控制反转是一回事,依赖注入是一种新的设计模式,通过正确使用依赖注入的相关技术,可以降低系统耦合度,增加系统的可扩展性。

我们来看一个例子:

public interface IInterfaceA
{ }

public interface IInterfaceB
{ }

public class ClassA : IInterfaceA
{
    private IInterfaceB B { get; set; }

    public ClassA(IInterfaceB b)
    {
        this.B = b;
    }
}

public class ClassB : IInterfaceB
{ }

这个时候,如果我们想要获取IInterfaceA的实例,如果不采用依赖注入,我们的做法通常是这样的:

IInterfaceB b = new ClassB();
IInterfaceA a = new ClassA(b);

这个时候IInterfaceA的控制权,在实例化的时候就已经被限定死了,没有任何想象空间,就是ClassA的实例,并且我们还要手工的初始化IInterfaceB,同样B的控制权也被限定死了。这样的代码毫无设计、也极不利于扩展。

如果采用依赖注入,我们看一下代码:

var a = container.GetService<IInterfaceA>();

这个时候接口A和B的控制权是由容器来控制的,我们可以通过向容器中注入不同的接口实现来扩展系统的灵活性,由于将控制权交给了IoC容器,我们还可以通过配置的方式灵活的控制对象的生命周期,这一点也是手工创建对象无法实现的。

控制反转的关系图如下(图片来源于官网):

ASP.NET Core中的依赖注入

上面介绍了依赖注入的基本概念,那么在 ASP.NET Core中,我们该如何使用依赖注入呢?在 ASP.NET Core中已经内置了一套依赖注入的容器,我们可以直接使用。

在Startup.ConfigureServices中添加我们要注册的服务和实现,添加的时候可以对服务的生命周期进行相应的配置,然后就可以在PageModel、Controller、Views等需要的地方使用了。

下面的示例将演示如何注册服务,代码来源于官网。首先要定义一个服务接口,并实现这个接口:

public interface IMyDependency
{
    Task WriteMessage(string message);
}

public class MyDependency : IMyDependency
{
    private readonly ILogger<MyDependency> _logger;

    public MyDependency(ILogger<MyDependency> logger)
    {
        _logger = logger;
    }

    public Task WriteMessage(string message)
    {
        _logger.LogInformation(
            "MyDependency.WriteMessage called. Message: {MESSAGE}",
            message);

        return Task.FromResult(0);
    }
}

然后我们进行服务注册:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IMyDependency, MyDependency>();
    services.AddMvc();
}

这里我们添加了IMyDependency的注册,同时也添加了使用Mvc所需要的服务的注册。这里有两个问题需要说明:

  • AddScoped是添加一个服务注册,Scoped是该服务的生命周期,表示按照作用于创建该服务,如果作用域中多次使用到该服务,则只创建一个对象。比如每一个HTTP请求都是一个作用域,那么在这个请求处理过程中,容器只会创建一个对象。与Scoped对应的还有其它的生命周期,我们将服务的生命周期列举如下:

    • Transient:瞬时服务,表示每次使用都会创建新的对象
    • Scoped:作用域服务,表示每次请求只创建一个对象。这里需要特殊说明一下,如果你的服务是一个中间件,不受此约束,因为中间件都是强制单例的。如果要在中间件中使用Scoped服务,则需要将服务注入到Invoke或InvokeAsync方法的参数中,此处可以参考 ASP.NET Core 中间件基本用法
    • Singleton:单例服务,表示每个应用程序域只会创建一个实力。
  • 基于约定,ASP.NET Core推荐我们采用类似于Add{SERVICE_NAME}的方式添加服务的注册,比如services.AddMvc(),这种方式可以通过扩展方法来实现,代码如下:
namespace Microsoft.Extensions.DependencyInjection
{
    public static partial class MyDependencyExtensions
    {
        public static IServiceCollection AddMyDependency(this IServiceCollection services)
        {
            return services.AddScoped<IMyDependency, MyDependency>();
        }
    }
}

使用依赖注入

在了解了依赖注入的基本用法之后,我们现在来了解一下如何将服务注入到Controller、Views中。

在控制器中注入服务

最常规的用法是采用构造函数注入的方式,将一个服务注入到控制器中,代码如下:

public class DefaultController : Controller
{
    private readonly ILogger<DefaultController> logger;

    public DefaultController(ILogger<DefaultController> logger)
    {
        this.logger = logger;
    }
}

构造函数注入是最常用的注入方式,这种方式要求依赖者提供公有的构造函数,并将依赖项通过构造函数的方式传入依赖者,完成对依赖项的赋值。

除此之外,还可以通过参数注入的方式,将依赖项注入到Action中,这里使用到FromServices特性:

public IActionResult Index([FromServices]ILogger<DefaultController> logger)
{
    throw new NotImplementedException();
}

ASP.NET Core 提供了这种支持,但是作者并不推荐这种操作

在视图中注入服务

ASP.NET Core 支持将依赖关系注入到视图,代码如下:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
   string myValue = Configuration["root:parent:child"];
   ...
}

上面的代码演示了将IConfiguration服务注入到视图中,从而实现在视图中读取配置的功能。

有时候将服务注入到视图中会很有用(例如本地化),但是作者也并不是很推荐这种做法,因为这样做容易造成视图和控制器的边界不清晰。

在PageModel中注入服务

在PageModel中注入服务的方式,与在Controller中注入服务的方式相似:

public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(IMyDependency myDependency)
    {
        _myDependency = myDependency;
    }
}

在main方法中获取服务

public static void Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var serviceScope = host.Services.CreateScope())
    {
        var services = serviceScope.ServiceProvider;

        try
        {
            var serviceContext = services.GetRequiredService<MyScopedService>();
            // Use the context here
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred.");
        }
    }

    host.Run();
}

在HttpContext.RequestServices中获取服务

这种方式不利于测试,不推荐此种用法。

虽然优先推荐通过构造函数的方式注入来获取服务,但是很难避免有些时候需要手工获取服务,在使用手工获取服务的时候,我们应当从HttpContext.RequestServices中获取。

使用第三方依赖注入框架

ASP.NET Core内置的依赖注入框架功能有限,当我们想使用第三方框架的特性时,我们可以替换默认依赖注入框架。

ASP.NET Core内置的依赖注入框架未包含的特性:

  • 属性注入
  • 基于名称的注入
  • 子容器
  • 自定义生命周期管理
  • 对lazy对象初始化的Func支持

如果要是用这些功能,我们可以使用第三方框架。本文采用官方文档中的Autofac框架。

  • 首先添加 Autofac、Autofac.Extensions.DependencyInjection 的引用
  • 在Startup.ConfigureServices中配置容器,并返回IServiceProvider。在使用第三方容器时,必须返回IServiceProvider。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    // Add other framework services

    // Add Autofac
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule<DefaultModule>();
    containerBuilder.Populate(services);
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}
  • 配置Autofac的Module,用来注册服务等
public class DefaultModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
    }
}

参考资料

原文地址:https://www.cnblogs.com/youring2/p/10926590.html

时间: 2024-11-05 23:32:42

ASP.NET Core 依赖注入基本用法的相关文章

ASP.NET Core 依赖注入最佳实践——提示与技巧

在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇文章假设你已经基本熟悉依赖注入和ASP.NET Core.如果不是,则先阅读文章: 在ASP.NET Core中使用依赖注入 基础 构造函数注入 构造函数注入常用于在服务构建上定义和获取服务依赖.例如: 1 public class ProductService 2 { 3 private read

asp.net core 依赖注入

依赖注入入门 全面理解 ASP.NET Core 依赖注入 参考https://www.cnblogs.com/tcjiaan/p/8732848.html 如何在StartUp中的ConfigureServices方法里直接调用刚刚添加好的注册? // redis注入 services.AddSingleton<IRedisConnection>(k => { return new RedisConnection(6, Configuration["RedisConnecti

ASP.NET Core依赖注入——依赖注入最佳实践

在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用中使用依赖注入的一些经验和建议,并且将会讨论这些原则背后的动机是什么: (1)有效地设计服务及其依赖关系. (2)防止多线程问题. (3)防止内存泄漏. (4)防止潜在的错误. 在讨论该话题之前,了解什么是服务是生命周期至关重要,当组件通过依赖注入请求另一个组件时,它接收的实例是否对该组件实例是唯一

ASP.NET Core 依赖注入(DI)

原文:ASP.NET Core 依赖注入(DI) ASP.NET Core的底层设计支持和使用依赖注入.ASP.NET Core 应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入.由ASP.NET Core 提供的默认服务容器提供了最小功能集,并不是取代其他容器. 1.浅谈依赖注入 依赖注入(Dependency injection,DI)是一种实现对象和依赖者之间松耦合的技术,将类用来执行其操作的这些对象以注入的方式提供给该类,而不是直接实例化依赖项或者

了解ASP.NET Core 依赖注入,看这篇就够了

DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET  Core的DI实现以及对实例生命周期的管理(这个是经常面试会问到的问题).最后再给大家简单介绍一下在控制台以及Mvc下如何使用DI,以及如何把默认的Service Container 替换成Autofac. 一.什么是依赖注入 1.1 依赖 1.2 什么注入 为什么反转 何为容器 二..NET Core DI 2.1 实例的注

【转】ASP.NET Core 依赖注入

DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET  Core的DI实现以及对实例生命周期的管理(这个是经常面试会问到的问题).最后再给大家简单介绍一下在控制台以及Mvc下如何使用DI,以及如何把默认的Service Container 替换成Autofac. 一.什么是依赖注入 1.1 依赖 1.2 什么注入 为什么反转 何为容器 二..NET Core DI 2.1 实例的注

全面理解 ASP.NET Core 依赖注入

DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET  Core的DI实现以及对实例生命周期的管理(这个是经常面试会问到的问题).最后再给大家简单介绍一下在控制台以及Mvc下如何使用DI,以及如何把默认的Service Container 替换成Autofac. 我录了一些关于ASP.NET Core的入门视频:有兴趣的同学可以去看看.  http://www.cnblogs.co

asp.net core 依赖注入问题

最近.net core可以跨平台了,这是一个伟大的事情,为了可以赶上两年以后的跨平台部署大潮,我也加入到了学习之列.今天研究的是依赖注入,但是我发现一个问题,困扰我很久,现在我贴出来,希望可以有人帮忙解决或回复一下. 背景:我测试.net自带的依赖注入生命周期,一共三个:Transient.Scope.Single三种,通过一个GUID在界面展示,但是我发现scope和single的每次都是相同的,并且single实例的guid值每次都会改变. 通过截图可以看到scope和Single每次浏览器

asp.net core 依赖注入实现全过程粗略剖析(2)

接着 上篇 目前也算是交代清楚了相关的类.那么框架具体是如何来实例化的呢?整个的流程是怎么样的. 我们参考源码中的Test文件夹来看看: var collection = new ServiceCollection(); collection.AddTransient<DependOnNonexistentService>(); var provider = CreateServiceProvider(collection); protected override IServiceProvid