| Severity | Error |
| Reported by | Metalama.Patterns.Caching |
| Message | Invalid [InvalidateCache] aspect on '<InvalidatingMethod>': there is no method named '<MethodName>' in type '<DeclaringType>'. |
Cause
The [InvalidateCache] aspect looks up the cached methods by name among all methods of a type, including inherited ones. By default, this type is the type that declares the invalidating method. When you pass a type as the first argument of the constructor, the aspect searches this type instead.
Metalama reports LAMA5106 when no method of the given name exists in this type. In the message, <InvalidatingMethod> is the invalidating method, <MethodName> is the name that couldn't be found, and <DeclaringType> is the searched type. The typical causes are:
- a typo in a method name written as a string literal;
- a cached method that's declared in another type, while the aspect doesn't specify this type.
If a method of the given name exists but can't be invalidated, for instance because it isn't cached or because its parameters don't match, Metalama reports a different error.
In the following example, the method name is misspelled.
1using Metalama.Patterns.Caching.Aspects;
2using System.Collections.Generic;
3
4namespace Doc.LAMA5106.Error;
5
6public sealed partial class ProductCatalogue
7{
8 private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
9
10 [Cache]
11 public decimal GetPrice( string productId ) => this._prices[productId];
12
13 // Error: there's no method named GetPrices in ProductCatalogue.
14 [InvalidateCache( "GetPrices" )]
Error LAMA5106: Invalid [InvalidateCache] aspect on 'ProductCatalogue.UpdatePrice(string, decimal)': there is no method named 'GetPrices' in type 'ProductCatalogue'.
15 public void UpdatePrice( string productId, decimal price ) => this._prices[productId] = price;
16}
17
Resolution
- Use the
nameofoperator instead of a string literal. The C# compiler then checks that the method exists. - If the cached method belongs to another type, pass this type as the first argument of the aspect, before the method names.
In the following example, the method name is written with nameof.
1using Metalama.Patterns.Caching.Aspects;
2using System.Collections.Generic;
3
4namespace Doc.LAMA5106.Fixed;
5
6public sealed partial class ProductCatalogue
7{
8 private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
9
10 [Cache]
11 public decimal GetPrice( string productId ) => this._prices[productId];
12
13 // Fixed: nameof guarantees that the method exists.
14 [InvalidateCache( nameof(GetPrice) )]
15 public void UpdatePrice( string productId, decimal price ) => this._prices[productId] = price;
16}
17
1using Metalama.Patterns.Caching;
2using Metalama.Patterns.Caching.Aspects;
3using System;
4using System.Collections.Generic;
5using System.Reflection;
6
7namespace Doc.LAMA5106.Fixed;
8
9public sealed partial class ProductCatalogue
10{
11 private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
12
13 [Cache]
14 public decimal GetPrice(string productId)
15 {
16 static object? Invoke(object? instance, object?[] args)
17 {
18 return ((ProductCatalogue)instance).GetPrice_Source((string)args[0]);
19 }
20
21 return _cachingService.GetFromCacheOrExecute<decimal>(_cacheRegistration_GetPrice, this, new object[] { productId }, Invoke);
22 }
23
24 private decimal GetPrice_Source(string productId) => this._prices[productId];
25
26 // Fixed: nameof guarantees that the method exists.
27 [InvalidateCache(nameof(GetPrice))]
28 public void UpdatePrice(string productId, decimal price)
29 {
30 this._prices[productId] = price;
31 object result = null;
32 _cachingService.Invalidate(_methodsInvalidatedBy_UpdatePrice_04CE964243E3A79953111EB334F8B65C[0], this, new object[] { productId });
33 }
34
35 private static readonly CachedMethodMetadata _cacheRegistration_GetPrice;
36 private ICachingService _cachingService;
37 private static MethodInfo[] _methodsInvalidatedBy_UpdatePrice_04CE964243E3A79953111EB334F8B65C;
38
39 static ProductCatalogue()
40 {
41 _methodsInvalidatedBy_UpdatePrice_04CE964243E3A79953111EB334F8B65C = new MethodInfo[] {
42 typeof(ProductCatalogue).GetMethod("GetPrice", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null) ?? throw new MissingMethodException("The method 'ProductCatalogue.GetPrice(string)' could not be found using reflection.")
43 };
44 _cacheRegistration_GetPrice = CachedMethodMetadata.Register(typeof(ProductCatalogue).GetMethod("GetPrice", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null) ?? throw new MissingMethodException("The method 'ProductCatalogue.GetPrice(string)' could not be found using reflection."), new CachedMethodConfiguration() { AbsoluteExpiration = null, AutoReload = null, IgnoreThisParameter = null, Priority = null, ProfileName = (string?)null, SlidingExpiration = null }, false);
45 }
46
47 public ProductCatalogue(ICachingService? cachingService = null)
48 {
49 this._cachingService = cachingService ?? throw new System.ArgumentNullException(nameof(cachingService));
50 }
51}
52