目录
-
简介
-
什么是依赖注入(DI)?
-
为什么选择 Autofac?
-
WinForms 与 DI 的结合场景
-
-
环境准备
-
安装 Autofac NuGet 包
-
项目初始化配置
-
-
基础用法
-
容器配置与启动
-
注册服务与组件
-
在窗体中注入依赖
-
-
高级用法
-
模块化注册(Module)
-
生命周期管理
-
属性注入与方法注入
-
-
最佳实践
-
如何组织代码结构
-
避免常见陷阱
-
-
完整示例
-
一个简单的 WinForms 应用 Demo
-
-
常见问题解答
1. 简介
什么是依赖注入(DI)?
依赖注入是一种设计模式,通过将对象的创建和依赖关系从代码中解耦,提高可维护性和可测试性。容器(如 Autofac)负责管理依赖的生命周期。
为什么选择 Autofac?
-
功能丰富:支持构造函数注入、属性注入、模块化配置等。
-
高性能:优化过的解析逻辑,适合桌面应用。
-
社区活跃:文档完善,问题易解决。
WinForms 与 DI 的结合场景
传统 WinForms 应用通常直接实例化窗体和服务,导致代码耦合度高。通过 Autofac 可以实现:
-
窗体依赖的服务自动注入。
-
统一管理数据库上下文、配置等共享资源。
2. 环境准备
步骤 1:安装 Autofac NuGet 包
通过 NuGet 包管理器或命令行安装以下包:
Install-Package Autofac
Install-Package Autofac.Extensions.DependencyInjection
步骤 2:修改 Program.cs
文件
WinForms 默认隐藏 Program.cs
,需在解决方案资源管理器中取消隐藏(右键项目 → 属性 → 启用应用程序框架)。
修改 Program.cs
代码:
static class Program
{
private static IContainer _container;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 配置 Autofac 容器
var builder = new ContainerBuilder();
builder.RegisterType<MainForm>().InstancePerDependency();
builder.RegisterType<UserService>().As<IUserService>().SingleInstance();
_container = builder.Build();
// 通过容器解析主窗体
using (var scope = _container.BeginLifetimeScope())
{
var mainForm = scope.Resolve<MainForm>();
Application.Run(mainForm);
}
}
}
3. 基础用法
注册服务与组件
在容器构建器 ContainerBuilder
中注册服务:
builder.RegisterType<FileLogger>().As<ILogger>().SingleInstance();
builder.RegisterType<DatabaseContext>().InstancePerLifetimeScope();
在窗体中注入依赖
假设窗体 MainForm
需要 IUserService
服务:
public partial class MainForm : Form
{
private readonly IUserService _userService;
// 构造函数注入
public MainForm(IUserService userService)
{
_userService = userService;
InitializeComponent();
}
}
4. 高级用法
模块化注册(Module)
通过 Autofac.Module
组织注册逻辑:
public class ServicesModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<OrderService>().As<IOrderService>();
builder.RegisterModule<LoggingModule>();
}
}
// 在 Program.cs 中加载模块
builder.RegisterModule<ServicesModule>();
生命周期管理
-
SingleInstance:全局单例。
-
InstancePerLifetimeScope:每个生命周期范围一个实例。
-
InstancePerDependency:每次解析新实例。
属性注入
若需在窗体设计器中正常显示,可使用属性注入:
public partial class MainForm : Form
{
[Inject] // 需引入 Autofac 命名空间
public ILogger Logger { get; set; }
}
// 注册时启用属性注入
builder.RegisterType<MainForm>().PropertiesAutowired();
5. 最佳实践
-
避免在窗体构造函数中调用依赖服务:
依赖未完全初始化时可能引发异常,建议在OnLoad
事件中操作。 -
生命周期匹配:
数据库上下文建议使用InstancePerLifetimeScope
,避免内存泄漏。 -
模块化设计:
按功能拆分模块(如UserModule
、OrderModule
),提高可维护性。
6. 完整示例
Demo 场景
-
主窗体显示用户列表。
-
通过
UserService
获取数据。
代码实现1
-
服务定义:
public interface IUserService
{
List<string> GetUsers();
}
public class UserService : IUserService
{
public List<string> GetUsers() => new List<string> { "Alice", "Bob" };
}
-
主窗体代码:
public partial class MainForm : Form
{
private readonly IUserService _userService;
public MainForm(IUserService userService)
{
_userService = userService;
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
dataGridView1.DataSource = _userService.GetUsers();
}
}
代码实现2
internal static class Program
{
public static IContainer Container { get; set; }
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 创建并配置 Autofac 容器
var builder = new ContainerBuilder();
// 注册你的服务或依赖项
builder.RegisterType<MEMMService>().As<IMEMMService>().InstancePerDependency();
builder.RegisterType<RePrintReceiptsService>().As<IRePrintReceiptsService>().InstancePerDependency();
builder.RegisterType<frmManagerSigned>().As<IfrmManagerSigned>().InstancePerDependency();
builder.RegisterType<frmRePrint>().As<IfrmRePrint>().InstancePerDependency();
builder.RegisterType<frmHistoryApplication>().As<IfrmHistoryApplication>().InstancePerDependency();
//builder.RegisterType<MainForm>().SingleInstance(); // 主窗体注册为单例
//获取所有窗体类型
var baseType = typeof(Form);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => baseType.IsAssignableFrom(t) && t != baseType).AsImplementedInterfaces()
.InstancePerDependency()
.Named(t => t.Name, typeof(Form));
// 构建容器
Container = builder.Build();
AppInfo.Container = Container;
Application.Run(Container.ResolveNamed<Form>("frmLogin"));
//Application.Run(Container.ResolveNamed<Form>("frmLogin"));
}
}
public class AppInfo
{
public static IContainer Container { get; set; }
}
7. 常见问题解答
Q1:窗体设计器无法加载?
-
确保窗体有默认无参构造函数(设计器使用)。
-
使用属性注入替代构造函数注入。
Q2:如何解决循环依赖?
-
检查服务设计,提取公共逻辑到新接口。
-
使用
Lazy<T>
延迟初始化。
Q3:Autofac 与其他 DI 容器对比?
-
对比 Simple Injector:Autofac 对高级场景支持更好。
-
对比 Microsoft.Extensions.DependencyInjection:Autofac 功能更丰富。