using Microsoft.Extensions.DependencyInjection;
// ReSharper disable ClassNeverInstantiated.Local
// ReSharper disable CollectionNeverUpdated.Local
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace Dpz.Core.Service;
public static class ServiceDependencyInjection
{
public static IServiceCollection AddDefaultServices(
this IServiceCollection services,
IConfiguration configuration
)
{
var injectServices = configuration.GetSection("RegisterInject").Get<List<RegisterInject>>();
if (injectServices == null || injectServices.Count == 0)
{
return services;
}
foreach (var injectService in injectServices)
{
if (string.IsNullOrEmpty(injectService.InterfaceAssemblyName))
{
continue;
}
var injectTypes = Assembly
.Load(injectService.InterfaceAssemblyName)
.GetTypes()
.Where(x => x.Namespace == injectService.InterfaceNamespace && x.IsInterface)
.ToList();
if (injectService.Remove is { Count: > 0 })
{
injectTypes = injectTypes
.Where(x => x.FullName != null && !injectService.Remove.Contains(x.FullName))
.ToList();
}
if (string.IsNullOrEmpty(injectService.ImplementAssemblyName))
{
continue;
}
var implementAssembly = Assembly
.Load(injectService.ImplementAssemblyName)
.GetTypes()
.Where(x =>
x.Namespace == injectService.ImplementNamespace
&& !x.IsAbstract
&& !x.IsInterface
)
.ToList();
foreach (var injectType in injectTypes)
{
var defaultImplementType = implementAssembly.FirstOrDefault(x =>
injectType.IsAssignableFrom(x)
);
if (defaultImplementType != null)
{
services.AddScoped(injectType, defaultImplementType);
}
}
if (injectService.Add is { Count: > 0 })
{
foreach (var addService in injectService.Add)
{
var injectType = injectTypes.FirstOrDefault(x =>
x.FullName == addService.ServiceFullName
);
var implementType = implementAssembly.FirstOrDefault(x =>
x.FullName == addService.ImplementFullName
);
if (
injectType == null
|| implementType == null
|| !injectType.IsAssignableFrom(implementType)
)
{
continue;
}
switch (addService.Type)
{
case "Transient":
services.AddTransient(injectType, implementType);
break;
case "Singleton":
services.AddSingleton(injectType, implementType);
break;
default:
services.AddScoped(injectType, implementType);
break;
}
}
}
}
return services;
}
private class RegisterInject
{
/// <summary>
/// 接口程序集名称
/// </summary>
public string? InterfaceAssemblyName { get; set; }
/// <summary>
/// 实现程序集名称
/// </summary>
public string? ImplementAssemblyName { get; set; }
/// <summary>
/// 接口命名空间
/// </summary>
public string? InterfaceNamespace { get; set; }
/// <summary>
/// 实现命名空间
/// </summary>
public string? ImplementNamespace { get; set; }
/// <summary>
/// 要移除注册的接口完全限定名
/// </summary>
public List<string>? Remove { get; set; }
/// <summary>
/// 要添加的自定义注入类型
/// </summary>
public List<AddService>? Add { get; set; }
}
private class AddService
{
/// <summary>
/// 注入类型:Transient Scoped(default) Singleton
/// </summary>
public string? Type { get; set; }
/// <summary>
/// 依赖注入服务接口完全限定名
/// </summary>
public string? ServiceFullName { get; set; }
/// <summary>
/// 实现完全限定名
/// </summary>
public string? ImplementFullName { get; set; }
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个用于 .NET 依赖注入的扩展方法实现,主要功能是通过配置文件自动注册服务。让我详细解释一下这个代码:
主要功能
这个静态类提供了一个扩展方法 AddDefaultServices,用于根据配置文件自动批量注册依赖注入服务,而不需要手动一个个注册。
核心流程
1. 配置读取
var injectServices = configuration.GetSection("RegisterInject").Get<List<RegisterInject>>();
从配置文件的 "RegisterInject" 节点读取注入配置列表。
2. 接口类型发现
var injectTypes = Assembly
.Load(injectService.InterfaceAssemblyName)
.GetTypes()
.Where(x => x.Namespace == injectService.InterfaceNamespace && x.IsInterface)
.ToList();
- 加载指定的接口程序集
- 筛选出指定命名空间下的所有接口类型
3. 移除不需要的接口
if (injectService.Remove is { Count: > 0 })
{
injectTypes = injectTypes
.Where(x => x.FullName != null && !injectService.Remove.Contains(x.FullName))
.ToList();
}
根据配置中的 Remove 列表,排除不需要注册的接口。
4. 实现类型发现
var implementAssembly = Assembly
.Load(injectService.ImplementAssemblyName)
.GetTypes()
.Where(x =>
x.Namespace == injectService.ImplementNamespace
&& !x.IsAbstract
&& !x.IsInterface
)
.ToList();
加载实现程序集,找到指定命名空间下的所有具体实现类。
5. 自动匹配注册
foreach (var injectType in injectTypes)
{
var defaultImplementType = implementAssembly.FirstOrDefault(x =>
injectType.IsAssignableFrom(x)
);
if (defaultImplementType != null)
{
services.AddScoped(injectType, defaultImplementType);
}
}
自动匹配接口和实现类,默认以 Scoped 生命周期注册。
6. 自定义注册
if (injectService.Add is { Count: > 0 })
{
// 根据配置的 Add 列表进行自定义注册
// 支持 Transient、Scoped、Singleton 三种生命周期
}
处理配置中的自定义注册规则,可以指定特定的接口-实现对和生命周期。
配置模型
RegisterInject 类
InterfaceAssemblyName: 接口所在程序集名称ImplementAssemblyName: 实现类所在程序集名称InterfaceNamespace: 接口命名空间ImplementNamespace: 实现类命名空间Remove: 要排除的接口完全限定名列表Add: 自定义注册配置列表
AddService 类
Type: 注入生命周期类型(Transient/Scoped/Singleton)ServiceFullName: 服务接口完全限定名ImplementFullName: 实现类完全限定名
使用场景
这个工具特别适用于:
- 大型项目:有大量接口和实现需要注册
- 模块化架构:不同程序集中的服务需要批量注册
- 配置驱动:希望通过配置文件控制服务注册,而不是硬编码
- 约定优于配置:遵循命名约定的接口和实现可以自动匹配
优势
- 减少样板代码:避免手动注册大量服务
- 配置灵活:可以通过配置文件控制注册行为
- 支持排除和自定义:既有自动匹配,也支持精确控制
- 生命周期可控:支持三种不同的依赖注入生命周期
评论加载中...