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

相關文章
相關標籤/搜索