WPF Core应用使用Caliburn.Micro时如何按请求注入AppDbContext
问题根因
- 重复注册同类型服务:同时调用
_container.Instance(_db)和_container.PerRequest<AppDbContext, AppDbContext>()时,会为AppDbContext类型注册两个无key服务条目。SimpleContainer解析无key服务时默认仅匹配单个注册条目,存在多条时就会抛出Sequence contains more than one element异常。 - 依赖缺失导致实例化失败:单独使用
PerRequest<AppDbContext, AppDbContext>()注册时空引用,是因为AppDbContext的构造函数需要DbContextOptions<AppDbContext>类型参数,但该依赖未注册到容器,SimpleContainer无法完成构造注入,导致实例创建失败。 - 预生成全局DbContext实例的逻辑本身不符合需求:单例DbContext会存在缓存脏读、并发冲突等问题,和「每个请求独立DbContext实例」的目标完全冲突,不需要保留提前创建的
_db实例。
修复步骤
- 移除Bootstrapper中预构造
AppDbContext的私有字段_db及相关实例化逻辑,不需要提前创建DbContext实例。 - 将
DbContextOptions<AppDbContext>数据库配置作为单例注册到容器,该配置线程安全,可被所有DbContext实例复用。 - 删除
_container.Instance(_db)这行重复注册代码,仅保留PerRequest生命周期的AppDbContext注册。 - 修正原连接字符串中数据库路径的多余空格,避免数据库连接失败。
修正后的Bootstrapper代码
public class Bootstrapper : BootstrapperBase { private SimpleContainer _container = new SimpleContainer(); public Bootstrapper() { Initialize(); ConventionManager.AddElementConvention<PasswordBox>( PasswordBoxHelper.BoundPasswordProperty, "Password", "PasswordChanged"); } protected override void Configure() { _container.Instance(_container); // 注册DbContext配置,单例生命周期 var options = new DbContextOptionsBuilder<AppDbContext>() .UseJet(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=F:\AccessDatabase\TestDatabase1.accdb;") .Options; _container.Instance(options); _container.Singleton<IWindowManager, WindowManager>(); _container.Singleton<IEventAggregator, EventAggregator>(); _container.PerRequest<DataAccess, DataAccess>(); // 仅保留按请求生命周期的AppDbContext注册,不需要额外注册预生成实例 _container.PerRequest<AppDbContext, AppDbContext>(); GetType().Assembly.GetTypes() .Where(type => type.IsClass) .Where(type => type.Name.EndsWith("ViewModel")) .ToList() .ForEach(viewModelType => _container.RegisterPerRequest( viewModelType, viewModelType.ToString(), viewModelType)); } protected override async void OnStartup(object sender, StartupEventArgs e) { await DisplayRootViewForAsync<ShellViewModel>(); } protected override object GetInstance(Type service, string key) { return _container.GetInstance(service, key); } protected override IEnumerable<object> GetAllInstances(Type service) { return _container.GetAllInstances(service); } protected override void BuildUp(object instance) { _container.BuildUp(instance); } }
注意事项
- SimpleContainer不会自动释放PerRequest生命周期的服务,
AppDbContext作为占用数据库连接的非托管类型,使用完后需要手动调用Dispose()释放:如果是在ViewModel中长期注入持有,可在ViewModel的关闭/失活逻辑中释放;如果是临时使用,建议用using语句包裹解析逻辑,自动释放资源。 - 所有依赖AppDbContext的服务(含DataAccess、ViewModel)必须通过容器解析,不要手动调用构造函数new实例,否则容器无法完成依赖注入,依然会出现空引用问题。
内容的提问来源于stack exchange,提问作者Kiarash Kanani
相关产品推荐
相关产品推荐

