Open sandboxFocusImprove this doc

CAC008: Method to Invalidate Not Found

Severity Error
Reported by PostSharp.Patterns.Caching
Message Invalid [InvalidateCache] aspect on <Method>: there is no method named <MethodName> in type <Type>. To invalidate a method of another type, pass this type as the first argument of the [InvalidateCache] attribute.

Cause

The InvalidateCacheAttribute aspect removes from the cache the values of the cached methods whose names you pass to its constructor. It looks for these methods in <Type>: the type that you pass as the first argument of the constructor or, when you pass only names, the type that declares the invalidating method <Method>. PostSharp reports CAC008 when <Type> has no method named <MethodName>, neither public nor private, neither static nor instance.

The most common cause is a cached method that belongs to another type, such as a repository, when the aspect does not name this type. The nameof operator does not help in this case: nameof(CustomerRepository.GetCustomerName) gives only the name GetCustomerName, not its type. A misspelled name in a string literal also causes the message.

PostSharp reports the message on the declaration of the invalidating method.

In the following example, RenameCustomer in CustomerService invalidates GetCustomerName, which CustomerRepository declares.

using System;
using System.Collections.Generic;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;

namespace Errors.CAC008_Error;

public class CustomerRepository
{
    internal static readonly Dictionary<int, string> Names = new() { [1] = "Alice" };

    [Cache( IgnoreThisParameter = true )]
    public string GetCustomerName( int customerId )
    {
        Console.WriteLine( $"Loading customer {customerId} from the database." );

        return Names[customerId];
    }
}

public class CustomerService
{
    // The aspect looks for GetCustomerName in CustomerService, not in CustomerRepository.
    [InvalidateCache( nameof(CustomerRepository.GetCustomerName) )]
    public void RenameCustomer( int customerId, string name )
    {
        CustomerRepository.Names[customerId] = name;
    }
}

public static class Program
{
    public static void Main()
    {
        CachingServices.DefaultService.Backend = new MemoryCachingBackend();

        var repository = new CustomerRepository();
        var service = new CustomerService();
        Console.WriteLine( repository.GetCustomerName( 1 ) );
        Console.WriteLine( repository.GetCustomerName( 1 ) );
        service.RenameCustomer( 1, "Alicia" );
        Console.WriteLine( repository.GetCustomerName( 1 ) );
    }
}
error CAC008: CAC008_Error.cs(25,17): Invalid [InvalidateCache] aspect on Errors.CAC008_Error.CustomerService.RenameCustomer(System.Int32, System.String): there is no method named GetCustomerName in type Errors.CAC008_Error.CustomerService. To invalidate a method of another type, pass this type as the first argument of the [InvalidateCache] attribute.

Resolution

  • When the cached method belongs to another type, pass this type as the first argument of the InvalidateCacheAttribute constructor, before the method names. If the cached method is an instance method and the invalidating method is not in the same type or a derived type, the instance must also be excluded from the cache key with the IgnoreThisParameter property, otherwise PostSharp reports CAC003.
  • Otherwise, correct the name, preferably with the nameof operator.

In the following example, the aspect names CustomerRepository, and the call to RenameCustomer removes the cached name, so the last call loads it again.

using System;
using System.Collections.Generic;
using PostSharp.Patterns.Caching;
using PostSharp.Patterns.Caching.Backends;

namespace Errors.CAC008_Fixed;

public class CustomerRepository
{
    internal static readonly Dictionary<int, string> Names = new() { [1] = "Alice" };

    [Cache( IgnoreThisParameter = true )]
    public string GetCustomerName( int customerId )
    {
        Console.WriteLine( $"Loading customer {customerId} from the database." );

        return Names[customerId];
    }
}

public class CustomerService
{
    // The first argument gives the type that declares the cached method.
    [InvalidateCache( typeof(CustomerRepository), nameof(CustomerRepository.GetCustomerName) )]
    public void RenameCustomer( int customerId, string name )
    {
        CustomerRepository.Names[customerId] = name;
    }
}

public static class Program
{
    public static void Main()
    {
        CachingServices.DefaultService.Backend = new MemoryCachingBackend();

        var repository = new CustomerRepository();
        var service = new CustomerService();
        Console.WriteLine( repository.GetCustomerName( 1 ) );
        Console.WriteLine( repository.GetCustomerName( 1 ) );
        service.RenameCustomer( 1, "Alicia" );
        Console.WriteLine( repository.GetCustomerName( 1 ) );
    }
}
Loading customer 1 from the database.
Alice
Alice
Loading customer 1 from the database.
Alicia

See Also

Reference

InvalidateCacheAttribute
CacheAttribute

Other Resources

Removing Items From the Cache