Open sandboxFocusImprove this doc

LAMA5104: A method name passed to [InvalidateCache] is empty

Severity Error
Reported by Metalama.Patterns.Caching
Message Invalid [InvalidateCache] aspect on '<InvalidatingMethod>': the constructor parameters cannot contain a null, empty or whitespace-only string.

Cause

The [InvalidateCache] aspect receives, in its constructor, the names of the cached methods whose results it removes from the cache. At compile time, the aspect looks up each of these methods by name.

Metalama reports LAMA5104 when one of these names is null, an empty string, or a string that contains only whitespace, because such a name can't designate any method. The <InvalidatingMethod> placeholder is the invalidating method, that is, the method to which the aspect is applied.

In the following example, the method name passed to the aspect is empty.

1using Metalama.Patterns.Caching.Aspects;
2using System.Collections.Generic;
3
4namespace Doc.LAMA5104.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: the name of the method to invalidate is empty.
14    [InvalidateCache( "" )]
    Error LAMA5104: Invalid [InvalidateCache] aspect on 'ProductCatalogue.UpdatePrice(string, decimal)': the constructor parameters cannot contain a null, empty or whitespace-only string.

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

Resolution

Pass the name of the cached method to invalidate. Use the nameof operator instead of a string literal, so that the C# compiler checks that the method exists.

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

3

4namespace Doc.LAMA5104.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: the aspect names the cached method with nameof.










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.LAMA5104.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: the aspect names the cached method with nameof.
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_E26CCAA37A17AD40BE23ADB511D2FCF8[0], this, new object[] { productId });
33    }
34
35    private static readonly CachedMethodMetadata _cacheRegistration_GetPrice;
36    private ICachingService _cachingService;
37    private static MethodInfo[] _methodsInvalidatedBy_UpdatePrice_E26CCAA37A17AD40BE23ADB511D2FCF8;
38
39    static ProductCatalogue()
40    {
41        _methodsInvalidatedBy_UpdatePrice_E26CCAA37A17AD40BE23ADB511D2FCF8 = 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