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.7
  • DryIoc.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());

规则详解

  1. WithConcreteTypeDynamicRegistrations:
    • 配置DryIoc以动态注册具体类型,指定使用瞬态(Transient)生命周期。
  2. With(Made.Of(FactoryMethod.ConstructorWithResolvableArguments)):
    • 指定在解析类型时使用可解析参数的构造函数。
  3. WithFuncAndLazyWithoutRegistration:
    • 允许使用未注册的Func和Lazy类型。
  4. WithTrackingDisposableTransients:
    • 跟踪并处理瞬态(Transient)生命周期的可释放对象。
  5. 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);
}

步骤详解

  1. ConfigureServices(services):
    • 配置Microsoft依赖注入服务集合。
  2. CreateContainerRules():
    • 创建并获取容器规则。
  3. new DryIoc.Container(CreateContainerRules()):
    • 使用自定义规则创建DryIoc容器实例。
  4. container.WithDependencyInjectionAdapter(services):
    • 将Microsoft依赖注入服务集合适配到DryIoc容器中。
  5. return new DryIocContainerExtension(container):
    • 返回DryIoc容器扩展实例。

通过上述配置,我们实现了Prism框架与DryIoc容器的集成。在DryIoc的新版本中,某些方法如WithoutFastExpressionCompiler()已经被移除,因此在使用这些版本时,需要确保代码适配最新的API变化。通过合理配置DryIoc的规则和容器扩展,可以充分发挥Prism框架的模块化和依赖注入功能,提高应用的可维护性和扩展性。

posted @   非法关键字  阅读(597)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 趁着过年的时候手搓了一个低代码框架
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现
历史上的今天:
2020-05-29 MvvmLight + Microsoft.Extensions.DependencyInjection + WpfApp(.NetCore3.1)
2018-05-29 pybind11简介
点击右上角即可分享
微信分享提示