iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > ASP.NET >ASP.NET Core依赖注入详解
  • 427
分享到

ASP.NET Core依赖注入详解

2024-04-02 19:04:59 427人浏览 安东尼
摘要

目录一、什么是依赖注入二、使用框架提供的服务三、注册服务四、生命周期五、请求服务六、设计你的依赖服务ASP.net core的底层设计支持和使用依赖注入。ASP.net core应用

ASP.net core的底层设计支持和使用依赖注入。ASP.net core应用程序可以利用内置的框架服务将它们注入到启动类的方法中,并且应用程序服务能够配置注入。由ASP.Net Core提供的默认服务容器提供了最小功能集,并不是要取代其它容器。

一、什么是依赖注入

依赖注入(Dependency injection,DI)是一种实现对象及其合作者或依赖项之间松散耦合的技术。将类用来执行其操作的这些对象以某种方式提供给该类,而不是直接实例化合作者或使用静态引用。通常,类会通过它们的构造函数声明其依赖关系,允许它们遵循显示依赖原则。这种方法被称为“构造函数注入”。

当类的设计使用DI思想时,它们的耦合更加松散,因为它们没有对它们的合作者直接硬编码的依赖。这遵循“依赖倒置原则(Dependency Inversion Principle)”,其中指出,“高层模块不应该依赖于低层模块;两者都应该依赖于抽象”。类要求在它们构造时向其提供抽象(通常是interfaces),而不是引用特定的实现。提取接口的依赖关系和提供这些接口的实现作为参数也是“策略设计模式”的一个示例。

当系统被设计使用DI,很多类通过它们的构造函数(或属性)请求其依赖关系,当一个类被用来创建这些类及其相关的依赖关系是很有帮助的。这些类被称为“容器(containers)”,或者更具体地被称为“控制反转(Inversion of Control,ioc)容器”或者“依赖注入(Dependency injection,DI)容器”。容器本质上是一个工厂,负责提供向它请求的类型实例。如果一个给定类型声明它具有依赖关系,并且容器已经被配置为提供依赖类型,那么它将把创建依赖关系作为创建请求实例的一部分。通过这种方式,可以向类型提供复杂的依赖关系而不需要任何硬编码的类型构造。除了创建对象的依赖关系外,容器通常还会管理应用程序中对象的生命周期。

asp.net Core包含了一个默认支持构造函数注入的简单内置容器(由IServiceProvider接口表示),并且ASP.net Core使某些服务可以通过DI获取。ASP.NET Core的容器指的是它管理的类型为services。services是指由ASP.NET Core的IOC容器管理的类型。我们可以在应用程序Startup类的ConfigureServices方法中配置内置容器的服务。

二、使用框架提供的服务

Startup类中的ConfigureServices方法负责定义应用程序将使用的服务,包括平台功能,比如EntityFramework Core和ASP.NET Core mvc。最初,IServiceCollection只向ConfigureServices提供了几个服务定义。如下面的例子:

除了使用默认提供的几个服务定义,我们还可以自己添加。下面是一个如何使用一些扩展方法(如ADDDbContext,AddIdentity)向容器中添加额外服务的例子:

public void ConfigureServices(IServiceCollection services)
{
    // 添加EntityFrameworkCore服务
    services.AddDbContext<AppDbContext>(options => 
    {
        options.UsesqlServer(Configuration.GetConnectionString("DefaultConnection"));
    });
    // 添加MVC服务
    services.AddControllersWithViews();
}

ASP.NET提供的功能和中间件,例如MVC,遵循约定使用一个单一的AddService扩展方法来注册所有该功能所需的服务。

当然,除了使用各种框架功能配置应用程序外,还可以使用ConfigureServices来配置自己的应用程序服务。 

三、注册服务

可以按照下面的方式注册自己的应用程序服务。第一个泛型类型表示将要从容器中请求的类型(这里的类型通常是一个接口)。第二个泛型类型表示将由容器实例化并且用于完成这些请求的具体类型:

// 添加自己的服务
// IRepository是一个接口,表示要请求的类型
// UserRepository表示IRepository接口的具体实现类型
services.AddTransient<IRepository, UserRepository>();

每个services.Add<service>调用添加服务。例如,services.AddControllersWithViews()表示添加MVC需要的服务。

在示例中,有一个名称为CharactersController的控制器。它的Index方法显示已经存储在应用程序的当前字符列表,并且,如果它不存在的话,则初始化具有少量字符的集合。值得注意的是:虽然应用程序使用Entity Framework Core和AppDbContext类作为持久化工具,这在控制器中都不是显而易见的。相反,具体的数据访问机制被抽象在遵循仓储模式的ICharacterRepository接口后面。ICharacterRepository实例是通过构造函数注入的,并且分配给一个私有字段,然后用来访问所需的字符:

using System.Linq;
using DependencyInjectionDemo.Model;
using DependencyInjectionDemo.Repository;
using Microsoft.Aspnetcore.Mvc;

namespace DependencyInjectionDemo.Controllers
{
    public class CharactersController : Controller
    {
        // 定义私有的只读字段
        private readonly ICharacterRepository _characterRepository;

        /// <summary>
        /// 通过构造函数注入并且给私有字段赋值
        /// </summary>
        /// <param name="characterRepository"></param>
        public CharactersController(ICharacterRepository characterRepository)
        {
            _characterRepository = characterRepository;
        }


        public IActionResult Index()
        {
            return View();
        }

        private void PopulateCharactersIfNoneExist()
        {
            // 如果不存在则添加
            if(!_characterRepository.ListAll().Any())
            {
                _characterRepository.Add(new Character("Tom"));
                _characterRepository.Add(new Character("Jack"));
                _characterRepository.Add(new Character("Kevin"));
            }
        }
    }
}

ICharacterRepository接口中只定义了控制器需要使用的Character实例的两个方法:

using DependencyInjectionDemo.Model;
using System.Collections.Generic;

namespace DependencyInjectionDemo.Repository
{
    public interface ICharacterRepository
    {
        IEnumerable<Character> ListAll();
        int Add(Character character);
    }
}

这个接口在运行时需要使用一个具体的CharacterRepository类型来实现。

在CharacterRepository类中使用DI的方式是一个可以在你的应用程序服务遵循的通用模型,不只是在“仓储”或者数据访问类中:

using DependencyInjectionDemo.Context;
using DependencyInjectionDemo.Model;
using System.Collections.Generic;
using System.Linq;

namespace DependencyInjectionDemo.Repository
{
    public class CharacterRepository : ICharacterRepository
    {
        // 定义私有字段
        private readonly AppDbContext _dbContext;

        /// <summary>
        /// 通过构造函数注入,并且给私有字段赋值
        /// </summary>
        /// <param name="dbContext"></param>
        public CharacterRepository(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public int Add(Character character)
        {
            // 添加
            _dbContext.Characters.Add(character);
            // 保存
            return _dbContext.SaveChanges();
        }

        public IEnumerable<Character> ListAll()
        {
            return _dbContext.Characters.AsEnumerable();
        }
    }
}

需要注意的是,CharacterRepository需要一个AppDbContext在它的构造函数中。依赖注入用于像这样的链式方法并不少见,每个请求依次请求它的依赖关系。容器负责解析所有的依赖关系,并返回完全解析后的服务。

创建请求对象和它需要的所有对象,以及那些需要的所有对象,有时称为一个对象图。同样的,必须解析依赖关系的集合通常称为依赖树或者依赖图。

在这种情况下,ICharacterRepository和AppDbContext都必须在Startup类的ConfigureServices方法的服务容器中注册。AppDbContext配置调用AddDbContex<T>扩展方法。下面的代码展示了ICharacterRepository和AppDbContext类型的注册:

public void ConfigureServices(IServiceCollection services)
{
    // 添加EntityFrameworkCore服务
    // 这里是注册AppDbContext使用AddDbContext<T>的形式
    services.AddDbContext<AppDbContext>(options => 
    {
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    });

    // 添加自己的服务
    // IRepository是一个接口,表示要请求的类型
    // UserRepository表示IRepository接口的具体实现类型
    services.AddTransient<IRepository, UserRepository>();

    // 注册ICharacterRepository类型
    services.AddTransient<ICharacterRepository, CharacterRepository>();
    // 添加MVC服务
    services.AddControllersWithViews();
}

Entity Framework Core的数据上下文应当使用Scope的生命周期添加到服务容器中。如果使用上面的AddDbContext<T>方法则会自动处理。仓储将使用与Entity Framework Core相同的生命周期。

四、生命周期

ASP.NET Core服务可以配置为以下三种生命周期:

  • Transient:瞬时生命周期。瞬时生命周期服务在它们每次请求时被创建。这一生命周期适合轻量级的、无状态的服务。
  • Scoped:作用域生命周期。作用域生命周期服务在每次请求时被创建一次。
  • Singleton:单例生命周期。单例生命周期服务在它们第一次被请求时创建,并且每个后续请求将使用相同的实例。如果你的应用程序需要单例行为,则建议让服务容器管理服务的生命周期,而不是在自己的类中实现单例模式和管理对象的生命周期。

服务可以用多种方式在容器中注册。我们已经看到了如何通过指定具体类型来注册一个给定类型的服务实现。除此之外,可以指定一个工厂,它将被用来创建需要的实例。第三种方式是直接指定要使用的类型的实例。在这种情况下,容器将永远不会尝试创建一个实例。

为了说明这些生命周期和注册选项之间的差异,考虑一个简单的接口将一个或多个任务表示为有一个唯一标识符OperationId的操作。根据我们配置这个服务的生命周期的方法,容器将为请求的类提供相同或不同的服务实例。为了弄清楚哪一个生命周期被请求,我们需要创建每一个生命周期选项的类型。我们先定义一个接口,里面定义基接口和三种注入模式的接口:

using System;

namespace DependencyInjectionDemo.Repository
{
    /// <summary>
    /// 基接口
    /// </summary>
    public interface IOperationRepository
    {
        Guid GetOperationId();
    }

    /// <summary>
    /// 瞬时接口
    /// </summary>
    public interface IOperationTransientRepository: IOperationRepository
    {

    }

    /// <summary>
    /// 作用域接口
    /// </summary>
    public interface IOperationScopeRepository : IOperationRepository
    {

    }

    /// <summary>
    /// 单例接口
    /// </summary>
    public interface IOperationSingletonRepository : IOperationRepository
    {

    }
}

我们使用OperationRepository类来实现这些接口:

using System;

namespace DependencyInjectionDemo.Repository
{
    public class OperationRepository : IOperationRepository
    {
        private readonly Guid _guid;

        public OperationRepository()
        {
            _guid = Guid.NewGuid();
        }

        public  Guid GetOperationId()
        {
            return _guid;
        }
    }

    public class OperationTransientRepository : OperationRepository, IOperationTransientRepository
    {

    }

    public class OperationScopeRepository : OperationRepository, IOperationScopeRepository
    {

    }

    public class OperationSingletonRepository : OperationRepository, IOperationSingletonRepository
    {

    }
}

然后在Startup类的ConfigureServices中,每一个类型根据它们命名的生命周期被添加到容器中:

public void ConfigureServices(IServiceCollection services)
{
    // 添加EntityFrameworkCore服务
    // 这里是注册AppDbContext使用AddDbContext<T>的形式
    services.AddDbContext<AppDbContext>(options => 
    {
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    });

    // 添加自己的服务
    // IRepository是一个接口,表示要请求的类型
    // UserRepository表示IRepository接口的具体实现类型
    services.AddTransient<IRepository, UserRepository>();

    // 注册ICharacterRepository类型
    services.AddTransient<ICharacterRepository, CharacterRepository>();

    // 添加瞬时生命周期
    services.AddTransient<IOperationTransientRepository, OperationTransientRepository>();
    // 添加作用域生命周期
    services.AddScoped<IOperationScopeRepository, OperationScopeRepository>();
    // 添加单例生命周期
    services.AddSingleton<IOperationSingletonRepository, OperationSingletonRepository>();
    // 添加MVC服务
    services.AddControllersWithViews();
}

然后添加一个控制器:

using DependencyInjectionDemo.Repository;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionDemo.Controllers
{
    public class OperationController : Controller
    {
        // 定义私有字段
        private readonly IOperationTransientRepository _transientRepository;
        private readonly IOperationScopeRepository _scopeRepository;
        private readonly IOperationSingletonRepository _singletonRepository;

        /// <summary>
        /// 通过构造函数实现注入
        /// </summary>
        /// <param name="transientRepository"></param>
        /// <param name="scopeRepository"></param>
        /// <param name="singletonRepository"></param>
        public OperationController(IOperationTransientRepository transientRepository,
            IOperationScopeRepository scopeRepository,
            IOperationSingletonRepository singletonRepository)
        {
            _transientRepository = transientRepository;
            _scopeRepository = scopeRepository;
            _singletonRepository = singletonRepository;
        }


        public IActionResult Index()
        {
            // ViewBag赋值
            ViewBag.TransientGuid = _transientRepository.GetOperationId();
            ViewBag.ScopedGuid = _scopeRepository.GetOperationId();
            ViewBag.SingletonGuid = _singletonRepository.GetOperationId();
            return View();
        }
    }
}

对应的Index视图代码:

<div class="row">
    <div>
        <h2>GuidItem Shows</h2>

        <h3>TransientGuid: @ViewBag.TransientGuid</h3>

        <h3>ScopedGuid: @ViewBag.ScopedGuid</h3>

        <h3>SingletonGuid: @ViewBag.SingletonGuid</h3>
    </div>
</div>

然后我们打开两个浏览器,刷新多次,只会发现“TransientGuid” 和“ScopedGuid”的值在不断变化,而“SingletonGuid”的值是不会变化的,这就体现了单例模式的作用,如下图所示:

但是这样还不够,要知道我们的Scoped的解读是“生命周期横贯整次请求”,但是现在演示起来和Transient好像没有什么区别(因为两个页面每次浏览器请求仍然是独立的,并不包含于一次中),所以我们采用以下代码来演示下(同一请求源):

@*引入命名空间*@
@using DependencyInjectionDemo.Repository

@*通过该inject引入*@
@inject IOperationTransientRepository OperationTransientRepository
@inject IOperationScopeRepository OperationScopeRepository
@inject IOperationSingletonRepository OperationSingletonRepository

<div class="row">
    <div>
        <h2>GuidItem Shows</h2>
        <h3>TransientGuid: @OperationTransientRepository.GetOperationId()</h3>
        <h3>ScopedGuid: @OperationScopeRepository.GetOperationId()</h3>
        <h3>SingletonGuid: @OperationSingletonRepository.GetOperationId()</h3>
    </div>
</div>

然后修改Index视图:

<div class="row">
    <div>
        @html.Partial("GuidPartial")
        <h2>**************************</h2>
        <h2>GuidItem Shows</h2>
        <h3>TransientGuid: @ViewBag.TransientGuid</h3>
        <h3>ScopedGuid: @ViewBag.ScopedGuid</h3>
        <h3>SingletonGuid: @ViewBag.SingletonGuid</h3>
    </div>
</div>

在运行程序执行:

可以看到:每次请求的时候Scope生命周期在同一请求中是不变的,而Transient生命周期还是会不断变化的。

  • 瞬时(Transient):对象总是不同的,向每一个控制器和每一个服务提供了一个新的实例(同一个页面内的Transient也是不同的)。
  • 作用域(Scoped):对象在一次请求中是相同的,但在不同请求中是不同的(在同一个页面内多个Scoped是相同的,在不同页面中是不同的)。
  • 单例(Singleton):对象对每个对象和每个请求是相同的(无论是否在ConfigureServices中提供实例)。

五、请求服务

来自HttpContext的一次ASP.NET请求中,可用的服务是通过RequestServices集合公开的。

请求服务将你配置的服务和请求描述为应用程序的一部分。在你的对象指定依赖关系后,这些满足要求的对象可通过查找RequestServices中对应的类型得到,而不是ApplicationServices。

通过,不应该直接使用这些属性,而是通过类的构造函数请求需要的类的类型,并且让框架来注入依赖关系。这将会生成更易于测试的和更松散耦合的类。

六、设计你的依赖服务

应该设计你的依赖注入服务来获取它们的合作者。这意味着在你的服务中,避免使用有状态的静态方法调用和直接实例化依赖的类型。

如果你的类有太多的依赖关系被注入时该怎么办?这通常表明你的类试图做太多,并且可能违反了单一职责原则。看看是否可以通过转移一些职责到一个新的类来重构。

注意,你的Controller类应该重点关注用户界面(UI),因此业务规则和数据访问实现细节应该保存在这些适合单独关注的类中。

关于数据访问,如果你已经在Startup类中配置了EF,那么你能够方便地注入Entity Framework的DBContext类型到你的控制器中。然而,最好不要在你的UI项目中直接依赖DBContext。相反,应该依赖于一个抽象(比如一个仓储接口),并且限定使用EF(或其他任何数据访问技术)来实现这个接口。这将减少应用程序和特定的数据访问策略之间的耦合,并且使你的应用程序代码更容易测试。

GitHub示例代码:https://github.com/jxl1024/DependencyInjection

到此这篇关于ASP.NET Core依赖注入的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: ASP.NET Core依赖注入详解

本文链接: https://www.lsjlt.com/news/143670.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ASP.NET Core依赖注入详解
    目录一、什么是依赖注入二、使用框架提供的服务三、注册服务四、生命周期五、请求服务六、设计你的依赖服务ASP.NET Core的底层设计支持和使用依赖注入。ASP.NET Core应用...
    99+
    2022-11-13
  • 详解asp.net core 依赖注入
    前言   好久没有写微博了,因为前段时间由于家庭原因决定从工作了3年多的北京转移到上海去。依赖注入在学习net core的时候也有写过类似的东西,只是实践的较少,结果来到上海新...
    99+
    2022-06-07
    net ASP.NET core ASP
  • ASP.NET Core 依赖注入详细
    目录一、控制反转二、好莱坞法则三、流程控制四、三种依赖注入方式1.构造器注入2.属性注入3.方法注入五、生命周期六、ASP.Net Core 中自带的注入 前言: ASP....
    99+
    2022-11-12
  • ASP.NET Core依赖注入(DI)讲解
    ASP.NET Core的底层设计支持和使用依赖注入。ASP.NET Core 应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入。由ASP.N...
    99+
    2022-11-13
  • 理解ASP.NET Core 依赖注入(Dependency Injection)
    目录依赖注入什么是依赖注入依赖注入有什么好处ASP.NET Core内置的依赖注入服务生存周期服务释放TryAdd{Lifetime}扩展方法解析同一服务的多个不同实现Replac&...
    99+
    2022-11-12
  • Asp.net core中依赖注入的实现
    使用服务 在Asp.net core的Controller中,可以通过如下两种方式获取系统注入的服务: 构造函数 可以直接在构造函数中传入所依赖的服务,这是非常常见的DI注入方式。 ...
    99+
    2022-11-13
  • ASP.NET Core MVC 依赖注入View与Controller
    目录一、ASP.NET Core MVC 之依赖注入 View 1.填充查找数据2.重写服务二、 ASP.NET Core MVC 之依赖注入 Controller1.构造...
    99+
    2022-11-12
  • ASP.NET Core实现自动依赖注入
    目录定义一个枚举定义三种注入类型扫描运行目录下所有的dll,进行自动注入使用自动依赖注入功能 在开发.NET Core web服务的时候,我们习惯使用自带的依赖注入容器来进行注入。 ...
    99+
    2022-11-12
  • ASP.NET Core依赖注入实例分析
    今天小编给大家分享一下ASP.NET Core依赖注入实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。ASP...
    99+
    2023-06-30
  • ASP.NET Core依赖注入框架的使用
    目录一、IoC框架二、IoC-Autofac 三、.NET Core中自带DI的使用四、Autofac 使用五、批量注入 前言: 还记得上篇文章中ASP.NET Core 依赖注入详...
    99+
    2022-11-12
  • ASP.NET Core MVC控制器请求依赖注入
    ASP.NET Core MVC 控制器应通过构造函数明确地请求它们地依赖关系,在某些情况下,单个控制器地操作可能需要一个服务,在控制器级别上的请求可能没有意义。在这种情况下,也可以...
    99+
    2022-11-13
  • ASP.NET Core依赖关系注入怎么实现
    本篇内容主要讲解“ASP.NET Core依赖关系注入怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ASP.NET Core依赖关系注入怎么实现”吧!1.前言面向对象...
    99+
    2023-06-29
  • ASP.NET Core MVC创建控制器与依赖注入讲解
    默认的IControllerActivator 在 ASP.NET Core 中,当 MVC 中间件接收到请求时,通过路由选择要执行的控制器和操作方法。为了实际的执行操作, MVC ...
    99+
    2022-11-13
  • ASP.NET Core依赖注入生命周期是什么
    这篇文章主要介绍“ASP.NET Core依赖注入生命周期是什么”,在日常操作中,相信很多人在ASP.NET Core依赖注入生命周期是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”A...
    99+
    2023-07-05
  • ASP.net core使用Autofac实现泛型依赖注入
    目录什么是泛型依赖注入.net core里实现泛型依赖注入安装AutofacIMyRepository定义仓储接口MyRepositoryBase仓储实现在Program声明实现依赖...
    99+
    2022-11-13
  • ASP.NET Core实现自动依赖注入的示例
    这篇文章主要介绍了ASP.NET Core实现自动依赖注入的示例,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在开发.NET Core web服务的时候,我们习惯使用自带的依...
    99+
    2023-06-14
  • ASP.NET Core依赖注入DI容器怎么实现
    这篇文章主要介绍“ASP.NET Core依赖注入DI容器怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ASP.NET Core依赖注入DI容器怎么实现”文章能帮助大家解...
    99+
    2023-07-05
  • swift依赖注入和依赖注入容器详解
    目录什么是控制反转(Inversion of Control)?什么是依赖注入?依赖注入的种类初始化器注入属性注入方法注入依赖注入容器实现一个简单的依赖注入容器总结什么是控制反转(I...
    99+
    2023-01-28
    swift依赖注入依赖注入容器 swift依赖注入
  • 详解Angular依赖注入
    目录概述一、依赖注入二、Angular的依赖注入框架概述 依赖注入:设计模式 依赖:程序里需要的某种类型的对象。 依赖注入框架:工程化的框架 注入器Injector:用它的API创...
    99+
    2022-11-12
  • ASP.NET Core MVC如何创建控制器与依赖注入
    这篇文章主要介绍“ASP.NET Core MVC如何创建控制器与依赖注入”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ASP.NET Core MVC如何创...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作