| 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
asyncmodifier, and pass the token to the asynchronous operations you call. - Keep the
voidmethod and run it in a background thread by settingBackground = truein 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.
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
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