Appearance
Autofac 依赖注入与三层架构实践
这是一个在 ASP.NET Core 3.0 环境下,配合 EF Core 和 Autofac 搭建经典三层项目架构的实践总结。
1. 为什么要使用依赖注入框架
- 解耦:实现表现层(Web)、业务层(Service)和数据访问层(Data/Repository)的物理与逻辑解耦。
- 对象生命周期管理:由容器统一管理对象的创建与销毁,避免手动
new对象导致的依赖混乱。 - 提高测试性:方便通过接口进行 Mock 测试。

2. 核心项目结构
建议的经典分层结构如下:
- TinyErp.Web: 表现层(控制器、视图/API)。
- TinyErp.Service: 业务逻辑层。
- TinyErp.Data: 数据访问层(EF Core DbContext、Repository 接口实现)。
- TinyErp.Model: 实体模型层。

3. Autofac 核心配置流程
3.1 启用 Autofac 服务工厂
在 Program.cs 中,通过 .UseServiceProviderFactory(new AutofacServiceProviderFactory()) 替换默认的容器工厂。
csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, loggingBuilder) =>
{
// 日志配置...
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// 关键:启用 Autofac
.UseServiceProviderFactory(new AutofacServiceProviderFactory());3.2 批量注册逻辑
在 Startup.cs 中添加 ConfigureContainer 方法。这是 Autofac 独有的配置入口,支持程序集扫描(Assembly Scanning)。
csharp
public void ConfigureContainer(ContainerBuilder builder)
{
// 1. 加载程序集
Assembly service = Assembly.Load("TinyErp.Service");
Assembly data = Assembly.Load("TinyErp.Data");
// 2. 程序集扫描注册(按命名约定)
builder.RegisterAssemblyTypes(service, data)
.Where(t => (t.FullName.EndsWith("Service") || t.Name.EndsWith("Repository")) && !t.IsAbstract)
.InstancePerLifetimeScope() // 生命周期:作用域内单例
.AsImplementedInterfaces() // 自动映射到接口
.PropertiesAutowired(); // 开启属性注入
// 3. 泛型仓储注册
builder.RegisterGeneric(typeof(GenericRepository<>))
.As(typeof(IGenericRepository<>))
.InstancePerLifetimeScope();
// 4. 工作单元与基础上下文注册
builder.RegisterType(typeof(TinyErpDbContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerLifetimeScope();
// 5. 如果需要在 Controller 中使用属性注入,需显式注册控制器
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
.PropertiesAutowired();
}4. EF Core 集成要点
4.1 数据库初始化
在 Program.cs 启动时进行数据库检查与初始化。
csharp
private static void CreateDbIfNotExists(IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<TinyErpDbContext>();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "数据库初始化失败");
}
}
}4.2 DbContext 定义
csharp
public class TinyErpDbContext : DbContext
{
public TinyErpDbContext(DbContextOptions<TinyErpDbContext> options) : base(options) { }
public DbSet<Student> Students { get; set; }
// ... 其他 DbSet
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("Student");
}
}5. 常见坑点与踩坑笔记
5.1 子类继承父类的构造函数问题
在实现 Service 层基类继承时,子类必须显式调用父类的构造函数并传递相应参数。
csharp
public class StudentService : EntityService<Student>, IStudentService
{
public IUnitOfWork unitOfWork;
public IGenericRepository<Student> studentRepository;
public StudentService(IUnitOfWork unitOfWork, IGenericRepository<Student> studentRepository)
: base(unitOfWork, studentRepository) // 必须调用 base
{
this.unitOfWork = unitOfWork;
this.studentRepository = studentRepository;
}
}5.2 属性注入失效
如果发现属性注入(PropertiesAutowired)不生效,通常是因为:
- 对象不是通过容器创建的(使用了
new)。 - 控制器没有显式在
ConfigureContainer中注册。
