WPF NET5 Prism8.0的升级指南
发布日期:2021-05-09 06:18:02 浏览次数:2 分类:博客文章

本文共 8857 字,大约阅读时间需要 29 分钟。

前言

​ 曾经我以学习的目的写了关于在.NET Core3.1使用Prism的系列文章,也谢谢大家的支持,事实上当初的版本则是Prism7.2.0.1442(7.2)版本,而现在也发布了.NET5和最新的Prism8.0.0.1909(8.0)版本,因此同样的我想将之前的Prism Demo项目可以升级到最新,写这篇文章的目的是自己也能学习一番,而更多的是回答那些在我Prism系列文章下面留下的我认为可以拿来一讲一些问题,而有些问题我则是水平有限回答不了(真的不是不想回答)

 然后我拿之前的Prism Demo项目,WPF从.NET Core3.1升级到.NET 5其实非常简单,无脑修改项目的TargetFrameworknet5.0-windows就行了,但是当Prism7.2升级到Prism8.0,我发现build的时候报了很多错误,那么让我们来看看究竟Prism8.0更新了些啥

一 .Prism8.0更新了什么?

我们先来看下关于Prism7.2和Prism8.0的程序集引用情况,可推敲出一些不同:

这里可能不会讲述所有关于Prism8.0更新的全部细节,只是我认为可能主要的一些功能,我们可以看到Prism8.0相比Prism7.2,在Prism.WPF中去除了System.Windows.InteractivityCommonServiceLocator程序集,引入了Microsoft.Xaml.Behaviors.Wpf,实际上Prism8.0做了以下整合:

  • Microsoft.Xaml.Behaviors.Wpf替换System.Windows.Interactivity
  • CommonServiceLocator整合入Prism.Core之中

因为你从旧版本更新到Prism8.0可能会发生报错,而我的目的则是一篇更新指南,关于Prism8.0更新的全部细节,可以看官方在github的Prism8.0的,这里还推荐dino.c大佬的有关Prism8.0的文章:和

1.ContainerLocator.Current.Resolve
函数去除:

ContainerLocator.Current.Resolve
//替换为ServiceLocator.Current.GetInstance

 这可能是你遇到的第一个升级报错,因为ContainerLocator.Current.Resolve<T>这个api本来是在Prism.WPF下的CommonServiceLocator程序集下面的,8.0时候被砍了,在Prism.Core加上ServiceLocator.Current.GetInstance<T>用于替换,切掉了CommonServiceLocator程序集,我觉得非常合理,因为该功能本身就应该是IOC里面的公共功能

2.有关事件转命令的程序集变化:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"//替换为xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

 这可能是你遇到的第二个升级报错,由于用Microsoft.Xaml.Behaviors.Wpf替换System.Windows.Interactivity,因此,xaml的xmlns也需要对应更改

3.去除 Bootstrapper :

public partial class App : Bootstrapper     //替换成public partial class App : PrismApplication //(推荐)其他平台也支持//orpublic partial class App : PrismBootstrapper //WPF独有

 这可能是你遇到的第三个升级报错,我们在App.cs中都会继承一个底层类用于注册或者配置,其实在Prism7.2的时候Bootstrapper 已经被标记为弃用状态,而在Prism8.0更是直接删除,推荐继承PrismApplication(各平台都支持),当然也可以选择PrismBootstrapper (WPF独有)

4.IOC添加新注册功能:

 其实IOC这部分功能我不打算细讲,因为其实不属于Prism的特性功能,因为Prism默认支持两个IOC扩展,也就是Unity和DryIoc的,而新添加的功能也是对应通过两个IOC支持实现的,直接看代码示例:

public interface ITestService { } public interface ITest2Service { } public class TestService : ITestService, ITest2Service { }private static ITestService TestDelegate() =>new TestService();//添加支持注册多服务对应单实现类的功能var services = new[] { typeof(ITestService), typeof(ITest2Service) };IContainerRegistry.RegisterManySingleton
(services);//注册成单例模式IContainerRegistry.RegisterMany
(services);//注册成瞬时模式//添加支持注册服务为scope(范围模式)IContainerRegistry.RegisterScoped(typeof(TestService))//单服务IContainerRegistry.RegisterScoped(typeof(TestService), typeof(TestService))//单服务IContainerRegistry.RegisterScoped
();//单服务泛型版本IContainerRegistry.RegisterScoped(typeof(ITestService), typeof(TestService))//单服务单实现//添加支持通过委托方法注册服务IContainerRegistry.Register(typeof(ITestService), TestDelegate)//注册为瞬时模式IContainerRegistry.RegisterSingleton(typeof(ITestService), TestDelegate)//注册为单例模式IContainerRegistry.RegisterScoped(typeof(ITestService), TestDelegate)//注册为范围模式

5.添加了有关在void方法中异步等待Task的扩展方法:

 你乍一看好像没什么卵用,但是里面还是有说法的,我们来看一个例子,WPF界面MVVM异步读取耗时数据加载界面,这里是xaml的简化代码::

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

ViewModel简化代码:

private ObservableCollection
_allMedicines=new ObservableCollection
(); public ObservableCollection
AllMedicines { get { return _allMedicines; } set { _allMedicines = value; } }private DelegateCommand _loadCommand; public DelegateCommand LoadCommand => _loadCommand ?? (_loadCommand = new DelegateCommand(ExecuteLoadCommand));async void ExecuteLoadCommand(){ await ALongTask(); this.AllMedicines.AddRange(_medicineSerivce.GetAllMedicines());}private async Task ALongTask(){ await Task.Delay(3000);//模拟耗时操作 Debug.WriteLine("耗时操作完成");}

 这是正常我们会实现的方式,同样的也确实不会出现跨线程问题(在非UI线程操作ObservableCollection集合会出现),关于async await在WPF不会出现跨线程问题,可以参考我的另外一篇文章,也同样的在执行耗时操作时候不会阻塞UI主线程,如果在最上层不用async void能否实现同样的效果,这就是TaskExtension的意义了,下面只例举非泛型版本TaskExtension的api,,实际还有泛型版本的TaskExtension,我们拿最多参数的重载方法来说明:

public static class TaskExtensions    {        public static async void Await(this Task task, Action completedCallback, Action
errorCallback, bool configureAwait) { try { await task.ConfigureAwait(configureAwait); completedCallback?.Invoke(); } catch (Exception obj) { errorCallback?.Invoke(obj); } } }

1.completedCallback:当前Task的回调函数,指Task执行的后续操作

2.errorCallback:回调函数的异常回调函数,回调函数异常后可以执行

3.configureAwait:指示回调函数是否在当前执行上下文执行,True为是,false为否

我们可以把ExecuteLoadCommand方法修改下:

void ExecuteLoadCommand(){      //TaskExtension for async void Command       ALongTask().Await( completedCallback:() =>      {          this.AllMedicines.AddRange(_medicineSerivce.GetAllMedicines());      }, errorCallback:null,configureAwait:true);}

该方式执行效果和之前一样,而且不用在void方法加上async 和方法内部await就能实现异步等待操作,而这只是推荐在Command的Excuted Method使用,这也是官方推荐的,因为一般Excuted Method返回值只会是void

二.回答一些问题

如何在Prism使用AOP?

 其实AOP并不是属于prism特有的功能,但是由于prism支持扩展IOC容器:Unity和DryIoc,只要其IOC容器本身支持,那就可以,由于默认Prism是以Unity为默认IOC容器,所以以Unity为例子:

  1. NuGet引用Unity AOP库:Unity.Interception(最新是5.11.1)

  2. 在App.cs添加扩展AOP,代码如下:

    protected override void RegisterTypes(IContainerRegistry containerRegistry) {     var container = PrismIocExtensions.GetContainer(containerRegistry);     container.AddNewExtension
    ()//add Extension Aop //注册服务和添加显示拦截 .RegisterType
    (new Interceptor
    (), new InterceptionBehavior
    ()) .RegisterType
    (new Interceptor
    (), new InterceptionBehavior
    ()) .RegisterType
    (new Interceptor
    (), new InterceptionBehavior
    ()); }
  3. 新建类LogHandler继承ICallHandler用于处理拦截逻辑和特性LogHandlerAttribute,模拟记录Log,:

    public class LogHandler : ICallHandler{    public int Order { get ; set ; }    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)    {          Debug.WriteLine("-------------Method Excute Befored-------------");          Debug.WriteLine($"Method Name:{input.MethodBase.Name}");          if (input.Arguments.Count>0)          {              Debug.WriteLine("Arguments:");              for (int i = 0; i < input.Arguments.Count; i++)              {                  Debug.WriteLine($"parameterName:{input.Arguments.ParameterName(i)},parameterValue:{input.Arguments[i]}");              }          }                     var methodReturn = getNext()(input, getNext);          Debug.WriteLine("-------------Method Excute After-------------");          if (methodReturn.Exception!=null)          {              Debug.WriteLine($"Exception:{methodReturn.Exception.Message} \n");          }          else          {              Debug.WriteLine($"Excuted Successed \n");          }          return methodReturn;    }}public class LogHandlerAttribute : HandlerAttribute{    public override ICallHandler CreateHandler(IUnityContainer container)    {        return new LogHandler() { Order = this.Order };    } }
  4. 为那些需要拦截的接口标上Attribute

    [LogHandler]    public interface IMedicineSerivce    {        List
    GetAllMedicines(); List
    GetRecipesByPatientId(int patientId); } [LogHandler] public interface IPatientService { List
    GetAllPatients(); } [LogHandler] public interface IUserService { List
    GetAllUsers(); }

    效果如下:

Vs输出:

-------------Method Excute Befored-------------Method Name:GetAllMedicines-------------Method Excute After-------------Excuted Successed -------------Method Excute Befored-------------Method Name:GetRecipesByPatientIdArguments:parameterName:patientId,parameterValue:1-------------Method Excute After-------------Excuted Successed -------------Method Excute Befored-------------Method Name:GetRecipesByPatientIdArguments:parameterName:patientId,parameterValue:2-------------Method Excute After-------------Excuted Successed -------------Method Excute Befored-------------Method Name:GetRecipesByPatientIdArguments:parameterName:patientId,parameterValue:3-------------Method Excute After-------------Excuted Successed -------------Method Excute Befored-------------Method Name:GetRecipesByPatientIdArguments:parameterName:patientId,parameterValue:4-------------Method Excute After-------------Excuted Successed

 当然这里篇幅有限,不可能讲述有关太多Unity AOP的细节,实际上Unity AOP功能非常强大,同样支持通过配置文件来配置AOP和支持对不同类型方法的拦截,需要了解更多细节在这里可推荐该博文

是否所有事件和逻辑都在ViewModel处理?

 WPF是个数据驱动型程序,当使用MVVM框架如Prism或者MVVMLight的时候,我们会在ViewModel处理业务数据逻辑,通过Binding方式驱动前台界面的显示,如果处理逻辑是View相关的,例如对控件的样式变化,鼠标移动控件等View逻辑相关的,这时候则推荐用依赖或者附加属性,或在View的Code-behind的cs文件中事件来处理有关View的逻辑,不要为了所谓的MVVM而把一切逻辑都放在ViewModel处理,实则更加不灵活,反而跟之前的MVC都放在C中处理没啥区别了

其他问题?(待补充)

三.源码

四.参考

转载地址:https://www.cnblogs.com/ryzen/p/14301414.html 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:理解C#泛型运作原理
下一篇:理解Task和async await

发表评论

最新留言

感谢大佬
[***.8.128.20]2023年09月18日 12时26分36秒