Open sandboxFocusImprove this doc

LAMA5208: A synchronous command method can't accept a CancellationToken

Severity Error
Reported by Metalama.Patterns.Wpf
Message The '<Method>' method has a CancellationToken parameter, but it does not return a Task and the Background property of the [Command] attribute is not set to true.

Cause

The [Command] aspect supports cancellation through a CancellationToken parameter in the last position, for example ExecuteAsync( CancellationToken cancellationToken ) or ExecuteAsync( string query, CancellationToken cancellationToken ). The token is supplied by an AsyncDelegateCommand, which the aspect generates in two cases:

  • the method returns a Task;
  • the Background property of the attribute is true.

A method that returns void and runs in the UI thread becomes a synchronous DelegateCommand, which can't be canceled. Metalama reports LAMA5208 on the target method when its last parameter is a CancellationToken, it returns void, and Background isn't true. The command property isn't generated.

In the message, <Method> is the target method.

In the following example, the Search method accepts a CancellationToken, but it returns void and runs in the UI thread.

1using Metalama.Patterns.Wpf;
2using System.Threading;
3
4namespace Doc.LAMA5208.Error;
5
6public class SearchViewModel
7{
8    // Error: the last parameter is a CancellationToken, but the method is synchronous.
9    [Command]
    Error LAMA5208: The 'SearchViewModel.Search(CancellationToken)' method has a CancellationToken parameter, but it does not return a Task and the Background property of the [Command] attribute is not set to true.

10    private void Search( CancellationToken cancellationToken ) { }
11}
12

Resolution

Choose one of the following options:

  • Make the method asynchronous: return Task, typically with the async modifier, and pass the token to the asynchronous operations you call.
  • Keep the void method and run it in a background thread by setting Background = true in the attribute, for example [Command( Background = true )].
  • Remove the CancellationToken parameter if the command doesn't need to be canceled.

In the following example, Background = true makes the aspect generate an asynchronous command, which supplies the token.

Source Code
1using Metalama.Patterns.Wpf;
2using System.Threading;
3
4namespace Doc.LAMA5208.Fixed;
5
6public class SearchViewModel
7{
8    // Fixed: the command runs in a background thread, so it supports cancellation.
9    [Command( Background = true )]
10    private void Search( CancellationToken cancellationToken ) { }
11}
12
Transformed Code
1using Metalama.Patterns.Wpf;
2using System.Threading;
3
4namespace Doc.LAMA5208.Fixed;
5
6public class SearchViewModel
7{
8    // Fixed: the command runs in a background thread, so it supports cancellation.
9    [Command(Background = true)]
10    private void Search(CancellationToken cancellationToken) { }
11
12    public SearchViewModel()
13    {
14        SearchCommand = DelegateCommandFactory.CreateBackgroundDelegateCommand(Search, null, false);
15    }
16
17    public AsyncDelegateCommand SearchCommand { get; }
18}
19

See also