Open sandboxFocusImprove this doc

LAMA5110: A static method can't use caching with dependency injection

Severity Error
Reported by Metalama.Patterns.Caching
Message The method '<Method>' cannot be static because caching is configured to use dependency injection, and a static method cannot access an injected dependency. Make the method non-static, or disable dependency injection by setting the UseDependencyInjection property of the [Cache] or [CachingConfiguration] attribute to false.

Cause

By default, the caching aspects get the caching service, ICachingService, through dependency injection: they introduce a field into the declaring type and pull it from the constructor. This field is an instance field, so a static method can't access it.

Metalama reports LAMA5110 in the following situations:

  • The CacheAttribute aspect is applied to a static method, and dependency injection isn't disabled for this method.
  • The InvalidateCacheAttribute aspect is applied to a static method, and at least one of the methods it invalidates uses dependency injection.

In both cases, <Method> is the static method. The aspect isn't applied.

When the [Cache] aspect is applied more than once to the same method, Metalama reports LAMA5112 instead.

In the following example, the static GetPrice method is cached, but dependency injection isn't disabled.

1using Metalama.Patterns.Caching.Aspects;
2
3namespace Doc.LAMA5110.Error;
4
5public class PriceCatalog
6{
7    // Error: the method is static, but caching uses dependency injection by default.
8    [Cache]
    Error LAMA5110: The method 'PriceCatalog.GetPrice(string)' cannot be static because caching is configured to use dependency injection, and a static method cannot access an injected dependency. Make the method non-static, or disable dependency injection by setting the UseDependencyInjection property of the [Cache] or [CachingConfiguration] attribute to false.

9    public static decimal GetPrice( string productId ) => productId.Length * 10m;
10}
11

Resolution

Do one of the following:

  • Disable dependency injection for the method by setting the UseDependencyInjection property of the [Cache] attribute to false. The aspect then uses the CachingService.Default service.
  • Disable dependency injection for a whole type or project by adding the CachingConfigurationAttribute attribute with UseDependencyInjection = false to the type or to the assembly, or by calling ConfigureCaching in a fabric.
  • Make the method non-static, if the declaring type is created by your dependency injection container.

For an [InvalidateCache] method, make the method non-static, or disable dependency injection for the methods it invalidates.

In the following example, dependency injection is disabled for the GetPrice method.

Source Code
1using Metalama.Patterns.Caching.Aspects;
2



3namespace Doc.LAMA5110.Fixed;
4
5public class PriceCatalog
6{
7    // Fixed: dependency injection is disabled for this method.
8    [Cache( UseDependencyInjection = false )]
9    public static decimal GetPrice( string productId ) => productId.Length * 10m;
10}
11
Transformed Code
1using System;
2using System.Reflection;
3using Metalama.Patterns.Caching;
4using Metalama.Patterns.Caching.Aspects;
5
6namespace Doc.LAMA5110.Fixed;
7
8public class PriceCatalog
9{
10    // Fixed: dependency injection is disabled for this method.
11    [Cache(UseDependencyInjection = false)]
12    public static decimal GetPrice(string productId)
13    {
14        static object? Invoke(object? instance, object?[] args)
15        {
16            return GetPrice_Source((string)args[0]);
17        }
18
19        return ((ICachingService)CachingService.Default).GetFromCacheOrExecute<decimal>(_cacheRegistration_GetPrice, null, new object[] { productId }, Invoke);
20    }
21
22    private static decimal GetPrice_Source(string productId) => productId.Length * 10m;
23
24    private static readonly CachedMethodMetadata _cacheRegistration_GetPrice;
25
26    static PriceCatalog()
27    {
28        _cacheRegistration_GetPrice = CachedMethodMetadata.Register(typeof(PriceCatalog).GetMethod("GetPrice", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null) ?? throw new MissingMethodException("The method 'PriceCatalog.GetPrice(string)' could not be found using reflection."), new CachedMethodConfiguration() { AbsoluteExpiration = null, AutoReload = null, IgnoreThisParameter = null, Priority = null, ProfileName = (string?)null, SlidingExpiration = null }, false);
29    }
30}
31

See also