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

.NET带Repository与DI的三层架构(表现、业务、数据)正确实现问询

三层架构结合Repository模式与DI的实现方案

核心架构依赖规则

首先明确符合要求的架构边界:

  • 业务逻辑层(BL):自包含,仅定义业务逻辑与IRepository抽象接口,不依赖表现层(Presentation)和数据访问层(DAL)
  • 数据访问层(DAL):仅依赖BL,实现BL定义的IRepository接口,负责具体数据操作
  • 表现层(Presentation):仅依赖BL,作为应用入口和组合根(DI注册处),不直接依赖DAL

解决Repository注册的可行方案

方案1:运行时动态加载DAL程序集自动注册

通过硬编码(或配置文件配置)DAL程序集名称,在组合根中动态加载程序集,再利用DI容器的扫描功能自动注册所有实现IRepository的类型。

  • 优势:严格遵循依赖倒置原则,Presentation完全无编译时依赖DAL,符合架构设计边界
  • 劣势:依赖程序集名称的准确性,名称变更需同步调整;动态加载可能增加调试复杂度
  • 示例代码(以.NET为例):
// Presentation层组合根(如Program.cs)
var dalAssembly = Assembly.Load("MyApp.DAL");
services.Scan(scan => scan
    .FromAssemblies(dalAssembly)
    .AddClasses(classes => classes.AssignableTo(typeof(IRepository<>)))
    .AsImplementedInterfaces()
    .WithScopedLifetime());

方案2:Presentation引用DAL但隐藏内部类型

让Presentation编译时引用DAL,但将DAL中所有具体Repository类设为internal,仅暴露一个空的标记类(如DalMarker)用于定位程序集,再通过扫描实现自动注册。

  • 优势:无需硬编码程序集名称,通过标记类定位更可靠;Presentation无法直接访问DAL内部实现,避免依赖泄漏
  • 劣势:存在编译时依赖,严格来说打破了"Presentation不依赖DAL"的逻辑边界,但运行时仍符合依赖倒置
  • 示例代码:
// DAL层定义标记类
public class DalMarker { }

// DAL层Repository实现(internal隐藏)
internal class UserRepository : IRepository<User> 
{ 
    // 实现逻辑
}

// Presentation层组合根
var dalAssembly = typeof(DalMarker).Assembly;
services.Scan(scan => scan
    .FromAssemblies(dalAssembly)
    .AddClasses(classes => classes.AssignableTo(typeof(IRepository<>)))
    .AsImplementedInterfaces()
    .WithScopedLifetime());

补充方案:独立DI注册模块

在DAL层定义一个公开的静态注册类,封装所有Repository的注册逻辑,Presentation通过反射调用该类的注册方法,无需直接引用DAL。

  • 优势:既保持Presentation无编译时依赖,又将注册逻辑封装在DAL内部,符合单一职责
  • 示例代码:
// DAL层注册类
public static class DalDependencyInjection
{
    public static IServiceCollection AddDalRepositories(this IServiceCollection services)
    {
        services.AddScoped<IRepository<User>, UserRepository>();
        // 其他Repository注册逻辑
        return services;
    }
}

// Presentation层组合根
var dalAssembly = Assembly.Load("MyApp.DAL");
var registrationType = dalAssembly.GetType("MyApp.DAL.DalDependencyInjection");
var addMethod = registrationType.GetMethod("AddDalRepositories", BindingFlags.Public | BindingFlags.Static);
addMethod.Invoke(null, new object[] { services });

关于架构图与代码不一致的问题说明

很多技术文章(包括微软文档)的架构图展示的是逻辑依赖关系(仅运行时依赖DAL),但示例代码为了简化演示,会让Presentation直接引用DAL并手动注册具体实现。这种做法虽然便捷,但违背了依赖倒置原则,实际生产中应尽量避免。需明确:逻辑上Presentation仅依赖BL的抽象,运行时才需要DAL的实现,编译时应尽量弱化甚至消除对DAL的依赖。

选型建议

  • 若严格遵循架构边界,优先选择方案1或独立DI注册模块;
  • 若追求开发便捷性,可接受弱编译时依赖,选择方案2;
  • 禁止直接让Presentation引用DAL并手动注册具体Repository,避免依赖泄漏,破坏架构设计初衷。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:20:26