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: 实现类完全限定名

使用场景

这个工具特别适用于:

  1. 大型项目:有大量接口和实现需要注册
  2. 模块化架构:不同程序集中的服务需要批量注册
  3. 配置驱动:希望通过配置文件控制服务注册,而不是硬编码
  4. 约定优于配置:遵循命名约定的接口和实现可以自动匹配

优势

  • 减少样板代码:避免手动注册大量服务
  • 配置灵活:可以通过配置文件控制注册行为
  • 支持排除和自定义:既有自动匹配,也支持精确控制
  • 生命周期可控:支持三种不同的依赖注入生命周期
评论加载中...