Open sandboxFocusImprove this doc

LAMA0254: The argument of ConfigureAwait after ProceedAsync must be a literal

Severity Error
Reported by Metalama.Framework
Message The argument of 'ConfigureAwait' after 'meta.ProceedAsync()' must be the literal 'true' or 'false', but it is '<Argument>'. To choose the value at compile time, use a compile-time 'if' statement with a literal argument in each branch.

Cause

In an async template, such as an implementation of OverrideAsyncMethod, you can write await meta.ProceedAsync().ConfigureAwait( false ) to call the overridden method without capturing the synchronization context. Metalama treats the whole meta.ProceedAsync().ConfigureAwait(...) expression as a special form of meta.Proceed(), and copies the argument of ConfigureAwait into the generated code as is.

For this reason, Metalama accepts only a literal as the argument of ConfigureAwait in this expression. It reports LAMA0254 when the argument is any other expression, even a compile-time one like an aspect property or a constant. The placeholder of the message is the argument as written in the template.

In the following example, the aspect lets the user choose the ConfigureAwait behavior with the ContinueOnCapturedContext property, and passes this property directly to ConfigureAwait.

1using Metalama.Framework.Aspects;
2using System;
3using System.Threading.Tasks;
4
5namespace Doc.LAMA0254.Error;
6
7public class LogAttribute : OverrideMethodAspect
8{
9    public bool ContinueOnCapturedContext { get; set; }
10
11    public override dynamic? OverrideMethod() => meta.Proceed();
12
13    public override async Task<dynamic?> OverrideAsyncMethod()
14    {
15        Console.WriteLine( $"Starting {meta.Target.Method.Name}." );
16
17        // Error: the argument of ConfigureAwait must be the literal true or false.
18        var result = await meta.ProceedAsync().ConfigureAwait( this.ContinueOnCapturedContext );
19
20        return result;
21    }
22}
23
1using System.Threading.Tasks;
2
3namespace Doc.LAMA0254.Error;
4
5internal class PriceService
6{
7    [Log]
8    public async Task<decimal> GetPriceAsync( string productId )
9    {
10        await Task.Delay( 10 );
11
12        return 42m;
13    }
14}
15

Resolution

Depending on your intent, do one of the following:

  • If the value depends on a compile-time setting, such as an aspect property, use a compile-time if statement with one meta.ProceedAsync().ConfigureAwait(...) call per value, each with the literal true or false, as in the following example. Metalama emits only the branch that matches the setting.
  • If the value is always the same, replace the expression with the literal true or false.

1using Metalama.Framework.Aspects;
2using System;
3using System.Threading.Tasks;
4
5namespace Doc.LAMA0254.Fixed;
6
7public class LogAttribute : OverrideMethodAspect
8{
9    public bool ContinueOnCapturedContext { get; set; }
10
11    public override dynamic? OverrideMethod() => meta.Proceed();
12
13    public override async Task<dynamic?> OverrideAsyncMethod()
14    {
15        Console.WriteLine( $"Starting {meta.Target.Method.Name}." );
16
17        // Fixed: a compile-time if statement selects a call with a literal argument.
18        if ( this.ContinueOnCapturedContext )
19        {
20            var result = await meta.ProceedAsync().ConfigureAwait( true );
21
22            return result;
23        }
24        else
25        {
26            var result = await meta.ProceedAsync().ConfigureAwait( false );
27
28            return result;
29        }
30    }
31}
32
Source Code
1using System.Threading.Tasks;
2

3namespace Doc.LAMA0254.Fixed;
4
5internal class PriceService
6{
7    [Log]
8    public async Task<decimal> GetPriceAsync( string productId )
9    {
10        await Task.Delay( 10 );
11







12        return 42m;
13    }
14}
15
Transformed Code
1using System;
2using System.Threading.Tasks;
3
4namespace Doc.LAMA0254.Fixed;
5
6internal class PriceService
7{
8    [Log]
9    public async Task<decimal> GetPriceAsync(string productId)
10    {
11        Console.WriteLine("Starting GetPriceAsync.");
12        var result = await GetPriceAsync_Source(productId).ConfigureAwait(false);
13        return result;
14    }
15
16    private async Task<decimal> GetPriceAsync_Source(string productId)
17    {
18        await Task.Delay(10);
19
20        return 42m;
21    }
22}
23

See also