WPF MVVM全局异常处理器如何注入ErrorLog上下文?
我来帮你搞定这个问题!你遇到的核心问题是App.xaml.cs的构造时机早于Prism容器的初始化,所以直接构造注入肯定拿不到实例。咱们一步步来解决:
解决方案步骤
1. 确保ErrorLog服务已正确注册到Prism容器
首先,更推荐为ErrorLog单独定义服务接口(避免和业务模型的IModel混淆,符合单一职责原则),然后在Prism容器中完成注册。
先定义IErrorLogService:
public interface IErrorLogService : IDisposable { void CreateErrorLog(ErrorLog errorLog); }
接着实现对应的服务类(基于你Code First生成的ErrorLog模型):
public class ErrorLogService : IErrorLogService { private readonly YourDbContext _dbContext; // 替换成你实际的数据库上下文类 public ErrorLogService(YourDbContext dbContext) { _dbContext = dbContext; } public void CreateErrorLog(ErrorLog errorLog) { _dbContext.ErrorLogs.Add(errorLog); _dbContext.SaveChanges(); } public void Dispose() { _dbContext?.Dispose(); } }
然后在你的Prism Bootstrapper(比如UnityBootstrapper或DryIocBootstrapper)的RegisterTypes方法中注册服务:
protected override void RegisterTypes(IContainerRegistry containerRegistry) { // 注册你的业务模型服务(保留你原有的代码) containerRegistry.Register<IModel, ModelService>(); // 注册ErrorLog服务,用单例生命周期减少上下文实例开销 containerRegistry.RegisterSingleton<IErrorLogService, ErrorLogService>(); // 别忘了注册数据库上下文(如果之前没注册的话) containerRegistry.Register<YourDbContext>(); }
2. 在App.xaml.cs中延迟获取服务实例
因为App的构造函数会在Prism容器初始化前执行,所以不能直接通过构造注入拿到服务。我们需要在Prism初始化完成后,通过容器实例获取服务:
修改App.xaml.cs代码:
public partial class App : Application { private IErrorLogService _errorLogService; private Bootstrapper _bootstrapper; // 替换成你实际的Bootstrapper类 protected override void OnStartup(StartupEventArgs e) { // 先初始化Prism Bootstrapper,完成容器注册 _bootstrapper = new Bootstrapper(); _bootstrapper.Run(); // 此时容器已就绪,获取ErrorLog服务实例 _errorLogService = _bootstrapper.Container.Resolve<IErrorLogService>(); // 也可以用ServiceLocator(如果你的Bootstrapper支持): // _errorLogService = ServiceLocator.Current.GetInstance<IErrorLogService>(); base.OnStartup(e); } private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { // 构造ErrorLog实体 var errorLog = new ErrorLog { ErrorMessage = e.Exception.Message, StackTrace = e.Exception.StackTrace, OccurredTime = DateTime.Now // 补充你模型中需要的其他字段 }; try { // 保存异常日志到数据库 _errorLogService.CreateErrorLog(errorLog); } catch (Exception ex) { // 容错处理:如果数据库保存失败,写入本地文件避免丢失日志 File.AppendAllText( "FallbackErrorLog.txt", $"[{DateTime.Now}] 日志保存失败:{ex.Message}\r\n原始异常:{e.Exception.Message}\r\nStackTrace:{e.Exception.StackTrace}\r\n---\r\n" ); } // 标记异常已处理(可选,根据你的需求决定是否让程序继续运行) e.Handled = true; } protected override void OnExit(ExitEventArgs e) { // 退出时释放服务资源 _errorLogService?.Dispose(); _bootstrapper?.Dispose(); base.OnExit(e); } }
3. 关键注意点
- 禁止在App构造函数中注入:App构造函数执行时,Prism容器还未完成初始化,注入操作必然失败,必须在
OnStartup中等待Bootstrapper运行完成后再获取服务。 - 日志保存的容错性:保存异常日志的代码本身可能出错(比如数据库断开),一定要加
try-catch做降级处理,避免日志保存失败导致程序二次崩溃。 - 生命周期管理:使用
RegisterSingleton确保ErrorLog服务仅创建一次,减少数据库上下文的资源开销,同时在App退出时记得释放资源。
如果你坚持要复用现有的IModel接口,只需在注册时添加命名标识,获取时指定名称即可:
// Bootstrapper中注册 containerRegistry.Register<IModel, ErrorLogService>("ErrorLogService"); // App中获取 _errorLogService = _bootstrapper.Container.Resolve<IModel>("ErrorLogService") as ErrorLogService;
不过还是推荐单独定义日志服务接口,代码会更清晰易维护。
内容的提问来源于stack exchange,提问作者CyberWolf
相关产品推荐
相关产品推荐

