前言

很久很久以前就写过了 Asp.net core 学习笔记 ( DI 依赖注入 ), 这篇只是整理一下而已.

参考

Using dependency injection in a .Net Core console application

介绍

Dependency Injection 是一种管理依赖的方式。

class 之间经常会有依赖关系,比如

public class ServiceA
{
public void DoSomething() => Console.WriteLine("do something...");
} public class ServiceB(ServiceA serviceA)
{
public void DoSomething() => serviceA.DoSomething();
}

ServiceB 的某个方法需要使用到 ServiceA 的某个方法,这就表示 ServiceB 依赖了 ServerA。

依赖关系会让实例化变得复杂和繁琐。

var serviceA = new ServiceA();
var serviceB = new ServiceB(serviceA);
serviceB.DoSomething();

若想实例化 ServiceB 首先需要实例化 ServiceA,试想想如果依赖链很长的话,代码将变得非常长和混乱。

而依赖注入就是为了解决这个问题而诞生的。使用依赖注入后,我们在实例化时就不需要去理会复杂的依赖关系了。

许多地方都有 Dependency Injection 这个概念。比如 Java、Angular 也有。但是它们的实现都有一些些区别,也可以理解为变种吧,但中心思想是一样的。

How It Work ?

首先, 把整个项目的 Service (也就是 class) 都交给一个 ServiceProvider 来管理. 不管你要什么 Service 都必须跟 ServiceProvider 拿.

每个 Service 通过 Construtor 来表达依赖关系.每当有人需要 Service 的时候, ServiceProvider 就会查找 Service 的依赖, 一层层的去创建出所有需要的对象.

整个过程大致上就是这样.

Get Started

用 Console 测试就可以了

dotnet new console -o DependencyInjection
dotnet add package Microsoft.Extensions.DependencyInjection

Console 没有很多 build-in 的 dll, 如果想方便可以一次添加.

dotnet add package Microsoft.AspNetCore.App

ServiceCollection & ServiceProvider

ServiceA 依赖 ServiceB

public class ServiceA
{
private readonly ServiceB _serviceB; public ServiceA(
ServiceB serviceB
)
{
_serviceB = serviceB;
} public string GetValue() {
return _serviceB.GetValue();
}
} public class ServiceB
{
public string GetValue()
{
return "Service B value";
}
}

program.cs

public class Program
{
public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection(); // 一个 Services 大合集
serviceCollection.AddScoped<ServiceA, ServiceA>(); // 添加 ServiceA 和 ServiceB 进去大合集
serviceCollection.AddScoped<ServiceB, ServiceB>();
var serviceProvider = serviceCollection.BuildServiceProvider(); // 把所有 Service 丢进去后就可以创建出 Provider
var serviceA = serviceProvider.GetRequiredService<ServiceA>(); // 通过 Provider 获取 Service, Provider 在创建 ServiceA 前, 会先创建 ServiceB 并提供给 ServiceA
Console.WriteLine(serviceA.GetValue());
}
}

创建大合集 > 把所有 service 放入合集 > 创建 Provider > 通过 Provider 获取 Service.

Provider 的职责就是管理好依赖, 依据每个 class 的依赖去创建对象.

添加 Service 的细节 Singleton, Scoped, Transient

serviceCollection.AddSingleton<ServiceB, ServiceB>();
serviceCollection.AddScoped<ServiceB, ServiceB>();
serviceCollection.AddTransient<ServiceB, ServiceB>();

Singleton 就是单列, ServiceProvider 第一次创建对象后会把对象收起来, 往后都直接返回这个对象.

Scoped 也是单列, 但是它有一个范围. Singleton 的范围是整个 Application 结束都是单列 (用在 Web Application, 一个 request 就表示一个 Scope)

public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<ServiceA, ServiceA>();
serviceCollection.AddScoped<ServiceB, ServiceB>();
var serviceProvider = serviceCollection.BuildServiceProvider();
ServiceA serviceA;
using (var scope = serviceProvider.CreateScope()) // 开启 scope
{
serviceA = scope.ServiceProvider.GetRequiredService<ServiceA>();
var serviceAA = scope.ServiceProvider.GetRequiredService<ServiceA>();
Console.WriteLine((serviceA == serviceAA).ToString()); // 在 scope 内是同一个对象
}
var serviceAAA = serviceProvider.GetRequiredService<ServiceA>();
Console.WriteLine((serviceA == serviceAAA).ToString()); // scope 之外和 scope 内是不同的对象
}

Transient 表示每一次 ServiceProvider 都会创建新的实例.

什么时候应该使用什么呢? 参考: Stack Overflow – AddTransient, AddScoped and AddSingleton Services Differences

Interface Best Practice

上面的例子中, 直接提供了类型, 这个是不理想的, 我们应该要面向接口编程 (方便扩展, 维护)

serviceCollection.AddScoped<ServiceA, ServiceA>();

设计一个接口

然后使用接口声明

serviceCollection.AddScoped<IGetValue, ServiceA>();
serviceProvider.GetRequiredService<IGetValue>();

这样的好处就是, 以后可以替换具体实现. 只要实现了接口, 任何一个 class 都可以替换上去.

Multiple Provide

当同一个接口提供多个 Service 时, 可以通过 GetServices 获取到所有 services, identity 的 UserValidator 就用了这个方式

serviceCollection.AddScoped<IGetValue, ServiceC>();
serviceCollection.AddScoped<IGetValue, ServiceD>();
Console.WriteLine(string.Join(',', services.Select(s => s.GetType().Name))); // ServiceC,ServiceD

Create Instance with ServiceProvider

如果我们想通过反射实例化一个带有依赖的 class, 可以这么写

var instance = ActivatorUtilities.CreateInstance<AbcService>(serviceProvider);

把 ServiceProvider 丢进去就可以了. 内部会调用 GetRequiredService

Web Application 中使用 Dependency Injection

program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddScoped<ServiceA, ServiceA>();

Razor Page or Controller

public class IndexModel : PageModel
{
public IndexModel(
ServiceA serviceA // 可以通过 Constructor
)
{
} public void OnGet(ServiceA serviceA) // 可以通过 Parameter
//public void OnGet([FromServices] ServiceA serviceA) // 在 .NET 7 之前需要加上 [FromServices]
{ }
}

View

@page
@model IndexModel
@inject ServiceA ServiceA // 使用 @inject

ASP.NET Core – Dependency Injection的更多相关文章

  1. C# Ioc ASP.NET MVC Dependency Injection

    ASP.NET MVC Dependency Injection 同志们,非常快速的Ioc注册接口和注入Mvc Controller,步骤如下: 安装Unity.Mvc NuGet Package 在 ...

  2. DI in ASP.NET Core

    .NET-Core Series Server in ASP.NET-Core DI in ASP.NET-Core Routing in ASP.NET-Core Error Handling in ...

  3. [译]ASP.NET Core依赖注入深入讨论

    原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...

  4. ASP.NET Core 2 学习笔记(四)依赖注入

    ASP.NET Core使用了大量的依赖注入(Dependency Injection, DI),把控制反转(Inversion Of Control, IoC)运用的相当巧妙.DI可算是ASP.NE ...

  5. Building simple plug-ins system for ASP.NET Core(转)

    Recently I built plug-ins support to my TemperatureStation IoT solution web site. The code for .NET ...

  6. ASP.NET Core依赖注入最佳实践,提示&技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  7. ASP.NET Core 依赖注入最佳实践与技巧

    ASP.NET Core 依赖注入最佳实践与技巧 原文地址:https://medium.com/volosoft/asp-net-core-dependency-injection-best-pra ...

  8. Professional C# 6 and .NET Core 1.0 - 40 ASP.NET Core

    本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - 40 ASP.NET Core --- ...

  9. asp.net core 系列之Dependency injection(依赖注入)

    这篇文章主要讲解asp.net core 依赖注入的一些内容. ASP.NET Core支持依赖注入.这是一种在类和其依赖之间实现控制反转的一种技术(IOC). 一.依赖注入概述 1.原始的代码 依赖 ...

  10. ASP.NET Core 的 Dependency Injection

    ASP.NET Core使用了大量的DI(Dependency Injection)设计,有用过Autofac或类似的DI Framework对此应该不陌生.本篇将介绍ASP.NET Core的依赖注 ...

随机推荐

  1. Jenkins插件管理(Manager Plugins)【快速提升项目构建和部署实施的工作效率】

    Jenkins 是一个很棒的开源自动化平台.它有一些开箱即用的强大功能.然而,在我看来,让它脱颖而出的是它的社区和它开发的插件.有超过一千个插件可用于支持几乎所有用于构建.部署和自动化项目的技术.工具 ...

  2. 部分解决 | ocrmypdf对中文pdf进行ocr识别后存在多余空格

    1.问题 ocrmypdf安装采用的是在windows安装方法具体看 https://media.readthedocs.org/pdf/ocrmypdf/latest/ocrmypdf.pdf 由于 ...

  3. 可视化—gojs 超多超实用经验分享(三)

    目录 32.go.Palette 一排放两个 33.go.Palette 基本用法 34.创建自己指向自己的连线 35.设置不同的 groupTemplate 和 linkTemplate 36.监听 ...

  4. [oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符

    另类字符 回忆上次内容 上次再次输出了大红心<span style="font-size:64px;color:red"></span> 找到了红心对应的编 ...

  5. ABC358

    A link -- 点击查看代码 #include<bits/stdc++.h> using namespace std; string s,t; signed main(){ cin & ...

  6. 2024-07-24:用go语言,给定一个整数数组 nums,其中至少包含两个元素。 可以根据以下规则执行操作:选择最前面两个元素删除、选择最后两个元素删除,或选择第一个和最后一个元素删除。 每次操作

    2024-07-24:用go语言,给定一个整数数组 nums,其中至少包含两个元素. 可以根据以下规则执行操作:选择最前面两个元素删除.选择最后两个元素删除,或选择第一个和最后一个元素删除. 每次操作 ...

  7. windows10使用scp命令

    windows10使用scp命令 windows自带scp命令 windows上传文件到linux//使用方法:scp 源文件路径 账户@地址:目的路径scp   C:\Users\zbh\Deskt ...

  8. c++代码实现 RSA的简易demo【偏向实践】

    写在前面 [如果你还没搞明白算法具体步骤建议先去看视频了解,本demo旨在简单实践该算法] 本实践在理论上是成立的,但由于计算x的时候很容易溢出,所以观者可以理解该简易demo后对数据进行处理[以字符 ...

  9. 【PHP】5版本 过程式操作MySQL

    建立连接和释放连接: # 连接参数 $sever = 'localhost:3309'; $username = 'root'; $password = 'root'; # 调用连接方法,如果失败结束 ...

  10. 【Hibernate】Re01 快速入门

    官网地址: http://hibernate.org/orm/releases/5.4/ 地址: https://bintray.com/hibernate/artifacts/hibernate-o ...