IoC 自动注入:让依赖注册不再重复劳动
在 ASP.NET Core 中,IoC(控制反转)功能通过依赖注入(DI)实现。ASP.NET Core 有一个内置的依赖注入容器,可以自动完成依赖注入。
我们可以结合反射、特性或程序集扫描来实现自动注册服务。
实现自动依赖注入的方法
需要做到两件事:
- 1. 通过程序集扫描注册服务
- 2. 按照接口与实现的命名规则注册服务
接口名:IRepository
实现类名:Repository
比如接口名为 IRepository,实现类名为 Repository。
下面介绍两种实现方法。
方法一:通过特性标记实现自动注册
使用自定义特性来标记需要注册的类。
创建特性:
[AttributeUsage(AttributeTargets.Class)]
public class AutoInjectAttribute : Attribute
{
public ServiceLifetime Lifetime { get; }
public AutoInjectAttribute(ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
Lifetime = lifetime;
}
}
应用特性到类:
[AutoInject(ServiceLifetime.Scoped)]
public class Repository : IRepository
{
// 类实现
}
注册代码:
public static class ServiceCollectionExtensions
{
public static void AddAutoInjectServices(this IServiceCollection services, Assembly assembly)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
var autoInjectAttribute = type.GetCustomAttribute<AutoInjectAttribute>();
if (autoInjectAttribute != null)
{
var interfaces = type.GetInterfaces();
foreach (var @interface in interfaces)
{
if (@interface.Name == "I" + type.Name)
{
Console.WriteLine(#34;注册服务:{@interface.Name} -> {type.Name},生命周期:{attribute.Lifetime}");
switch (autoInjectAttribute.Lifetime)
{
case ServiceLifetime.Singleton:
services.AddSingleton(@interface, type);
break;
case ServiceLifetime.Scoped:
services.AddScoped(@interface, type);
break;
case ServiceLifetime.Transient:
services.AddTransient(@interface, type);
break;
}
}
}
}
}
}
}
使用方式:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoInjectServices(Assembly.GetExecutingAssembly());
}
}
这样就可以通过特性控制哪些类需要注册,并指定生命周期。
方法二:按命名规则自动注册
可以通过类名和接口名的命名规则来自动注册服务。
注册代码:
public static class ServiceCollectionExtensions
{
public static void AddAutoInjection(this IServiceCollection services, Assembly assembly)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
var interfaces = type.GetInterfaces();
foreach (var @interface in interfaces)
{
if (@interface.Name == "I" + type.Name)
{
services.AddScoped(@interface, type);
}
}
}
}
}
使用方式:
在 Startup.cs 或 Program.cs 中调用方法:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoInjection(Assembly.GetExecutingAssembly());
}
}
这样就可以自动注册当前程序集中符合命名规则的服务。
优化
建议
- o 支持多个程序集扫描;
- o 添加配置选项,灵活控制注册行为;
- o 增加单元测试验证注册逻辑是否正确。
- o 注册过程添加日志,利于调试。
特性注册
- o 重复扫描类型:每次调用都重新获取程序集类型,建议缓存。
- o 只能识别 I+类名 接口,建议支持更多命名格式。
- o 特性注册不支持泛型接口:如 IRepository<T> 无法识别。
public static class ServiceCollectionExtensions
{
private static readonly ConcurrentDictionary<Assembly, Type[]> _cachedTypes = new();
public static void AddAutoInjectServices(this IServiceCollection services, Assembly assembly)
{
if (!_cachedTypes.TryGetValue(assembly, out var types))
{
types = assembly.GetTypes();
_cachedTypes.TryAdd(assembly, types);
}
foreach (var type in types)
{
try
{
if (!type.IsClass || type.IsAbstract || type.IsGenericType)
continue;
var attribute = type.GetCustomAttribute<AutoInjectAttribute>();
if (attribute == null)
continue;
var interfaces = type.GetInterfaces();
foreach (var @interface in interfaces)
{
if (@interface.Name != "I" + type.Name &&
!@interface.Name.StartsWith("I") &&
!@interface.Name.EndsWith("Service"))
continue;
switch (attribute.Lifetime)
{
case ServiceLifetime.Singleton:
services.AddSingleton(@interface, type);
break;
case ServiceLifetime.Scoped:
services.AddScoped(@interface, type);
break;
case ServiceLifetime.Transient:
services.AddTransient(@interface, type);
break;
}
Console.WriteLine(#34;注册服务:{@interface.Name} -> {type.Name},生命周期:{attribute.Lifetime}");
}
}
catch (Exception ex)
{
Console.WriteLine(#34;注册失败: {type.FullName},错误:{ex.Message}");
}
}
}
}
命名规则注册
- o 只支持 Scoped 生命周期,应支持 Singleton/Transient。
- o 没有过滤机制:可能误注册不需要的服务。
public static void AddAutoInjection(this IServiceCollection services, Assembly assembly)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
try
{
if (type.IsClass && !type.IsAbstract && !type.IsGenericType)
{
var interfaces = type.GetInterfaces();
foreach (var @interface in interfaces)
{
if (@interface.Name == "I" + type.Name && @interface.IsAssignableFrom(type))
{
services.AddScoped(@interface, type);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(#34;注册 {type.FullName} 失败:{ex.Message}");
}
}
}
相关文章
- 网络爬虫:数据抓取的几种方法
- 计算机信息系统集成服务合同(中英文对照)
- 3D 渲染视频带你游览苹果"太空船"新总部园区 ...
- 微软发布SQL Server 2012 Service Pack 2
- 将数据从 MySQL 导出到 SQL Server
- Sugon and Hygon Announce Strategic Restructuring to Strengthen China's IT Industry Leadership
- 闪送如何授权并无需API集成连接其他系统
- 工业私有云如何安装PlantPAX
- (1图)美军:全球信息基础设施
- 隐形触手无处不在!智能家居、物联网:Java正在悄悄渗透你的生活