ReactiveUI - STARIONGROUP/COMET-IME-Community-Edition GitHub Wiki

Conversion from Net Framework to Net 6

Update *.csproj file

Start with updating the project file. File to use as an example are CDP4Composition.csproj for normal projects, CDP4ServicesDal.csproj for Dal plugins and EngineeringModel.csproj for normal plugins.

Change the Target framework version

<TargetFramework>net6.0-windows</TargetFramework>

Add Windows Desktop indications, so that WPF and also WindowsForms frameworks are available at runtime.

<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>

Upgrade CDP4-COMET SDK versions

PackageReferences
    <PackageReference Include="CDP4Common-CE" Version="9.0.0-750-PR236" />
    <PackageReference Include="CDP4Dal-CE" Version="9.0.0-750-PR236" />
    <PackageReference Include="CDP4JsonSerializer-CE" Version="9.0.0-750-PR236" />
    <PackageReference Include="CDP4RequirementsVerification-CE" Version="9.0.0-750-PR236" />

Upgrade the CommonServiceLocator to use version 2.0.5

    <PackageReference Include="CommonServiceLocator" Version="2.0.5" />

Upgrade the DevExpress packages to use the NetStandard ones. Also install the latest version

    <PackageReference Include="DevExpress.WindowsDesktop.CodeParser" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.Data" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.DataDesktop" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.Images" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.Mvvm" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.Wpf" Version="20.2.11" />
    <PackageReference Include="DevExpress.WindowsDesktop.Wpf.Themes.Seven" Version="20.2.11" />

Exception reporter and MarkDown.Xaml don't work, but for now convert to the latest version:

    <PackageReference Include="ExceptionReporter" Version="5.0.0" />
    <PackageReference Include="Handlebars.Net" Version="2.0.2" />

Add System.ComponentModel.Composition

    <PackageReference Include="System.ComponentModel.Composition" Version="6.0.0" />

The Interactivity library has been implemented in a new package

    <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />

Upgrade Reactive packages. Use ReactiveUI.WPF if UI components are implemented in a library and use reactiveui for all other projects that need Reactive parts.

    <PackageReference Include="ReactiveUI.WPF" Version="18.0.10" />

or

    <PackageReference Include="reactiveui" Version="18.0.10" />
  • Remove <Reference Include=" Net Framework legacy ItemGroups

  • Remove Compile (xaml) ItemGroups

  • For (DAL) plugins: be sure to exclude runtime Assets for assemblies referenced by Nuget packages that result in assemblies being written to Plugin*pluginname* folder. Also use <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> for IDal related plugins.

The code conversion

Start with building the project Rebuilding and fixing errors until no errors were found is a good first step to start the conversion. Also we recommend using ReSharper during the manual conversion, as it helps to fix a lot of things, like using statements, super easy and fast.

Steps you will have to take during conversion are:

  • We implemented our own version of ReactiveList, using DynamicData, in the CDP4Composition.Mvvm namespace. As a result we need to be sure that using CDP4Composition.Mvvm; is present in all code files that references ReactiveList. After adding that using statement, you might be able to remove the using ReactiveUI; statement, as it might not be used anymore.

  • Specifically for ReactiveLists that handle ItemChanged observables, we created a TrackedReactiveList. It listens to changes on objects in the list, so the list's generic type needs to implement INotifyPropertyChanged.

  • Replace using System.Windows.Interactivity; with using Microsoft.Xaml.Behaviors; in all code files. For XAML files that use interactivity/behaviors the reference should be updated. Please use Resharper's suggestions for that as it is the fastest way. xmlns:b="http://schemas.microsoft.com/xaml/behaviors". Be sure to remove the old reference (`xmlns:i=.....").

  • Replace using Microsoft.Practices.ServiceLocation; with using CommonServiceLocator; in all code files.

  • Remove All ChangeTrackingEnabled = true statements, because we did not use the concept behind it. If we need it in the future, we cannot implement it the way it was implemented before as suspending notifications is implemented differently in DynamicData.

  • All ReactiveCommands need to be changed manually, as they all need to have two generic types instead of one. So a normal ReactiveCommand usage (ReactiveCommand<object>) that is not using a specific input parameter, needs to become a ReactiveCommand<Unit, Unit>. A ReactiveCommand that does use an input parameter, for example OpenSelectIterationsCommand in ShellViewModel.cs, has to be changed to ReactiveCommand<T, Unit>. So the OpenSelectIterationsCommand in ShellViewModel.cs becomes a ReactiveCommand<ISession, Unit>.

  • Every ReactiveCommand should be instanciated using the ReactiveCommandCreator. This wrapper class makes sure that minor changes in the ReactiveUI API can be fixed more easily.

  • Before, we needed to create a ReactiveCommand and Subscribe to it using a separate call to its Subscribe() method, that returned an IDisposable that officially needed to be manually disposed, which we almost never did. Now this can be done in a single call to ReactiveCommandCreator.Create as we can add the Subscription as a parameter to that method call.

Unit tests

  • If you want to execute a command synchronously, cast the command to an ICommand, as this is the correct Execute overload: menuItemToRemove.ClosePanelsCommand.Execute(null); => ((ICommand)menuItemToRemove.ClosePanelsCommand).Execute(default); and Add an extra namespace using System.Windows.Input;

  • Execute a command in unit tests should be done asynchronously by default: vm.CancelCommand.Execute(null); => await vm.CancelCommand.Execute(); Also be sure that the unit test itself becomes an async Task instead of a void.

  • Checking CanExecute in unit tests can only be done via the (synchronous) ICommand interface. We need to cast the command to an ICommand to do this: Assert.IsFalse(vm.OkCommand.CanExecute(null)); should become Assert.IsFalse(((ICommand)vm.OkCommand).CanExecute(null));

  • Checking DoesNotThrow in unit tests needs to be done asynchronous by default: Assert.DoesNotThrow(() => configViewModel.CloseCommand.Execute()) should become Assert.DoesNotThrowAsync(async () => await configViewModel.CloseCommand.Execute());

  • Sometimes we implement ThrownExceptions() on an IObservable, that handles all errors in a Reactive pipeline. When we use that in a unit test, using await this.viewmodel.OkCommand.Execute() the unit test will now fail. We need to use Observable.Return(Unit.Default).InvokeCommand(this.viewmodel.OkCommand); as calling the Execute method now bypasses all internal ReactiveUI logic (like the CanExecute logic) and also the ThrownExceptions system that "swallowed" errors before. See also: https://github.com/reactiveui/ReactiveUI/issues/1356

  • Tests that already use .Execute(someParameter) will build without errors, but will probably fail during Execution. These tests should become async Tasks and the await statement should be added: await viewmodel.SelectAllCommand.Execute(someParameter);

⚠️ **GitHub.com Fallback** ⚠️