You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6清洁架构中如何合规注册基础设施层依赖?

解决清洁架构下基础设施层依赖注册的问题

核心矛盾是清洁架构要求表现层(Web API)仅依赖应用层,不能直接引用基础设施层,但基础设施层的服务注册逻辑又需要被触发。以下是两种合规的解决方案:

方案一:基于接口的依赖注册扩展(推荐)

通过在应用层定义抽象注册接口,让基础设施层实现该接口,再由应用层统一扫描执行注册,完全隔离表现层与基础设施层的直接依赖。

步骤1:在应用层定义抽象注册接口

namespace JwtWithIdentityDemo.Application.IoC
{
    public interface IDependencyRegistrar
    {
        void RegisterServices(IServiceCollection services, IConfiguration configuration);
    }
}

步骤2:在基础设施层实现该接口

把原有的AddInfrastructure逻辑迁移到接口实现中:

namespace JwtWithIdentityDemo.Infrastructure.IoC
{
    public class InfrastructureDependencyRegistrar : IDependencyRegistrar
    {
        public void RegisterServices(IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<JwtSetings>(configuration.GetSection(JwtSetings.SectionName));
            services.AddSingleton<IJwtTokenGenerator, JwtTokenGenerator>();
            services.AddTransient<IUserManagerWrapper, UserManagerWrapper>();

            // Identity配置
            services.AddIdentity<IdentityUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddScoped<UserManager<IdentityUser>>();

            // EF Core配置
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(configuration.GetConnectionString("JwtWithIdentityDemoDb"),
                    sqlServerOptions => sqlServerOptions.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)
                        .EnableRetryOnFailure(
                            maxRetryCount: 5,
                            maxRetryDelay: TimeSpan.FromSeconds(30),
                            errorNumbersToAdd: null)
                ),
                ServiceLifetime.Transient
            );
        }
    }
}

步骤3:在应用层实现扫描与注册逻辑

创建应用层的扩展方法,自动扫描所有实现IDependencyRegistrar的类型并执行注册:

namespace JwtWithIdentityDemo.Application.IoC
{
    public static class ApplicationDependencyRegistration
    {
        public static void AddApplicationWithDependencies(this IServiceCollection services, IConfiguration configuration)
        {
            // 注册应用层自身服务(比如MediatR)
            services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ApplicationDependencyRegistration>());
            
            // 扫描并执行所有依赖注册器
            var registrars = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(assembly => assembly.GetTypes())
                .Where(type => typeof(IDependencyRegistrar).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                .Select(Activator.CreateInstance)
                .Cast<IDependencyRegistrar>();

            foreach (var registrar in registrars)
            {
                registrar.RegisterServices(services, configuration);
            }
        }
    }
}

步骤4:表现层仅调用应用层方法

在Program.cs中只需调用应用层的扩展方法,无需引用基础设施层:

var builder = WebApplication.CreateBuilder(args);
{
    builder.Services.AddApplicationWithDependencies(builder.Configuration);
    builder.Services.AddControllers();

    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
}

注意:确保基础设施层的程序集在运行时能被加载(部署时包含该DLL,或在启动项目依赖中设置为「复制到输出目录」)。

方案二:反射直接调用基础设施层注册方法

若不想定义接口,可通过反射直接调用基础设施层的AddInfrastructure方法,同样避免直接引用:

var builder = WebApplication.CreateBuilder(args);
{
    builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>());
    builder.Services.AddApplication(builder.Configuration);
    builder.Services.AddControllers();

    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    // 反射调用基础设施层的注册方法
    var infrastructureAssembly = AppDomain.CurrentDomain.Load("JwtWithIdentityDemo.Infrastructure");
    var registerType = infrastructureAssembly.GetType("JwtWithIdentityDemo.Infrastructure.IoC.RegisterInfrastructure");
    var addInfrastructureMethod = registerType.GetMethod("AddInfrastructure", BindingFlags.Public | BindingFlags.Static);
    addInfrastructureMethod?.Invoke(null, new object[] { builder.Services, builder.Configuration });
}

方案对比

  • 方案一:符合面向接口设计,扩展性强,新增基础设施实现(如替换数据库)时只需新增接口实现,无需修改上层代码。
  • 方案二:实现简洁,适合小型项目,但需硬编码程序集和类型名称,灵活性较差。

内容的提问来源于stack exchange,提问作者Rakesh Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:59:58