前言

​ 曾经我以学习的目的写了关于在.NET Core3.1使用Prism的系列文章.NET Core 3 WPF MVVM框架 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其实非常简单,无脑修改项目的TargetFramework为net5.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.Interactivity和CommonServiceLocator程序集,引入了Microsoft.Xaml.Behaviors.Wpf,实际上Prism8.0做了以下整合:

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

因为你从旧版本更新到Prism8.0可能会发生报错,而我的目的则是一篇更新指南,关于Prism8.0更新的全部细节,可以看官方在github的Prism8.0的ReleaseNote,这里还推荐Dior大佬的有关Prism8.0的文章:[Windows] Prism 8.0 入门(上):Prism.Core和[Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unity

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

ContainerLocator.Current.Resolve<T>
//替换为
ServiceLocator.Current.GetInstance<T>

 这可能是你遇到的第一个升级报错,因为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 //(推荐)其他平台也支持
//or
public 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<TestService>(services);//注册成单例模式
IContainerRegistry.RegisterMany<TestService>(services);//注册成瞬时模式 //添加支持注册服务为scope(范围模式)
IContainerRegistry.RegisterScoped(typeof(TestService))//单服务
IContainerRegistry.RegisterScoped(typeof(TestService), typeof(TestService))//单服务
IContainerRegistry.RegisterScoped<TestService>();//单服务泛型版本
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"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers> <DataGrid Grid.Row="1" IsReadOnly="True" ItemsSource="{Binding AllMedicines}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
<DataGridTextColumn Header="Unit" Binding="{Binding Unit}"/> </DataGrid.Columns>
</DataGrid>

ViewModel简化代码:

 private ObservableCollection<Medicine> _allMedicines=new ObservableCollection<Medicine>();

 public ObservableCollection<Medicine> 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不会出现跨线程问题,可以参考我的另外一篇文章异步函数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<Exception> 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<Interception>()//add Extension Aop
    //注册服务和添加显示拦截
    .RegisterType<IMedicineSerivce, MedicineSerivce>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
    .RegisterType<IPatientService, PatientService>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
    .RegisterType<IUserService, UserService>(new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>()); }
  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<Medicine> GetAllMedicines();
    List<Recipe> GetRecipesByPatientId(int patientId);
    }
    [LogHandler]
    public interface IPatientService
    {
    List<Patient> GetAllPatients();
    }
    [LogHandler]
    public interface IUserService
    {
    List<User> GetAllUsers();
    }

    效果如下:

Vs输出:

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

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

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

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

其他问题?(待补充)

三.源码

.NET5-Prsm8.0-Sample

四.参考

https://github.com/PrismLibrary/Prism

https://github.com/PrismLibrary/Prism/releases

C#中AOP_使用Unity实现AOP

WPF NET5 Prism8.0的升级指南的更多相关文章

  1. Apache ShardingSphere 5.0.0 内核优化及升级指南

    经过近两年时间的优化和打磨,Apache ShardingSphere 5.0.0 GA 版终于在本月正式发布,相比于 4.1.1 GA 版,5.0.0 GA 版在内核层面进行了大量的优化.首先,基于 ...

  2. Spring Boot 2.0 升级指南

    Spring Boot 2.0 升级指南 前言 Spring Boot已经发布2.0有5个月多,多了很多新特性,一些坑也慢慢被填上,最近有空,就把项目中Spring Boot 版本做了升级,顺便整理下 ...

  3. Angular4.0.0正式发布,附新特性及升级指南

    本文首发地址:Angular4.0.0正式发布,附新特性及升级指南 作者|孙薇 编辑|尾尾 经历了6个RC版本之后,Angular项目组终于发布了新版,即正式版 Angular 4.0.0.新版的 A ...

  4. 企业IT管理员IE11升级指南【17】—— F12 开发者工具

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  5. 企业IT管理员IE11升级指南【15】—— 代理自动配置脚本

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  6. 企业IT管理员IE11升级指南【5】—— 不跟踪(DNT)例外

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  7. 企业IT管理员IE11升级指南【6】—— Internet Explorer 11面向IT专业人员的常见问题

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  8. 企业IT管理员IE11升级指南【7】—— Win7和Win8.1上的IE11功能对比

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  9. webpack4升级指南

    webpack4升级指南 鉴于图书项目编译速度极慢的情况(项目里面module太多了,编译慢很正常)且最近需求不多(很少出现的空挡期).所以我觉得搞一波webpack升级,看看有没有帮助.webpac ...

随机推荐

  1. android adb命令* daemon not running.starting it now on port 5037 * 问题解决

    输入adb devices却出现了问题daemon not running.starting it now on port 5037, 2. 原因: adb的端口(5037)被占用了.至于这个5037 ...

  2. Java中instanceof注意的地方

    instanceof只能用于对象的判断,不能用于基本类型的判断,以下代码会编译不通过 'A' instanceof Character instanceof特有的规则:若左操作数是null,结果就直接 ...

  3. glances linux资源使用监控

    安装 yum install glances -y 界面 介绍 命令选项 -b:显示网络连接速度 Byte/ 秒 -B @IP|host :绑定服务器端 IP 地址或者主机名称 -c @IP|host ...

  4. Shell-sed之替换字符

    将A_B_C_D_TXT 修改为 A.B_C_D.TXT echo "A_B_C_D_TXT"|sed 's/_/./1;s/_/./3' 注:后面的数字代表匹配的次数, 1表示第 ...

  5. Spark性能调优篇六之调节数据本地化等待时长

    数据本地化等待时长调节的优化 在项目该如何使用? 通过 spark.locality.wait 参数进行设置,默认为3s,6s,10s. 项目中代码展示: new SparkConf().set(&q ...

  6. 多任务-python实现-使用队列完成进程间的通信(2.1.8)

    @ 目录 1.为什么要使用队列 2.python代码实现 1.为什么要使用队列 进程之间是互相独立的,而线程能够共享全局变量 所以如果进程间想要交换数据的话 只有通过进程间的通信,比如socket.太 ...

  7. 如何修改openstack虚拟机密码

    1.虚拟机创建时设置密码 计算节点安装以下软件包 yum install libguestfs python-libguestfs libguestfs-tools-c 配置计算节点nova配置文件/ ...

  8. Known快速开发框架

    Known是一个基于.NET开发的快速开发框架,前后端分离,使用极少的第三方组件,开发简单快速,大部分代码可通过代码生成工具自动生成,非常适合单兵作战和小团队开发.前端UI是一个基于JQuery开发的 ...

  9. Python机器学习课程:线性回归算法

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最基本的机器学习算法必须是具有单个变量的线性回归算法.如今,可用的高级机器学习算法,库和技术如此之多 ...

  10. python 实现数值积分与画图

    import numpy as np from scipy import integrate def half_circle(x): return (1 - x ** 2) ** 0.5 N = 10 ...