Prism框架与Microsoft.Extensions.DependencyInjection的集成使用笔记
在现代的WPF应用开发中,Prism框架提供了强大的模块化、依赖注入和MVVM支持,而Microsoft.Extensions.DependencyInjection
提供了简洁而功能强大的依赖注入机制。另外很重要的一点是Microsoft.Extensions.*
或者第三方的Nuget基本会提供Microsoft.Extensions.DependencyInjection
, 那么使用起来省心省力提高开发效率是必然的,本文档将详细讲解如何在Prism框架的WPF应用中集成Microsoft.Extensions.DependencyInjection
,并通过具体的代码示例来说明关键点。
1. 项目概述
在本项目中,我们将构建一个使用Prism框架的WPF应用,并集成Microsoft.Extensions.DependencyInjection
进行依赖注入管理。我们的主要目标包括:
- 配置日志记录
- 处理全局未捕获的异常
- 使用DryIoc作为依赖注入容器
- 注册和配置各种服务,包括数据库和自定义服务
2. 初始化与启动
在应用程序启动时,首先需要进行日志记录配置和全局异常处理的设置。
protected override void OnStartup(StartupEventArgs e) { var LOG_TEMPLATE = @"[{Timestamp:yyyy-MM-dd hh:mm:ss.fff} {Level:u3}] {Message:lj} {NewLine}{Exception}"; Log.Logger = new LoggerConfiguration() .MinimumLevel .Override("Microsoft", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.File("Logs/.log", rollingInterval: RollingInterval.Day, outputTemplate: LOG_TEMPLATE) .WriteTo.Sink(_routerSink) .CreateLogger(); try { base.OnStartup(e); // 单实例检查 Process ap = Process.GetCurrentProcess(); if (Process.GetProcessesByName(ap.ProcessName).Length > 1) { MessageBox.Show("程序已运行.", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 全局异常处理 ConfigureGlobalExceptionHandlers(); } catch (Exception ex) { Log.Logger.Error(ex.Message); MessageBox.Show(ex.Message, "应用程序错误", MessageBoxButton.OK, MessageBoxImage.Error); } }
3. 创建主窗口
protected override Window CreateShell() { var logEventReceiver = Container.Resolve<LogPageViewModel>() as ILogEventReceiver; var observableSink = new ObservableSink(logEventReceiver); _routerSink.AddSink(observableSink); this.UseHSLAuthorization("****************************"); return Container.Resolve<MainWindow>(); }
- 创建主窗口:从依赖注入容器中解析
MainWindow
实例。
4. 日志配置
日志记录采用Serilog配置,将日志输出到文件,并使用自定义Sink处理日志事件。
var LOG_TEMPLATE = @"[{Timestamp:yyyy-MM-dd hh:mm:ss.fff} {Level:u3}] {Message:lj} {NewLine}{Exception}"; Log.Logger = new LoggerConfiguration() .MinimumLevel .Override("Microsoft", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.File("Logs/.log", rollingInterval: RollingInterval.Day, outputTemplate: LOG_TEMPLATE) .WriteTo.Sink(_routerSink) .CreateLogger();
5. 异常处理
为了提高应用的稳定性,我们设置了全局的未处理异常处理程序,包括UI线程、非UI线程和任务线程。
private void ConfigureGlobalExceptionHandlers() { // 捕获UI线程未处理的异常 DispatcherUnhandledException += (sender, ex) => { Log.Logger.Error(ex.Exception.Message); ex.Handled = true; }; // 捕获非UI线程未处理的异常 AppDomain.CurrentDomain.UnhandledException += (sender, ex) => { Exception exception = (Exception)ex.ExceptionObject; if (exception != null) { Log.Logger.Error(exception.Message); } }; // 捕获Task线程中未处理的异常 TaskScheduler.UnobservedTaskException += (sender, ex) => { Log.Logger.Error(ex.Exception.Message); }; }
6. 依赖注入容器配置
使用DryIoc作为依赖注入容器,并集成Microsoft.Extensions.DependencyInjection
服务。
protected override IContainerExtension CreateContainerExtension() { var services = new ServiceCollection(); ConfigureServices(services); var container = new DryIoc.Container(CreateContainerRules()); container.WithDependencyInjectionAdapter(services); return new DryIocContainerExtension(container); }
7. 服务注册与配置
在ConfigureServices
方法中,我们注册了各种服务,包括日志服务、数据库服务和自定义服务。
private void ConfigureServices(IServiceCollection services) { services.AddLogging(builder => builder.AddSerilog(dispose: true)); services.AddScoped<IAsyncInterceptor, ExceptionInterceptor>(); services.AddDefaultProxyGenerator() .AddTransientWithAsyncInterceptor<EquipmentControlViewModel, ExceptionInterceptorAsync>(); services.AddTransientWithAsyncInterceptor<AbsoluteLocationViewModel, ExceptionInterceptorAsync>(); var configurationBuilder = new ConfigurationManager() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile("appsettings.json", true, true); var configuration = configurationBuilder.Build(); services.AddSingleton<IConfiguration>(configuration); // ViewModel services.AddScoped<LogPageViewModel>(); // SqlSugar ISqlSugarClient sugar = new SqlSugarScope( new ConnectionConfig { DbType = SqlSugar.DbType.Sqlite, ConnectionString = configuration.GetConnectionString("Sqlite"), InitKeyType = InitKeyType.Attribute, IsAutoCloseConnection = true } ); sugar.DbMaintenance.CreateDatabase(AppDomain.CurrentDomain.BaseDirectory); sugar.CodeFirst.InitTables<Alarm>(); sugar.CodeFirst.InitTables<Warning>(); sugar.CodeFirst.InitTables<IO>(); sugar.CodeFirst.InitTables<EquiControl>(); sugar.CodeFirst.InitTables<EquiControlGroup>(); sugar.CodeFirst.InitTables<JogControl>(); sugar.CodeFirst.InitTables<JogControlGroup>(); sugar.CodeFirst.InitTables<RealVal>(); sugar.CodeFirst.InitTables<RealValGroup>(); sugar.CodeFirst.InitTables<DIntVal>(); sugar.CodeFirst.InitTables<DIntValGroup>(); services.AddSingleton<ISqlSugarClient>(sugar); // Repositories services.AddTransient<IIORepository, IORepository>(); services.AddTransient<IAlarmReository, AlarmRepository>(); services.AddTransient<IWarningRepository, WarningRepository>(); services.AddTransient<IEquiControlGroupRepository, EquiControlGroupRepository>(); services.AddTransient<IJogControlGroupRepository, JogControlRepository>(); services.AddTransient<IRealValGroupRepository, RealValGroupRepository>(); services.AddTransient<IDIntValGroupRepository, DIntValGroupRepository>(); // HSLDIExtensions var deploymentType = configuration.GetSection("DeploymentType").Get<string>(); if (string.IsNullOrWhiteSpace(deploymentType) || deploymentType == "模拟") { //services.AddScoped<IPLCService, FakePLCService>(); } else if (deploymentType == "生产机台") { services.AddHSL(options => { options.UseS7Net("控制器", configuration); }); services.AddSingleton<IPLCService, PLCService>(); } }
8. 其他配置与功能
除了核心功能的配置外,Prism还提供了ViewModel定位器、模块目录、区域适配器映射等功能,以增强应用的模块化和可维护性。
protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<PubSubEventModule>(); } protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings) { base.ConfigureRegionAdapterMappings(regionAdapterMappings); } protected override void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors) { base.ConfigureDefaultRegionBehaviors(regionBehaviors); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterDialog<HintMessageDialog>(nameof(HintMessageDialog)); // Register Region containerRegistry.RegisterForNavigation<HomePage>(); containerRegistry.RegisterForNavigation<SpeedPage>(); containerRegistry.RegisterForNavigation<AlarmPage>(); containerRegistry.RegisterForNavigation<IOPage>(); containerRegistry.RegisterForNavigation<CompositePage>(); containerRegistry.RegisterForNavigation<LogPage>(); containerRegistry.RegisterForNavigation<ViusalTestView>(); }
9. Prism与DryIoc集成中的规则配置详解
在集成Prism与DryIoc的过程中,我们需要配置DryIoc的规则以满足Prism框架的需求。下面我们将详细解析相关代码,并说明其中的一些关键点,特别是关于DryIoc的版本依赖和规则配置的细节。
依赖版本
在使用Prism和DryIoc进行集成时,需要注意以下依赖版本:
Prism.DryIoc
对 DryIoc 的依赖是 >= 4.7DryIoc.Microsoft.DependencyInjection
对 DryIoc 的依赖是 >= 5.40
这意味着在DryIoc的新版本中,某些扩展方法已经被移除或替换,例如WithoutFastExpressionCompiler()
。
默认规则配置
首先,我们定义了一个静态属性DefaultRules
,用于配置DryIoc的默认规则,代码参考PrismApplication
中的DryIocContainerExtension.DefaultRules
实现,注释掉不存在的扩展方法即可。
/// <summary> /// Gets the Default DryIoc Container Rules used by Prism /// </summary> public static Rules DefaultRules => Rules.Default .WithConcreteTypeDynamicRegistrations(reuse: Reuse.Transient) .With(Made.Of(FactoryMethod.ConstructorWithResolvableArguments)) .WithFuncAndLazyWithoutRegistration() .WithTrackingDisposableTransients() //.WithoutFastExpressionCompiler() .WithFactorySelector(Rules.SelectLastRegisteredFactory());
规则详解:
- WithConcreteTypeDynamicRegistrations:
- 配置DryIoc以动态注册具体类型,指定使用瞬态(Transient)生命周期。
- With(Made.Of(FactoryMethod.ConstructorWithResolvableArguments)):
- 指定在解析类型时使用可解析参数的构造函数。
- WithFuncAndLazyWithoutRegistration:
- 允许使用未注册的Func和Lazy类型。
- WithTrackingDisposableTransients:
- 跟踪并处理瞬态(Transient)生命周期的可释放对象。
- WithFactorySelector(Rules.SelectLastRegisteredFactory):
- 使用最后注册的工厂选择器。
注意:在DryIoc的新版本中,WithoutFastExpressionCompiler()
已被移除,因此该方法被注释掉。
创建规则
接下来,我们通过覆盖CreateContainerRules
方法来创建并返回默认规则。
/// <summary> /// Create <see cref="Rules" /> to alter behavior of <see cref="IContainer" /> /// </summary> /// <returns>An instance of <see cref="Rules" /></returns> protected override Rules CreateContainerRules() => DefaultRules;
创建容器扩展
然后,我们通过覆盖CreateContainerExtension
方法来创建DryIoc容器,并集成Microsoft的依赖注入服务。
protected override IContainerExtension CreateContainerExtension() { var services = new ServiceCollection(); ConfigureServices(services); var container = new DryIoc.Container(CreateContainerRules()); container.WithDependencyInjectionAdapter(services); return new DryIocContainerExtension(container); }
步骤详解:
- ConfigureServices(services):
- 配置Microsoft依赖注入服务集合。
- CreateContainerRules():
- 创建并获取容器规则。
- new DryIoc.Container(CreateContainerRules()):
- 使用自定义规则创建DryIoc容器实例。
- container.WithDependencyInjectionAdapter(services):
- 将Microsoft依赖注入服务集合适配到DryIoc容器中。
- return new DryIocContainerExtension(container):
- 返回DryIoc容器扩展实例。
通过上述配置,我们实现了Prism框架与DryIoc容器的集成。在DryIoc的新版本中,某些方法如WithoutFastExpressionCompiler()
已经被移除,因此在使用这些版本时,需要确保代码适配最新的API变化。通过合理配置DryIoc的规则和容器扩展,可以充分发挥Prism框架的模块化和依赖注入功能,提高应用的可维护性和扩展性。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步