| Severity | Error |
| Reported by | Metalama.Framework |
| Message | A cycle was found in the specifications of aspect ordering between the following aspect layers: <Layers>. |
Cause
When several aspects are applied to the same declaration, Metalama must know in which order to apply them. You specify this order with the AspectOrderAttribute custom attribute. Metalama combines all these attributes, including those of referenced assemblies, with the order of the layers within each aspect, and sorts the aspect layers accordingly.
Metalama reports LAMA0021 when these specifications contradict each other, so that no order satisfies all of them. For instance, one attribute says that A runs before B and another one says that B runs before A. In the message, <Layers> lists the full names of the aspect layers that form the cycle. The error is reported on the AspectOrder attributes involved, and Metalama doesn't transform the project.
In the following example, two attributes order the Log and Measure aspects in opposite directions.
1using Metalama.Framework.Aspects;
2using System;
3using System.Diagnostics;
4
5namespace Doc.LAMA0021.Error;
6
7public class LogAttribute : OverrideMethodAspect
8{
9 public override dynamic? OverrideMethod()
10 {
11 Console.WriteLine( $"Executing {meta.Target.Method}." );
12
13 return meta.Proceed();
14 }
15}
16
17public class MeasureAttribute : OverrideMethodAspect
18{
19 public override dynamic? OverrideMethod()
20 {
21 var stopwatch = Stopwatch.StartNew();
22
23 try
24 {
25 return meta.Proceed();
26 }
27 finally
28 {
29 Console.WriteLine( $"{meta.Target.Method} took {stopwatch.ElapsedMilliseconds} ms." );
30 }
31 }
32}
33
1using Doc.LAMA0021.Error;
2using Metalama.Framework.Aspects;
3
4[assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(LogAttribute), typeof(MeasureAttribute) )]
5
6// Error: this attribute specifies the opposite order, which creates a cycle.
Error LAMA0021: A cycle was found in the specifications of aspect ordering between the following aspect layers: Doc.LAMA0021.Error.MeasureAttribute, Doc.LAMA0021.Error.LogAttribute.
7[assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(MeasureAttribute), typeof(LogAttribute) )]
8
9namespace Doc.LAMA0021.Error;
10
11internal class Calculator
12{
13 [Log]
14 [Measure]
15 public int Add( int a, int b ) => a + b;
16}
17
Resolution
Find the AspectOrder attributes that involve the aspects listed in the message, decide which order you want, and remove or correct the attributes that contradict it. Check the attributes in all files of the project, and remember that the attributes of referenced projects also apply. To avoid such conflicts, specify the order of a group of aspects in a single attribute.
In the following example, a single attribute orders the two aspects.
1using Metalama.Framework.Aspects;
2using System;
3using System.Diagnostics;
4
5namespace Doc.LAMA0021.Fixed;
6
7public class LogAttribute : OverrideMethodAspect
8{
9 public override dynamic? OverrideMethod()
10 {
11 Console.WriteLine( $"Executing {meta.Target.Method}." );
12
13 return meta.Proceed();
14 }
15}
16
17public class MeasureAttribute : OverrideMethodAspect
18{
19 public override dynamic? OverrideMethod()
20 {
21 var stopwatch = Stopwatch.StartNew();
22
23 try
24 {
25 return meta.Proceed();
26 }
27 finally
28 {
29 Console.WriteLine( $"{meta.Target.Method} took {stopwatch.ElapsedMilliseconds} ms." );
30 }
31 }
32}
33
1using Doc.LAMA0021.Fixed;
2using Metalama.Framework.Aspects;
3
4// Fixed: a single attribute specifies the order of the two aspects.
5[assembly: AspectOrder( AspectOrderDirection.RunTime, typeof(LogAttribute), typeof(MeasureAttribute) )]
6
7namespace Doc.LAMA0021.Fixed;
8
9internal class Calculator
10{
11 [Log]
12 [Measure]
13 public int Add( int a, int b ) => a + b;
14}
15
1using System;
2using System.Diagnostics;
3using Doc.LAMA0021.Fixed;
4using Metalama.Framework.Aspects;
5
6// Fixed: a single attribute specifies the order of the two aspects.
7[assembly: AspectOrder(AspectOrderDirection.RunTime, typeof(LogAttribute), typeof(MeasureAttribute))]
8
9namespace Doc.LAMA0021.Fixed;
10
11internal class Calculator
12{
13 [Log]
14 [Measure]
15 public int Add(int a, int b)
16 {
17 Console.WriteLine("Executing Calculator.Add(int, int).");
18 var stopwatch = Stopwatch.StartNew();
19 try
20 {
21 return a + b;
22 }
23 finally
24 {
25 Console.WriteLine($"Calculator.Add(int, int) took {stopwatch.ElapsedMilliseconds} ms.");
26 }
27 }
28}
29