| Severity | Error |
| Reported by | Metalama.Patterns.Caching |
| Message | The [Cache] aspect cannot be applied to the method '<Method>' because the classifier '<Classifier>' marked the parameter '<ParameterName>' as ineligible. |
Cause
A parameter classifier is a compile-time class implementing ICacheParameterClassifier, registered with the AddParameterClassifier method of the caching options, usually from a fabric. For each parameter of a method cached with the CacheAttribute aspect, the classifier decides whether the parameter is part of the cache key, is excluded from it, or makes the method impossible to cache.
Metalama reports LAMA5111 when a classifier returns CacheParameterClassification.Ineligible() for a parameter. Classifiers typically do this for parameter types that can't be turned into a reliable cache key. In the message, <Classifier> is the classifier, shown as the result of its ToString method, which is the full name of its type by default, and <ParameterName> is the name of the parameter. The [Cache] aspect isn't applied.
In the following example, a classifier marks all parameters of type Stream as ineligible, and the cached CountLines method has a Stream parameter.
1using Metalama.Framework.Code;
2using Metalama.Patterns.Caching.Aspects.Configuration;
3using System.IO;
4
5namespace Doc.LAMA5111.Error;
6
7public class StreamParameterClassifier : ICacheParameterClassifier
8{
9 // A stream can't be part of a cache key, so methods with a Stream parameter can't be cached.
10 public CacheParameterClassification GetClassification( IParameter parameter )
11 => parameter.Type.IsConvertibleTo( typeof(Stream) )
12 ? CacheParameterClassification.Ineligible()
13 : CacheParameterClassification.Default;
14}
15
1using Metalama.Framework.Fabrics;
2using Metalama.Patterns.Caching.Aspects.Configuration;
3
4namespace Doc.LAMA5111.Error;
5
6internal class Fabric : ProjectFabric
7{
8 public override void AmendProject( IProjectAmender amender )
9 => amender.ConfigureCaching( caching => caching.AddParameterClassifier(
10 "Stream",
11 new StreamParameterClassifier() ) );
12}
13
1using Metalama.Patterns.Caching.Aspects;
2using System.IO;
3
4namespace Doc.LAMA5111.Error;
5
6public class DocumentService
7{
8 // Error: the classifier marks the Stream parameter as ineligible.
9 [Cache]
Error LAMA5111: The [Cache] aspect cannot be applied to the method 'DocumentService.CountLines(Stream)' because the classifier 'Doc.LAMA5111.Error.StreamParameterClassifier' marked the parameter 'stream' as ineligible.
10 public int CountLines( Stream stream )
11 {
12 using var reader = new StreamReader( stream );
13
14 return reader.ReadToEnd().Split( '\n' ).Length;
15 }
16}
17
Resolution
Do one of the following:
- Change the method so that its parameters can be part of the cache key. In the following example, the method receives the path of the file instead of a stream.
- Remove the [Cache] aspect from the method if the method can't be cached.
- If the parameter doesn't affect the result, change the classifier so that it returns CacheParameterClassification.ExcludeFromCacheKey for this parameter instead of Ineligible().
1using Metalama.Framework.Code;
2using Metalama.Patterns.Caching.Aspects.Configuration;
3using System.IO;
4
5namespace Doc.LAMA5111.Fixed;
6
7public class StreamParameterClassifier : ICacheParameterClassifier
8{
9 // A stream can't be part of a cache key, so methods with a Stream parameter can't be cached.
10 public CacheParameterClassification GetClassification( IParameter parameter )
11 => parameter.Type.IsConvertibleTo( typeof(Stream) )
12 ? CacheParameterClassification.Ineligible()
13 : CacheParameterClassification.Default;
14}
15
1using Metalama.Framework.Fabrics;
2using Metalama.Patterns.Caching.Aspects.Configuration;
3
4namespace Doc.LAMA5111.Fixed;
5
6internal class Fabric : ProjectFabric
7{
8 public override void AmendProject( IProjectAmender amender )
9 => amender.ConfigureCaching( caching => caching.AddParameterClassifier(
10 "Stream",
11 new StreamParameterClassifier() ) );
12}
13
1using Metalama.Patterns.Caching.Aspects;
2using System.IO;
3
4namespace Doc.LAMA5111.Fixed;
5
6public class DocumentService
7{
8 // Fixed: the method receives the file path, which can be part of the cache key.
9 [Cache]
10 public int CountLines( string path )
11 {
12 return File.ReadAllLines( path ).Length;
13 }
14}
15
1using Metalama.Framework.RunTime;
2using Metalama.Patterns.Caching;
3using Metalama.Patterns.Caching.Aspects;
4using System;
5using System.IO;
6using System.Reflection;
7
8namespace Doc.LAMA5111.Fixed;
9
10public class DocumentService
11{
12 // Fixed: the method receives the file path, which can be part of the cache key.
13 [Cache]
14 public int CountLines(string path)
15 {
16 static object? Invoke(object? instance, object?[] args)
17 {
18 return ((DocumentService)instance).CountLines_Source((string)args[0]);
19 }
20
21 return _cachingService.GetFromCacheOrExecute<int>(_cacheRegistration_CountLines, this, new object[] { path }, Invoke);
22 }
23
24 private int CountLines_Source(string path)
25 {
26 return File.ReadAllLines(path).Length;
27 }
28
29 private static readonly CachedMethodMetadata _cacheRegistration_CountLines;
30 private ICachingService _cachingService;
31
32 static DocumentService()
33 {
34 _cacheRegistration_CountLines = CachedMethodMetadata.Register(typeof(DocumentService).GetMethod("CountLines", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null) ?? throw new MissingMethodException("The method 'DocumentService.CountLines(string)' could not be found using reflection."), new CachedMethodConfiguration() { AbsoluteExpiration = null, AutoReload = null, IgnoreThisParameter = null, Priority = null, ProfileName = (string?)null, SlidingExpiration = null }, false);
35 }
36
37 public DocumentService([AspectGenerated] ICachingService? cachingService = null)
38 {
39 this._cachingService = cachingService ?? throw new System.ArgumentNullException(nameof(cachingService));
40 }
41}
42