AspNetCore IOC
前言
AspNetCore IOC基本天天都在用,但是有一些东西还是需要总结一下。
1.基础知识
官网:https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0
ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现[控制反转 (IoC)]的技术。
概念比较抽象,如果觉得不好理解,我认为多熟悉AspNetCore的IOC的API也是一种进步。
依赖包:
Microsoft.Extensions.DependencyInjection:实现包,实现IOC的基本功能
Microsoft.Extensions.DependencyInjection.Abstractions:抽象包,用于扩展容器
核心接口:
- Service:服务实例
- ServiceDescriptor:用于描述服务的信息。比如服务名(ServiceType)、实现类(ImplementationType)、生命周期(Lifetime)。
- IServiceCollection:是一个List
集合,用于保存服务描述信息。 - IServiceProvider:用于解析服务实列。
- ActivatorUtilities:用于解析一个容器中不存在,但是依赖了容器中的服务的实列。
2. 服务注册
创建测试类:
public interface IDbConnection { }
public class SqlDbConnection : IDbConnection { }
public class DbContext
{
public IDbConnection DBConnection { get; set; }
public string ConnectionString { get; set; }
public DbContext(IDbConnection _DBConnection,string _connectionString)
{
this.DBConnection = _DBConnection;
this.ConnectionString = _connectionString;
}
}
public interface ILogger<T, T1> { }
public class ConsoleLogger<T, T1> : ILogger<T, T1> { }
创建服务:
IServiceCollection services = new ServiceCollection();
万能公式:
// 单例模式
// 由于IServiceCollection实现了IList<ServiceDescriptor> 接口
// 因此下面是一个万能公式,其它的都是扩展方法,本质调用的还是这个万能公式,包括委托的方式(他的实现类型是一个委托)
services.Add(new ServiceDescriptor(typeof(IDbConnection), typeof(SqlDbConnection), ServiceLifetime.Singleton));
泛型接口:
services.AddSingleton<IDbConnection, SqlDbConnection>();
反射:
services.AddSingleton(typeof(IDbConnection), typeof(SqlDbConnection));
委托方式:
// 委托方式
services.AddSingleton(sp =>
{
var connection = sp.GetRequiredService<IDbConnection>();
return new DbContext(connection, "c1");
});
// 高级用法
services.AddSingleton(sp =>
{
return ActivatorUtilities.CreateInstance<DbContext>(sp, "c1");
});
泛型注册:
// 注册泛型时,只能使用反射接口,并且泛型参数不要写入,解析时来确立,如果有多个泛型参数使用逗号隔开
services.AddSingleton(typeof(ILogger<,>), typeof(ConsoleLogger<,>));
2.服务解析
构建容器:
IServiceCollection services = new ServiceCollection();
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = true,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
同一个服务多个实现,默认获取最后一个
services.AddSingleton<IDbConnection, SqlDbConnection>();
services.AddSingleton<IDbConnection, MySqlConnection>();
如果服务未注册,返回null
IDbConnection? connection = container.GetService<IDbConnection>();
服务不存在讲引发异常
IDbConnection connection1 = container.GetRequiredService<IDbConnection>();
获取IDbConnection所有实现
IEnumerable<IDbConnection> connections = container.GetServices<IDbConnection>();
假设DbContext依赖IDbConnection,并且需要一个name,但是容器没有注册
DbContext
var context = ActivatorUtilities.CreateInstance<DbContext>(container, "c1");
3.生命周期
Transient
:瞬时生命周期, Transient服务在每次被请求时都会被创建一个新的对象。这种生命周期比较适用于轻量级的无状态服务。Scoped
: Scoped生命周期的服务是每次web请求被创建,局部单例对象, 在某个局部内是同一个对象(作用域单例,本质是容器单例);一次请求内是一个单例对象,多次请求则多个不同的单例对象.Singleton
: Singleton生命能够周期服务在第一被请求时创建,在后续的每个请求都会使用同一个实例。如果你的应用需要单例服务,推荐的做法是交给服务容器来负责单例的创建和生命周期管理,而不是自己来走这些事情。
测试类:
public class IOCLife : IDisposable
{
public string ID { get; }
public IOCLife()
{
ID = Guid.NewGuid().ToString();
}
public void Dispose()
{
Console.WriteLine(ID + ":已释放...");
}
}
3.1Scoped
a1、a2同一个对象,a3、a4同一个对象,当执行完using的代码,a3、a4生命周期结束
var services = new ServiceCollection();
// 在某个局部内是同一个对象(作用域单例,本质是容器单例);
services.AddScoped<IOCLife>();
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions()
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = false,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
//a1:通过根容器创建,需要设ValidateScopes为false(危险)
var a1 = container.GetRequiredService<IOCLife>();
var a2 = container.GetRequiredService<IOCLife>();
using (var scope = container.CreateScope())
{
var a3 = scope.ServiceProvider.GetRequiredService<IOCLife>();
var a4 = scope.ServiceProvider.GetRequiredService<IOCLife>();
Console.WriteLine("scop0:" + a1.ID);
Console.WriteLine("scop1:" + a2.ID);
Console.WriteLine("scop2:" + a3.ID);
Console.WriteLine("scop3:" + a4.ID);
}
3.2Singleton
创建完a1后,a2、a3、a4每次都返回已生成的对象
var services = new ServiceCollection();
// 在某个局部内是同一个对象(作用域单例,本质是容器单例);
// 单例
services.AddSingleton<IOCLife>();
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions()
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = false,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
//a1:通过根容器创建,需要设ValidateScopes为false(危险)
var a1 = container.GetRequiredService<IOCLife>();
var a2 = container.GetRequiredService<IOCLife>();
using (var scope = container.CreateScope())
{
var a3 = scope.ServiceProvider.GetRequiredService<IOCLife>();
var a4 = scope.ServiceProvider.GetRequiredService<IOCLife>();
Console.WriteLine("scop0:" + a1.ID);
Console.WriteLine("scop1:" + a2.ID);
Console.WriteLine("scop2:" + a3.ID);
Console.WriteLine("scop3:" + a4.ID);
}
3.3 Transient
a1、a2、a3、a4每次都是重新生成的对象,当执行完using的代码,a3、a4生命周期结束
var services = new ServiceCollection();
// 瞬时生命周期
services.AddTransient<IOCLife>();
IServiceProvider container = services.BuildServiceProvider(new ServiceProviderOptions()
{
ValidateOnBuild = true,//构建时检查是否有依赖没有注册的服务
ValidateScopes = false,//在解析服务时检查是否通过根容器来解析Scoped类型的实列
});
//a1:通过根容器创建,需要设ValidateScopes为false(危险)
var a1 = container.GetRequiredService<IOCLife>();
var a2 = container.GetRequiredService<IOCLife>();
using (var scope = container.CreateScope())
{
var a3 = scope.ServiceProvider.GetRequiredService<IOCLife>();
var a4 = scope.ServiceProvider.GetRequiredService<IOCLife>();
Console.WriteLine("scop0:" + a1.ID);
Console.WriteLine("scop1:" + a2.ID);
Console.WriteLine("scop2:" + a3.ID);
Console.WriteLine("scop3:" + a4.ID);
}
4.组件扫描
IServiceCollection扩展类
namespace Microsoft.Extensions.DependencyInjection
{
[AttributeUsage(AttributeTargets.Class)]
public class InjectionAttribute : Attribute
{
public Type? ServiceType { get; set; }
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient;
}
public static class InjectionIServiceCollectionExtensions
{
public static IServiceCollection AddServicesByInjection<T>(this IServiceCollection services)
{
var serviceTypes = typeof(T).Assembly.GetTypes()
.Where(a => a.IsClass)
.Where(a => a.GetCustomAttribute<InjectionAttribute>() != null)//扫描注解
.Where(a => !a.IsAbstract);
foreach (var item in serviceTypes)
{
var injection = item.GetCustomAttribute<InjectionAttribute>();
if (injection!.ServiceType == null)
{
services.Add(new ServiceDescriptor(item, item, injection.Lifetime));
}
else
{
services.Add(new ServiceDescriptor(injection!.ServiceType, item, injection.Lifetime));
}
}
return services;
}
}
}
测试
var services = new ServiceCollection();
services.AddServicesByInjection<Program>();
var bsp = services.BuildServiceProvider();
var connection = bsp.GetServices<IDbConnection>();
5.IOC构造模式
- 构造器的目的和构造函数一样,但是构造器可以提供丰富的api来简化对象的构造
- 构造模式用于简化被构造对象的创建,通过提供一大堆的api来丰富简化构造过程,增加调用者的体验。
- 构造者需要提供一个Build方法用于构建和返回将要构造的对象实列。
- 在容器中一般需要提供一个公开的IServiceCollection类型的属性,用于注册服务。
- IServiceCollection是构造者模式
using Microsoft.Extensions.DependencyInjection;
//List<ServiceDescriptor> list = new List<ServiceDescriptor>();
//list.Add(new ServiceDescriptor(typeof(SqlDbConnection), ServiceLifeTime.Scoped));
//list.Add(new ServiceDescriptor(typeof(MySqlSqlDbConnection), ServiceLifeTime.Scoped));
//var container = new Container(list);
//var containerBuilder = new ContainerBuilder();
//containerBuilder.AddScoped<SqlDbConnection>();
//containerBuilder.AddScoped<MySqlSqlDbConnection>();
//var container1 = containerBuilder.Build();
IServiceCollection containerBuilder = new ServiceCollection();
IServiceProvider sp = containerBuilder.BuildServiceProvider();
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Console.ReadKey();
public interface IDbConnection { }
public class SqlDbConnection { }
public class MySqlSqlDbConnection { }
public enum ServiceLifeTime
{
Transient,
Scoped
}
public class ServiceDescriptor
{
public Type ServiceType { get; set; }
public ServiceLifeTime ServiceLifeTime { get; set; }
public ServiceDescriptor(
Type serviceType,
ServiceLifeTime lifetime
)
{
ServiceType = serviceType;
ServiceLifeTime = lifetime;
}
}
// 目标对象
public interface IContainer { }
// 如果直接创建成本很高,体验很差
public class Container : IContainer
{
public List<ServiceDescriptor> _services = new();
public Container(List<ServiceDescriptor> services)
{
_services = services;
}
}
// 目标对象的构造者
public interface IContainerBuilder
{
// 接口只提供一个通用方法,降低实现成本
void Add(ServiceDescriptor descriptor);
// 构造目标对象
IContainer Build();
}
// 实现构造者
public class ContainerBuilder : IContainerBuilder
{
private List<ServiceDescriptor> _services = new();
public void Add(ServiceDescriptor descriptor)
{
_services.Add(descriptor);
}
public IContainer Build()
{
return new Container(_services);
}
}
// 扩展构造者,提供更加便捷的api
public static class IContainerBuilderExtensions
{
public static void AddTransient<T>(this IContainerBuilder builder)
{
builder.Add(new ServiceDescriptor(typeof(T), ServiceLifeTime.Transient));
}
public static void AddScoped<T>(this IContainerBuilder builder)
{
builder.Add(new ServiceDescriptor(typeof(T), ServiceLifeTime.Scoped));
}
}
6.IOC工厂模式
- 工厂模式侧重于对象的管理(创建销毁),一般提供一个Create方法,支持命名创建。
- 通过上面的学习我们发现IOC有一个弊端,就是他是通过服务类型的解析服务的。有些情况下我们需要通过命名的方式来解析服务。此时可以使用工厂模式。
- IServiceProvider也是工厂模式
// See https://aka.ms/new-console-template for more information
using Microsoft.Extensions.DependencyInjection;
using System;
var services = new ServiceCollection();
services.AddScoped<MySqlDbConnection>();
services.AddScoped<SqlDbConnection>();
services.AddSingleton(sp =>
{
var connections = new Dictionary<string, Type>
{
{ "s1", typeof(SqlDbConnection) },
{ "s2", typeof(MySqlDbConnection) }
};
return new DbConnectionFactory(sp, connections);
});
var sp = services.BuildServiceProvider();
var factory = sp.GetRequiredService<DbConnectionFactory>();
var s1 = factory.Create(sp, "s1");
var s2 = factory.Create(sp, "s2");
Console.WriteLine("Hello, World!");
Console.ReadKey();
public interface IDbConnection
{
}
public class MySqlDbConnection : IDbConnection
{
}
public class SqlDbConnection : IDbConnection
{
}
//如果是一个重量级的工厂,建议注册成单实例
public class DbConnectionFactory
{
private IServiceProvider _serviceProvider;
private Dictionary<string, Type> _connections;
public DbConnectionFactory(
IServiceProvider serviceProvider,
Dictionary<string, Type> connections
)
{
_serviceProvider = serviceProvider;
_connections = connections;
}
public IDbConnection? Create(IServiceProvider serviceProvider, string name)
{
if (_connections.TryGetValue(name, out Type? connectionType))
{
return serviceProvider.GetRequiredService(connectionType) as IDbConnection;
}
return default;
}
}
7.提供模式
- 如果看到提供者模式,说明我们可以提供多个方案,支持多实现
- 一般通过工厂来管理提供者,用以支持命名实列
// See https://aka.ms/new-console-template for more information
using System.Diagnostics;
//List<ILoggerProvider> providers = new List<ILoggerProvider>() {
// new ConsoleLoggerProvider(),
// new DebuggerLoggerProvider()
//};
//var factory = new LoggerFactory(providers);
var factory = new LoggerFactory();
factory.AddProvider(new ConsoleLoggerProvider());
factory.AddProvider(new DebuggerLoggerProvider());
//LoggerFactory loggerFactory = LoggerFactory.Create(new Action<LoggerFactoryBuilder>((loggerBuilder) =>
//{
// loggerBuilder.Add(new ConsoleLoggerProvider());
// loggerBuilder.Add(new DebuggerLoggerProvider());
//}));
var logger = factory.Create("03IO提供模式");
logger.Info("666888");
Console.WriteLine("Hello, World!");
Console.ReadKey();
public interface ILogger
{
void Info(string message);
}
public interface ILoggerProvider
{
ILogger CreateLogger(string name);
}
// 日志提供方案1
public class ConsoleLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string name)
{
return new ConsoleLogger(name);
}
class ConsoleLogger : ILogger
{
private string _name;
public ConsoleLogger(string name)
{
_name = name;
}
public void Info(string message)
{
Console.WriteLine($"{_name}:{message}");
}
}
}
// 日志提供方案2
public class DebuggerLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string name)
{
return new DebuggerLogger(name);
}
class DebuggerLogger : ILogger
{
private string _name;
public DebuggerLogger(string name)
{
_name = name;
}
public void Info(string message)
{
Debug.WriteLine($"{_name}:{message}");
}
}
}
// 代理模式、工厂模式、构造模式、提供模式
public class LoggerFactory
{
private List<ILoggerProvider> _providers = new();
public LoggerFactory() { }
public LoggerFactory(List<ILoggerProvider> providers)
{
_providers = providers;
}
public static LoggerFactory Create(Action<LoggerFactoryBuilder> configure)
{
var builder = new LoggerFactoryBuilder();
configure(builder);
return builder.Build();
}
public void AddProvider(ILoggerProvider provider)
{
_providers.Add(provider);
}
public ILogger Create(string name)
{
var loggers = _providers.Select(s => s.CreateLogger(name));
return new LoggerCollection(loggers);
}
// 代理模式
class LoggerCollection : ILogger
{
private IEnumerable<ILogger> _loggers;
public LoggerCollection(IEnumerable<ILogger> loggers)
{
_loggers = loggers;
}
public void Info(string message)
{
foreach (var logger in _loggers)
{
logger.Info(message);
}
}
}
}
public class LoggerFactoryBuilder
{
private List<ILoggerProvider> _providers = new();
public void Add(ILoggerProvider provider)
{
_providers.Add(provider);
}
public LoggerFactory Build()
{
return new LoggerFactory(_providers);
}
}
8.代理模式
- 代理模式侧重于对目标对象进行加强,通过实现目标对象的接口具备目标对象的能力。
- 一般通过实现和目标对象相同的接口来获得目标对象的能力
- 代理可以通过目标对象来简化实现成本,代理只负责编写加强逻辑
- 一般代理器只代理单个目标对象,我们把下面这个模式也可以归纳到代理模式,因为它能满足代理的许多特点比如加强、拥有目标对象的能力
8.1IList
实现IList:
修改add()方法逻辑
只添加偶数,所以只有2
8.2例子
代理模式不负责实现,侧重目标对象的加强
public interface ILogger
{
void Info(string message);
}
class LoggerCollection : ILogger
{
private IEnumerable<ILogger> _loggers;
public LoggerCollection(IEnumerable<ILogger> loggers)
{
_loggers = loggers;
}
public void Info(string message)
{
foreach (var logger in _loggers)
{
logger.Info(message);
}
}
}
9.IOC容器实现
实现容器有三个重要的对象,通过IContainerBuilder来构建Container实列。Container负责根据ServiceDescriptor(服务描述)来找到服务实现,通过服务实现的依赖来进行注入。
- ServiceDescriptor:负责描述服务信息
- IContainerBuilder:负责构建容器
- IContainer:负责根据服务描述信息解析服务
// See https://aka.ms/new-console-template for more information
IContainerBuilder builder = new ContainerBuilder();
//builder.Add(b => new DbConnection());
//builder.Add<DbConnection>();
builder.Add(b => new DbConnection());
builder.Add<DbContext>();
var container = builder.Build();
//var context = container.GetService(typeof(DbContext));
var context = container.GetService<DbContext>();
Console.WriteLine("Hello, World!");
Console.ReadKey();
// 服务描述信息类
public class ServiceDescriptor
{
public Type ServiceType { get; }
public Type ImplementionType { get; }
public Object? Instance { get; }
public ServiceDescriptor(
Type serviceType,
Type implementionType,
object? instance = null)
{
ServiceType = serviceType;
ImplementionType = implementionType;
Instance = instance;
}
}
// 容器实现接口
public interface IContainer
{
object? GetService(Type serviceType);
T GetService<T>();
}
// 容器生成接口
public interface IContainerBuilder
{
void Add(ServiceDescriptor descriptor);
IContainer Build();
}
// 容器实现类
public class Container : IContainer
{
private IEnumerable<ServiceDescriptor> _services;
public Container(IEnumerable<ServiceDescriptor> services)
{
_services = services;
}
public object? GetService(Type serviceType)
{
var descriptor = _services.FirstOrDefault(s => s.ServiceType == serviceType);
if (descriptor == null)
{
throw new InvalidOperationException("服务未注册");
}
// 判断是否是委托(协变)
var invokerType = typeof(Func<IContainer, object>);
if (descriptor.Instance != null &&
typeof(Func<IContainer, object>).IsInstanceOfType(descriptor.Instance))
{
var func = descriptor.Instance as Func<IContainer, object> ?? throw new ArgumentNullException();
return func(this);
}
// 获取参数最多的构造函数
var constructor = serviceType.GetConstructors().OrderByDescending(s => s.GetParameters().Length).FirstOrDefault() ?? throw new ArgumentNullException();
// 递归解析依赖
// IEnumerable<Object> 只有在ToArray()时执行递归
var parameters = constructor.GetParameters().Select(s => GetService(s.ParameterType));
// 反射
return Activator.CreateInstance(descriptor.ImplementionType, parameters.ToArray());
}
public T GetService<T>()
{
return (T)GetService(typeof(T));
}
}
// 容器生成类
public class ContainerBuilder : IContainerBuilder
{
private List<ServiceDescriptor> _services = new();
public void Add(ServiceDescriptor descriptor)
{
_services.Add(descriptor);
}
public IContainer Build()
{
return new Container(_services);
}
}
// 容器生成类扩展方法
public static class IContainerBuilderExtensions
{
public static void Add<TService>(this IContainerBuilder builder)
{
builder.Add(new ServiceDescriptor(typeof(TService), typeof(TService)));
}
public static void Add<TService, TImplement>(this IContainerBuilder builder)
{
builder.Add(new ServiceDescriptor(typeof(TService), typeof(TImplement)));
}
public static void Add<TService>(this IContainerBuilder builder, Func<IContainer, TService> func)
{
builder.Add(new ServiceDescriptor(typeof(TService), typeof(Func<IContainer, TService>), func));
}
}
public class DbConnection { }
public class DbContext
{
public DbConnection DbConnection { get; }
public DbContext(DbConnection dbConnection)
{
DbConnection = dbConnection;
}
}
10.协变逆变
10.1协变
子类继承父类,所以父类可以等于子类
但是泛型不可以
协变
- 我们说泛型是不完整的,当指定泛型参数时,才使得其完整。
- 在接口或者委托上,在泛型参数上使用out关键字,使得完整泛型,完整的泛型满足泛型参数的多态
IEnumerable:
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
Func:
委托
XieBian<ParentClass> parentClass = null;
XieBian<ChildClass> childClass = null;
parentClass = childClass;
ParentClass parentClass1 = null;
ChildClass childClass1 = null;
parentClass1 = childClass1;
IEnumerable<string> strs = null;
IEnumerable<object> objs = null;
objs = strs;
Func<ParentClass> parentClass2 = null;
Func<ChildClass> childClass2 = null;
parentClass2 = childClass2;
NiBian<ParentClass> parentClass3 = null;
NiBian<ChildClass> childClass3 = null;
childClass3 = parentClass3;
Console.WriteLine("Hello, World!");
Console.ReadKey();
public class ParentClass { }
public class ChildClass : ParentClass { }
public interface NiBian<in T> { }
public interface XieBian<out T> {
10.2逆变
和协变相反,子类等于父类,关键字in
11.Autofac
安装包
Autofac.Extensions.DependencyInjection
var service = new ServiceCollection();
// 微软容器注册服务
service.AddScoped(typeof(ILogger<>), typeof(Logger<>));
// 创建Container容器
var builder = new ContainerBuilder();
// autofac容器注册服务
builder.RegisterType<CService>().PropertiesAutowired().As<CService>().InstancePerLifetimeScope();
// 将IServiceCollection中的服务注册到Autofac
builder.Populate(service);
// 使用AutofacServiceProvider的实现方案,创建容器
// 加载autofac中的服务
IServiceProvider container = new AutofacServiceProvider(builder.Build());
var logger = container.GetRequiredService<ILogger<Program>>();
var cservice = container.GetRequiredService<CService>();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!