.NET Extensions 是一套官方的、开源的、跨平台的 API 集合,提供了一些常用的编程模式和实用工具,例如依赖项注入、日志记录、缓存、Host以及配置等等。该项目的大多数 API 都被用在 .NET 平台的各个应用框架上,如 http://ASP.NET Core,Xamarin 等等。虽然 http://ASP.NET 使用了很多这些 API 但 http://ASP.NET 并没有与它们紧密耦合。你也可以在控制台应用程序、WinForm以及WPF应用程序上使用它们。总结一句就是:它是官方造的“轮子”
**
NET API browser 中的 .NET Platform Extensions
.NET Platform Extensions 包括下面所列的包,由于 .NET 5 正在整合各个代码仓库,所以 .NET Extension 中的部分代码仓库已经分别以迁移到了 dotnet/runtime 和 dotnet/aspnetcore 这两个仓库中。
## 移动到 dotnet/runtime 的包:
## **Caching**
Microsoft.Extensions.Caching.Abstractions
Microsoft.Extensions.Caching.Memory
## Configuration
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.CommandLine
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Ini
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.UserSecrets
Microsoft.Extensions.Configuration.Xml
## Dependency Injection
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions
## File Providers
Microsoft.Extensions.FileProviders.Abstractions
Microsoft.Extensions.FileProviders.Composite
Microsoft.Extensions.FileProviders.Physical
File System Globbing
Microsoft.Extensions.FileSystemGlobbing
## Hosting
Microsoft.Extensions.Hosting
Microsoft.Extensions.Hosting.Abstractions
Http Client Factory
Microsoft.Extensions.Http
## Logging
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging.Abstractions
Microsoft.Extensions.Logging.Configuration
Microsoft.Extensions.Logging.Console
Microsoft.Extensions.Logging.Debug
Microsoft.Extensions.Logging.EventLog
Microsoft.Extensions.Logging.EventSource
Microsoft.Extensions.Logging.Testing
Microsoft.Extensions.Logging.TraceSource
## Options
Microsoft.Extensions.Options
Microsoft.Extensions.Options.ConfigurationExtensions
Microsoft.Extensions.Options.DataAnnotations
## Primitives
Microsoft.Extensions.Primitives
## 移动到 dotnet/aspnetcore 的包:
## Configuration
Microsoft.Extensions.Configuration.KeyPerFile
## File Providers
Microsoft.Extensions.FileProviders.Embedded
Microsoft.Extensions.FileProviders.Embedded.Manifest.Task
## Health Checks
Microsoft.Extensions.Diagnostics.HealthChecks
Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions
## JS Interop
Microsoft.JSInterop
Mono.WebAssembly.Interop
## Localization
Microsoft.Extensions.Localization
Microsoft.Extensions.Localization.Abstractions
## Logging
Microsoft.Extensions.Logging.AzureAppServices
## Object Pool
Microsoft.Extensions.ObjectPool
Web Encoders
Microsoft.Extensions.WebEncoders
## 目前仍在 dotnet/extensions 的包:
## Caching
Microsoft.Extensions.Caching.SqlServer
Microsoft.Extensions.Caching.StackExchangeRedis
## Configuration
Microsoft.Extensions.Configuration.NewtonsoftJson
## Hosting
Microsoft.Extensions.Hosting.Systemd
Microsoft.Extensions.Hosting.WindowsServices
## Http Client Factory
Microsoft.Extensions.Http.Polly
## Logging
Microsoft.Extensions.Logging.Analyzers (has not been released to NuGet.org as of writing)
Microsoft.Extensions.DependencyInjection.Specification.Tests
**
依赖注入,字面理解就是将依赖注入到你的代码中,那么为什么需要将依赖注入到你的代码中呢?以及怎么注入到你的代码?下面我们来一步一步来了解整个过程。
1)首先,通常情况下当我们要使用另外一个类型时我们会使用下面的代码来生成一个该类型的实例。
var a = new ClassA();
a.MehtodA();
此时相当于我们的代码依赖了ClassA,一旦 ClassA 发生了改变我们的代码也就需要发生相应的改变,这不符合松耦合的设计原则,那么我们应该怎么做呢?
2)为了让我们的代码不直接依赖于 ClassA 我们来定义一个接口 InterfaceA,
interface InterfaceA
{
void MethodA();
}
并让== ClassA 实现该接口==,那么我们就可以这样写我们的代码了,
InterfaceA a = new ClassA();
a.MethodA();
此时我们的代码本质上是对 InterfaceA 的依赖,并不关心 InterfaceA 的具体实现。不过目前好像没什么鸟用,因为我们的代码中仍然需要显式实例化 ClassA,这时候我们就希望能有一个神奇的工具能给我们一个 InterfaceA 的实例了。
3)现在 IoC 容器就闪亮登场了(IoC, Inversion of Control / 控制反转),它可以使用多种方式给我们的代码中注入我所需要的 InterfaceA 实例,下面的代码就是利用构造函数注入依赖。
public class MyCode
{
private InterfaceA _ia;
public MyCode(InterfaceA ia)
{
_ia = ia;
}
public void MyMethod()
{
_ia.MethodA();
}
}
这样在我们的代码里就看不到 ClassA 了,我们的代码也就脱离了对 ClassA的依赖,实现了松耦合,也践行了部分面向对象的设计原则,如依赖倒置(DIP)原则。
下面我们使用 Microsoft.Extensions 中的 DependencyInjection 容器(在下文中我们简称为DI容器)来实现上面示例的完整代码。
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace DemoCode
{
class Program
{
static void Main(string[] args)
{
//初始化容器
IServiceCollection ioc = new ServiceCollection();
//这里的是添加服务到容器中
//Transient指的是服务实例的生命周期,即每次请求服务的时候都会创建一个新的实例
ioc.TryAddTransient<MyCode>();
ioc.TryAddTransient<InterfaceA, ClassA>();
//创建serviceProvider对象来获取服务的实例
var serviceProvider = ioc.BuildServiceProvider();
var myCodeinstance = serviceProvider.GetService<MyCode>();
//调用我们定义的方法
myCodeinstance .Run();
}
}
//
public class MyCode
{
private InterfaceA _ia;
public MyCode(InterfaceA ia)
{
_ia = ia;
}
public void Run()
{
_ia.MethodA();
}
}
//定义服务的接口
public interface InterfaceA
{
void MethodA();
}
//服务的具体实现
public class ClassA : InterfaceA
{
public void MethodA()
{
Console.WriteLine("MethodA 被调用");
}
}
}
依赖注入以及控制反转都属于设计模式(Design Pattern),并且通常情况下,要实现控制反转就需要基于依赖注入。它们都是为了遵从面向对象语言的设计原则 S.O.L.I.D 而产生的。
S: Single responsibility / 单一职责
O: Open closed / 开闭原则(对扩展开放,对修改关闭)
L: Liskov substitution / 里氏替换
I: Interface segregation / 接口分离
D: Dependency inversion / 依赖倒置
关于S.O.L.I.D 的更多内容可以查看下面这篇文章,
S.O.L.I.D: The First 5 Principles of Object Oriented Designscotch.io
1、引用
Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.Extensions.DependencyInjection
这两个包含了容器的抽象以及容器的具体实现,其中Microsoft.Extensions.DependencyInjection.Abstractions 为抽象,Microsoft.Extensions.DependencyInjection 则为具体实现。
当你要在自己的程序中使用时,使用nuget包管理工具安装 Microsoft.Extensions.DependencyInjection 包即可。
2、主要类型
1、引用类型
使用 DI 容器需要熟悉下面的接口与类型,Microsoft.Extensions.DependencyInjection.IServiceCollection,该接口包含了一系列 Add 扩展方法来添加你的服务,该接口的默认实现为 Microsoft.Extensions.DependencyInjection.ServiceCollection 类。
容器接口叫做 IServiceCollection 是因为 “微软”将容器中的类型视为我们程序所需的服务,有了这个前提将它命名为 “ServiceCollection”也就非常合理了。
System.IServiceProvider,使用 IServiceCollection 的扩展方法 BuildServiceProvider() 可以得到一个默认的 ServiceProvider 对象来让我们获取服务实例,ServiceProvider 实现了IServiceProvider 接口,该接口包含了一个 GetService() 方法来获取服务。IServiceProvider 接口的默认实现为 Microsoft.Extensions.DependencyInjection.ServiceProvider 类。
2、IServiceCollection,添加服务与生命周期
IServiceCollection 接口包含一系列的扩张方法让我们方便的添加服务,常用的包括下面三个方法,
除了上面这三个常用方法之外,还有 TryAddTransient、TryAddSingleton、TryAddScoped 这三个用于添加服务的方法,与之前所提三个方法的区别在于带 Try 的这三个方法在添加服务时会检查服务是否已存在,若已经存在则不再添加。
3、IServiceProvider,获取服务与注入依赖
你可以使用 IServiceCollection 的 BuildServiceProvider() 方法来获取 IServiceProvider 对象,使用 GetServices 方法便可以获取你添加到容器中的服务,对于 Transient 和 Singleton 的服务使用这个方法便可以实现其对应的生命周期逻辑。
Scoped的情况相对有点复杂,如果你直接使用 IServiceProvider 的 GetServices()方法获取服务那么Scoped 服务的行为就和 Singleton 是一样的,它的的生命周期和 IServiceProvider 对象是一致的,下面的代码演示了如何使用 ScopedService,
//在using的作用域内scopeService是单例的
//在创建新的Scope之后,容器会创建新的scopedService实例
using (var scope = serviceProvider.CreateScope())
{
var scopedServiceProvider = scope.ServiceProvider;
var myScopedService = scopedServiceProvider.GetService<IMyScopedService>();
}
除了使用容器直接获取服务,我们还需要了解容器的注入方式,ioc容器的注入通常有三种方法
构造函数注入在上面已经演示了,下面是 http://ASP.NET Core 自定义Middleware 中方法注入的示例代码,
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
//在Invoke方法中,将IMyScopedService服务注入到类中。
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
四、在 http://ASP.NET Core 中替换容器实现
DI 容器相对于其他第三方更加成熟的 IoC 容器来说,功能并不是十分完善,下面是 DI 容器暂时不支持的功能。
不过在 DI 容器能满足你需求的情况下,微软还是建议使用该容器。 如果它不能满足你的需求,你可以选择下面推荐的第三方 IoC 容器。
不过在 http://ASP.NET Core 中情况比较特殊,由于 http://ASP.NET Core 是基于 DI 容器的,所以要在http://ASP.NET Core 中使用其他容器,就需要替换 IServiceProvider 的实现。下面的代码展示的是如何在 http://ASP.NET Core 中集成 Autofac 容器。
public class Program
{
public static void Main(string[] args)
{
// 适用于ASP.NET Core 3.0+:
var host = Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webHostBuilder => {
webHostBuilder
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>();
})
.Build();
host.Run();
}
}