Exrin MVVM Operations – Write Less Code

Operations are functions, typically run in an ICommand in the ViewModel, that contain the logic of your ICommand, and have specific, easily testable return types. Interesting fact, Operations were actually the reason I designed Exrin. Then an awesome NavigationService was next. By this stage, it was hard to develop as a separate component to an MVVM framework, hence Exrin became it’s own. Operations and Navigation are two of the strongest points of Exrin, for Xamarin.Forms.

The purpose of an Operation is to reduce the amount of code you write in your ViewModel, and ensure consistency among numerous developers, over time. It solves many common issues I have continued to encounter when developing, especially in a team.

Operation

This is how an operation works. In our ViewModel, we have a Command, that returns an IRelayCommand. This is just an Exrin enhanced ICommand, but nothing special here in terms of operations.

Next we have GetCommand, but this is just a function to cache commands, so your app doesn’t have to recreate them every time the BindingEngine retrieves this value.

Now we get into the details, we reference the Execution Container, and call ViewModelExecute. This is what will run our operation. An operation can be it’s own separate class, that inherits from ISingleOperation. However here, you can just enter a function, and it will wrap it in an Operation for you.

The function accepts 2 parameters. The first ‘parameter’ is the CommandParameter. The second, ‘token’, is a CancellationToken. This can be used if you want to cancel the operation, or it may be triggered by the Execution Container, under various conditions. Pass this cancellation token along to any new tasks you may start to ensure they receive the cancellation request if needed.

Finally, it returns an IList<IResult>. It is simply a list of results, that the Execution Container will act upon, in order of being sent. In this example, it is a  NavigationResult, with a stack and page to go to. The results are actually just a Result object. You can create your own result and set whatever parameters you need, including displaying a dialog, or recording an error.

public IRelayCommand AboutCommand        
{            
    get            
    {               
        return GetCommand(() =>               
        {                    
            return Execution.ViewModelExecute((parameter, token) => 
            { 
                return new NavigationResult(Stacks.Main, Main.About); 
            });                
        });            
    }        
}

Execution Container

Operations are run inside an Execution container. This container, wraps each operation, in several functions that handle threading, result handling and more. An Execution container is set up by default in each ViewModel, and you can reference it as such.

public class MainViewModel: Exrin.Framework.ViewModel
{
    public MainViewModel()
    {
        Execution.<method>
    }
}

Its just a property, that is in the Exrin.Framework.ViewModel. Now lets go through what happens, and what you can do with an Operation.

Timeouts

You never know when a service or operation might suddenly be sat waiting for a response, and it is likely you will have missed placing appropriate timeouts on the code. In order to resolve this, you can implement a timeout on your operation. These are configurable, and you can just pass through a parameter.

return Execution.ViewModelExecute((parameter, token) =>
{
    // Anything I want in here. Example of NavigationResult shown.
    return new NavigationResult(Stacks.Main, Main.About);
}, timeout:30000);

Background Threading

Whenever you run an ICommand, it comes in on the UIThread. This is generally not preferred, if you have a lot of processing to do, as it will lock up the UI while it works. Operations, always moves the logic to a background thread, to ensure the UI Thread isn’t interrupted.

VisualState.IsBusy

If you are using the VisualState property in your ViewModel, there is an IsBusy property already defined. Whenever an operation is running it will set this boolean to true. All you need to do is bind, as appropriate, to controls, if needed. I generally use this for page wide locks of the screen, when you don’t want the user doing anything else.

IsVisible="{Binding VisualState.IsBusy}"

However, it adds a little more logic, to help smooth out the UI. If an operation has an extremely small run time, say less than 400ms, you don’t want to trigger this, while it loads. Hence, it has an inbuilt 400ms delay, meaning it won’t display for 400ms. If the operation finishes before this time, it won’t trigger it. This is also configurable, and you can do so via this code, directly in your ViewModel.

IsBusyDelay = 1000; // In milliseconds

PreChecks

One of the most common things I see, is the same repeated code over and over again, checking for something. For example, a recent project, checked if the internet was connected, before continuing. With prechecks, you can write this code once, and have it run on all appropriate operations.

In your constructor or any other initialization section of your ViewModel, you can set a precheck as such.

Execution.PreCheck = async (arg) => 
{ 
    await Task.Delay(1000);
    return true; 
};

This will now run on every operation in that ViewModel. In here you could do numerous check for internet connectivity, or if the user is authenticated. But what if you only want to run it on certain operations. You can do that as well, with the parameter. First lets have an enum of numerous checks.

[Flags]
public enum PreChecks
{
    Authentication,
    InternetConnectivity
}

Now lets pass this through, in the parameter. As a Flag, you can pass multiple through as once.

return Execution.ViewModelExecute((parameter, token) => 
{ 
    // Anything I want in here. Example of NavigationResult shown. 
    return new NavigationResult(Stacks.Main, Main.About); 
}, precheck: PreChecks.Authentication | PreChecks.InternetConnectivity);

In the precheck function, perform the checks, as dictated via the enums sent through.

Execution.PreCheck = async (arg) => 
{ 
    var flags = (PreChecks)arg;

    if (flags.HasFlag(PreChecks.Authentication))
        // Do your check
    
    if (flags.HasFlag(PreChecks.InternetConnectivity))
        // Do your check 
};

Separate Class

If you have an extremely large operation, or maybe it’s one you want to share with many ViewModels, you might want to put it in a separate class, and you can easily do this.

public class BackToMainOperation : ISingleOperation
{
    public BackToMainOperation() { }

    public Func<object, CancellationToken, Task<IList>> Function
    {
        get
        {
            return (parameter, token) =>
            {
                return new NavigationResult(Stacks.Main, Main.Main, new DetailOperation(), true);
            };
        }
    }

    public bool ChainedRollback { get; private set; } = false;

    public Func Rollback { get { return null; } }
}

Then you call it as such.

public IRelayCommand MainCommand 
{
    get
    {
        return GetCommand(() => 
        { 
            return Execution.ViewModelExecute(new BackToMainOperation()); 
        }); 
    }
}

Unit Testing

When it’s hard to unit test a block of code, you can almost be certain it won’t be tested. With this in mind, operations are always designed for easy unit testing. There are 2 approaches, depending upon how you structured your operation. If you have embedded your operation, directly inside the ICommand, then you can do this.

// Arrange
var viewModel = new MyViewModel(); // This is your existing ViewModel you want to test
var source = new CancellationTokenSource(); 
// Act
var result = await viewModel.MyCommand.Function(parameter, source.Token);
// Assert
// Now test, the results as required

If you decided to use a separate class then you would do something similar to this.

// Arrange
var visualState = new MyVisualState();
// What is awesome here, is you can define any VisualState of the page, you want to test, and just pass it in.
// Assuming your Operation deals with any VisualState.
var operation = new MyOperation(visualState, anythingElse);
var source = new CancellationTokenSource();

// Act
var result = await operation.Function(parameter, source.Token);

// Assert
// Now test you had the right results, or calls to anything your passed in were made.

Chained Operations

Admittedly, I doubt you will use this, it is only for very rare occasions. But say you have an operation that is very complex, involves numerous steps, that you want to do step by step. You can create numerous operations and chain them together, and even provide a rollback scenario, if not all operations succeed. In your operation, as a separate class, you will need to set the ChainedRollback = true, if you want to do a rollback, if any of the future operations fail in the chain. Then create the function to perform, if a rollback is initiated.

public bool ChainedRollback { get; private set; } = false;

public Func Rollback { get { return null; } }

Then, to link all of these operations together, this is how you do it. You need to create a ViewModelExecute Container.

public class ViewModelExecute : IViewModelExecute 
{ 
    public List<IBaseOperation> Operations => new List<IBaseOperation>() 
                                              { 
                                                  new SettingsOperation(null), 
                                                  new BackToMainOperation() 
                                              };
    public int TimeoutMilliseconds => 10000; 
}

Then you pass it in, as you would a regular operation.

return Execution.ViewModelExecute(new ViewModelExecute());

They will be executed, in order of listing, and rolled back if required, in the reverse order, from the one just before the one that failed.

Summary

There is a lot that goes on behind the scenes of an operation. But once you understand how they work, they can provide numerous benefits.

  • Team wide consistency on how Commands are built
  • Reduced code
  • Easily PreCheck. before performing operations
  • Controlled timeouts
  • Background Threading
  • Easily unit testable
  • Simple and extendable
  • IsBusy visual state flag

Learn More

If you want to know more about Exrin, have a look at the following resources

Microsoft MVP | Xamarin MVP | Xamarin Certified Developer |
Exrin MVVM Framework | Xamarin Forms Developer | Melbourne, Australia

Related Posts

Tagged:Tags:

Leave A Comment?