Open sandboxFocusImprove this doc

LAMA5100: The invalidated method isn't cached

Severity Error
Reported by Metalama.Patterns.Caching
Message The [InvalidateCache] aspect applied to '<InvalidatingMethod>' cannot invalidate '<InvalidatedMethod>': this method is not cached by the [Cache] aspect.

Cause

The [InvalidateCache] aspect makes a method, called the invalidating method, remove from the cache the values of the methods whose names you pass to the attribute. These methods must be cached by the [Cache] aspect.

Metalama reports LAMA5100 when the aspect finds a method with the given name, but this method doesn't have the [Cache] aspect. In the message, <InvalidatingMethod> is the invalidating method and <InvalidatedMethod> is the method that isn't cached. When several overloads have the given name, the diagnostic is reported only if none of them can be invalidated.

In the following example, UpdatePrice invalidates GetPrice, but GetPrice doesn't have the [Cache] aspect.

1using Metalama.Patterns.Caching.Aspects;
2using System.Collections.Generic;
3
4namespace Doc.LAMA5100.Error;
5
6public sealed class ProductCatalogue
7{
8    private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
9
10    // Error: GetPrice is not cached, so it can't be invalidated.
11    public decimal GetPrice( string productId ) => this._prices[productId];
12
13    [InvalidateCache( nameof(GetPrice) )]
    Error LAMA5100: The [InvalidateCache] aspect applied to 'ProductCatalogue.UpdatePrice(string, decimal)' cannot invalidate 'ProductCatalogue.GetPrice(string)': this method is not cached by the [Cache] aspect.

14    public void UpdatePrice( string productId, decimal price ) => this._prices[productId] = price;
15}
16

Resolution

Do one of the following:

  • Add the [Cache] aspect to the invalidated method, as in the following example. You can also add it with a fabric.
  • If the method isn't meant to be cached, remove its name from the [InvalidateCache] attribute, or remove the attribute.
  • If you meant a method of another type, use the constructor of [InvalidateCache] that accepts the declaring type.

Source Code
1using Metalama.Patterns.Caching.Aspects;
2using System.Collections.Generic;

3

4namespace Doc.LAMA5100.Fixed;

5
6public sealed class ProductCatalogue
7{
8    private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
9
10    // Fixed: GetPrice is cached, so UpdatePrice can invalidate it.
11    [Cache]
12    public decimal GetPrice( string productId ) => this._prices[productId];
13
14    [InvalidateCache( nameof(GetPrice) )]






























15    public void UpdatePrice( string productId, decimal price ) => this._prices[productId] = price;
16}
17
Transformed Code
1using Metalama.Patterns.Caching;
2using Metalama.Patterns.Caching.Aspects;
3using System;
4using System.Collections.Generic;
5using System.Reflection;
6
7namespace Doc.LAMA5100.Fixed;
8
9public sealed class ProductCatalogue
10{
11    private readonly Dictionary<string, decimal> _prices = new() { ["corn"] = 100 };
12
13    // Fixed: GetPrice is cached, so UpdatePrice can invalidate it.
14    [Cache]
15    public decimal GetPrice(string productId)
16    {
17        static object? Invoke(object? instance, object?[] args)
18        {
19            return ((ProductCatalogue)instance).GetPrice_Source((string)args[0]);
20        }
21
22        return _cachingService.GetFromCacheOrExecute<decimal>(_cacheRegistration_GetPrice, this, new object[] { productId }, Invoke);
23    }
24
25    private decimal GetPrice_Source(string productId) => this._prices[productId];
26
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_9255DAD916F046F57653BEA20C82E79F[0], this, new object[] { productId });
33    }
34
35    private static readonly CachedMethodMetadata _cacheRegistration_GetPrice;
36    private ICachingService _cachingService;
37    private static MethodInfo[] _methodsInvalidatedBy_UpdatePrice_9255DAD916F046F57653BEA20C82E79F;
38
39    static ProductCatalogue()
40    {
41        _methodsInvalidatedBy_UpdatePrice_9255DAD916F046F57653BEA20C82E79F = 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

See also