| Severity | Error |
| Reported by | Metalama.Framework |
| Message | Cannot invoke the member '<Member>' with InvokerOptions.<InvokerOption> on an explicit object or type, because '<Member>' is not a member of the template target type '<TargetType>', of its base types, or of its interfaces. Use InvokerOptions.Default or InvokerOptions.Final instead. |
Cause
In a template, you can invoke a member through its invoker, for instance with IMethod.Invoke, and choose which implementation is called with InvokerOptions. InvokerOptions.Base and InvokerOptions.Current select a state of the member within the aspect layers of the current type: before or after the overrides added by the current aspect layer. This only makes sense for members of the type into which the template is expanded.
Metalama reports LAMA0063 when you specify an explicit object or type with WithObject and use InvokerOptions.Base or InvokerOptions.Current, but the target type of the template isn't convertible to the type that declares the invoked member. In the message, <Member> is the invoked member, <TargetType> is the type into which the template is expanded, and <InvokerOption> is the option.
The error is reported when the template is expanded, on the declaration to which the aspect is applied.
In the following example, the ValidateFirst aspect overrides OrderService.Submit and tries to call the base implementation of Order.Validate on the order parameter.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.Invokers;
4using System.Linq;
5
6namespace Doc.LAMA0063.Error;
7
8public class ValidateFirstAttribute : MethodAspect
9{
10 public override void BuildAspect( IAspectBuilder<IMethod> builder )
11 {
12 var parameterType = (INamedType) builder.Target.Parameters[0].Type;
13 var validateMethod = parameterType.Methods.OfName( "Validate" ).Single();
14
15 builder.Override( nameof(this.Template), new { validateMethod } );
16 }
17
18 [Template]
19 public dynamic? Template( [CompileTime] IMethod validateMethod )
20 {
21 // Error: Validate isn't a member of the target type, so InvokerOptions.Base isn't allowed.
22 validateMethod.WithObject( meta.Target.Method.Parameters[0] ).WithOptions( InvokerOptions.Base ).Invoke();
23
24 return meta.Proceed();
25 }
26}
27
1using System;
2
3namespace Doc.LAMA0063.Error;
4
5public class Order
6{
7 public int Quantity { get; set; }
8
9 public void Validate()
10 {
11 if ( this.Quantity <= 0 )
12 {
13 throw new InvalidOperationException( "The quantity must be positive." );
14 }
15 }
16}
17
18public class OrderService
19{
20 [ValidateFirst]
Error LAMA0063: Cannot invoke the member 'Order.Validate()' with InvokerOptions.Base on an explicit object or type, because 'Order.Validate()' is not a member of the template target type 'OrderService', of its base types, or of its interfaces. Use InvokerOptions.Default or InvokerOptions.Final instead.
21 public void Submit( Order order )
22 {
23 Console.WriteLine( $"Submitting an order of {order.Quantity} items." );
24 }
25}
26
Resolution
To invoke a member of another type, use InvokerOptions.Default or InvokerOptions.Final, or don't call WithOptions. With an explicit object, these options call the final implementation of the member, as normal C# code would. The base or current implementation is available only for members of the target type and its base types.
In the following example, the aspect uses InvokerOptions.Default.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.Invokers;
4using System.Linq;
5
6namespace Doc.LAMA0063.Fixed;
7
8public class ValidateFirstAttribute : MethodAspect
9{
10 public override void BuildAspect( IAspectBuilder<IMethod> builder )
11 {
12 var parameterType = (INamedType) builder.Target.Parameters[0].Type;
13 var validateMethod = parameterType.Methods.OfName( "Validate" ).Single();
14
15 builder.Override( nameof(this.Template), new { validateMethod } );
16 }
17
18 [Template]
19 public dynamic? Template( [CompileTime] IMethod validateMethod )
20 {
21 // Fixed: InvokerOptions.Default calls the final implementation of Validate.
22 validateMethod.WithObject( meta.Target.Method.Parameters[0] ).WithOptions( InvokerOptions.Default ).Invoke();
23
24 return meta.Proceed();
25 }
26}
27
1using System;
2
3namespace Doc.LAMA0063.Fixed;
4
5public class Order
6{
7 public int Quantity { get; set; }
8
9 public void Validate()
10 {
11 if ( this.Quantity <= 0 )
12 {
13 throw new InvalidOperationException( "The quantity must be positive." );
14 }
15 }
16}
17
18public class OrderService
19{
20 [ValidateFirst]
21 public void Submit( Order order )
22 {
23 Console.WriteLine( $"Submitting an order of {order.Quantity} items." );
24 }
25}
26
1using System;
2
3namespace Doc.LAMA0063.Fixed;
4
5public class Order
6{
7 public int Quantity { get; set; }
8
9 public void Validate()
10 {
11 if (this.Quantity <= 0)
12 {
13 throw new InvalidOperationException("The quantity must be positive.");
14 }
15 }
16}
17
18public class OrderService
19{
20 [ValidateFirst]
21 public void Submit(Order order)
22 {
23 order.Validate();
24 Console.WriteLine($"Submitting an order of {order.Quantity} items.");
25 }
26}
27