WPF NET5 Prism8.0的升级指南

前言

​ 曾经我以学习的目的写了关于在.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更新了些啥html

一 .Prism8.0更新了什么?

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

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

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

由于你从旧版本更新到Prism8.0可能会发生报错,而个人目的则是一篇更新指南,关于Prism8.0更新的所有细节,能够看官方在github的Prism8.0的ReleaseNote,这里还推荐dino.c大佬的有关Prism8.0的文章:[Windows] Prism 8.0 入门(上):Prism.Core[Windows] Prism 8.0 入门(下):Prism.Wpf 和 Prism.Unityexpress

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里面的公共功能bootstrap

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也须要对应更改c#

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独有)windows

4.IOC添加新注册功能:

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

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的简化代码::app

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

相关文章
相关标签/搜索