MetalamaConceptual documentationCreating aspectsMaking aspects configurableConfiguration before v2023.​4
Open sandboxFocusImprove this doc

Exposing configuration (before v2023.4)

Note

Starting with Metalama 2023.4, this approach is considered obsolete.

To establish a configuration API prior to Metalama 2023.4:

  1. Construct a class that inherits from ProjectExtension and includes a default constructor.
  2. If necessary, override the Initialize method, which accepts the IProject.
  3. In your aspect code, invoke the IProject.Extension<T>() method, where T represents your configuration class, to acquire the configuration object.
  4. If desired, devise an extension method for the IProject type to make your configuration API more discoverable. The class must be annotated with [CompileTime].
  5. For users to configure your aspect, they should implement a project fabric and access your configuration API using this extension method.

Example

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Options;
4using System.Diagnostics;
5
6namespace Doc.AspectConfiguration
7{
8    // The aspect itself, consuming the configuration.
9    public class LogAttribute : OverrideMethodAspect
10    {
11        public override dynamic? OverrideMethod()
12        {
13            var options = meta.Target.Method.Enhancements().GetOptions<LoggingOptions>();
14
15            var message = $"{options.Category}: Executing {meta.Target.Method}.";
16
17            switch ( options.Level!.Value )
18            {
19                case TraceLevel.Error:
20                    Trace.TraceError( message );
21
22                    break;
23
24                case TraceLevel.Info:
25                    Trace.TraceInformation( message );
26
27                    break;
28
29                case TraceLevel.Warning:
30                    Trace.TraceWarning( message );
31
32                    break;
33
34                case TraceLevel.Verbose:
35                    Trace.WriteLine( message );
36
37                    break;
38            }
39
40            return meta.Proceed();
41        }
42    }
43}
1using Metalama.Framework.Fabrics;
2using System.Diagnostics;
3using System.Linq;
4
5namespace Doc.AspectConfiguration
6{
7    // The project fabric configures the project at compile time.
8    public class Fabric : ProjectFabric
9    {
10        public override void AmendProject( IProjectAmender amender )
11        {
12            amender.Outbound
13                .SetOptions( new LoggingOptions { Category = "GeneralCategory", Level = TraceLevel.Info } );
14
15            amender.Outbound
16                .Select( x => x.GlobalNamespace.GetDescendant( "Doc.AspectConfiguration.ChildNamespace" )! )
17                .SetOptions( new LoggingOptions() { Category = "ChildCategory" } );
18
19            // Adds the aspect to all members.
20            amender.Outbound
21                .SelectMany( c => c.Types.SelectMany( t => t.Methods ) )
22                .AddAspectIfEligible<LogAttribute>();
23        }
24    }
25}
1using Metalama.Framework.Code;
2using Metalama.Framework.Diagnostics;
3using Metalama.Framework.Options;
4using Metalama.Framework.Project;
5using System;
6using System.Diagnostics;
7
8namespace Doc.AspectConfiguration
9{
10    // Options for the [Log] aspects.
11    public class LoggingOptions : IHierarchicalOptions<IMethod>, IHierarchicalOptions<INamedType>,
12                                  IHierarchicalOptions<INamespace>, IHierarchicalOptions<ICompilation>
13    {
14        public string? Category { get; init; }
15
16        public TraceLevel? Level { get; init; }
17
18        object IIncrementalObject.ApplyChanges( object changes, in ApplyChangesContext context )
19        {
20            var other = (LoggingOptions) changes;
21
22            return new LoggingOptions { Category = other.Category ?? this.Category, Level = other.Level ?? this.Level };
23        }
24    }
25}
Source Code
1namespace Doc.AspectConfiguration
2{


3    // Some target code.
4    public class SomeClass
5    {
6        [Log]
7        public void SomeMethod() { }
8    }
9



10    namespace ChildNamespace
11    {
12        public class SomeOtherClass
13        {
14            [Log]
15            public void SomeMethod() { }
16        }
17    }



18}
Transformed Code
1using System.Diagnostics;
2
3namespace Doc.AspectConfiguration
4{
5    // Some target code.
6    public class SomeClass
7    {
8        [Log]
9        public void SomeMethod()
10        {
11            Trace.TraceInformation("GeneralCategory: Executing SomeClass.SomeMethod().");
12        }
13    }
14
15    namespace ChildNamespace
16    {
17        public class SomeOtherClass
18        {
19            [Log]
20            public void SomeMethod()
21            {
22                Trace.TraceInformation("ChildCategory: Executing SomeOtherClass.SomeMethod().");
23            }
24        }
25    }
26}