前言

​ 曾经我以学习的目的写了关于在.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其实非常简单,无脑修改项目的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的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. C++将整型值转换为字符串

    最简单的方法是使用一个字符串流(stringstream):#include<iostream>#include<string>#include<sstream>u ...

  2. MetaException(message:Could not connect to meta store using any of the URIs provided. Most recent failure: org.apache.thrift.transport.TTransportException: java.net.ConnectException: 拒绝连接 (Connection

    hive在hive-site.xml中配置hive.metastore.uris属性,后启动异常 hive异常 [fan@master hive-0.13.1-cdh5.3.6]$ bin/hive ...

  3. 06-flask-文件上传案例

    前端代码 Demo.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. 关于Java Integer和Long直接比较

    Integer和Long不能直接equals比较会返回False Long.class源码 ` public boolean equals(Object obj) { if (obj instance ...

  5. Restful API 接口设计标准及规范

    Restful API 接口设计标准以及规范 RESTful概念 理解和评估以网络为基础的应用软件的架构设计,得到一个功能强.性能好.适宜通信的架构.REST指的是一组架构约束条件和原则." ...

  6. 用Wireshark对Android应用的网络流量进行抓包

    通过Wireshark.Charles.Burpsuite等工具分析网络流量的过程,又叫做抓包. 为何需要抓包 测试手机应用(如搜狗号码通.搜狗手机浏览器)的功能时,经常遇到与网络交互的场景,这时候我 ...

  7. Swift3.0学习之Button

    1.根据字符串的长度确定Button的长度(button的高度给定) let hight:CGFloat = 30.0 let size1 = CGSize(width: getLabWidth(la ...

  8. java 去掉html标签 使用正则表达式删除HTML标签。

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class HTMLSpirit{ public stat ...

  9. jmeter+jdk环境配置

    一:JDK 安装配置:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html  ,下载JDK8 安装 1. ...

  10. 初探JAVA内部类细节一

    定义: 可以将一个类的定义放在另一个类的内部 这就是内部类.--摘自java编程思想 一般实现方式: public class SimpleInnerClass { class Content { p ...