Open sandboxFocusImprove this doc

CAC004: Invalidating Method Lacks a Parameter of the Cached Method

Severity Error
Reported by PostSharp.Patterns.Caching
Message The [InvalidateCache] aspect applied to <InvalidatingMethod> cannot invalidate <CachedMethod>: <InvalidatingMethod> does not contain a parameter named '<Parameter>'. Add a parameter named '<Parameter>' to <InvalidatingMethod>, or add the [NotCacheKey] attribute to the '<Parameter>' parameter of <CachedMethod>.

Cause

To remove a value from the cache, the InvalidateCacheAttribute aspect rebuilds the cache key of the cached method, <CachedMethod>, from the arguments of the invalidating method, <InvalidatingMethod>. It matches each parameter of the cached method with the parameter of the same name of the invalidating method, regardless of their order. PostSharp reports CAC004, on <InvalidatingMethod>, when the invalidating method has no parameter named <Parameter>, and this parameter of the cached method is part of the cache key, that is, it does not have the NotCacheKeyAttribute attribute.

The match is by name only, so two parameters of the same type and meaning but with different names do not match.

In the following example, the parameter of GetPrice is named productId, but the parameter of SetPrice is named id.

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

namespace Errors.CAC004_Error;

public class ProductCatalog
{
    private readonly Dictionary<string, decimal> prices = new() { ["TEA-01"] = 4.50m };

    [Cache]
    public decimal GetPrice( string productId )
    {
        Console.WriteLine( $"Reading the price of {productId}." );

        return this.prices[productId];
    }

    [InvalidateCache( nameof(GetPrice) )]
    public void SetPrice( string id, decimal price ) // No parameter is named productId, as in GetPrice.
    {
        this.prices[id] = price;
    }
}

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

        var catalog = new ProductCatalog();
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
        catalog.SetPrice( "TEA-01", 5.00m );
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
    }
}
error CAC004: CAC004_Error.cs(21,17): The [InvalidateCache] aspect applied to Errors.CAC004_Error.ProductCatalog.SetPrice(System.String, System.Decimal) cannot invalidate Errors.CAC004_Error.ProductCatalog.GetPrice(System.String): Errors.CAC004_Error.ProductCatalog.SetPrice(System.String, System.Decimal) does not contain a parameter named 'productId'. Add a parameter named 'productId' to Errors.CAC004_Error.ProductCatalog.SetPrice(System.String, System.Decimal), or add the [NotCacheKey] attribute to the 'productId' parameter of Errors.CAC004_Error.ProductCatalog.GetPrice(System.String).

Resolution

  • Rename the parameter of the invalidating method so that it has the name of the parameter of the cached method, or add a parameter of this name.
  • If the parameter of the cached method does not affect its value, exclude it from the cache key with the [NotCacheKey] attribute.

In the following example, the parameter of SetPrice is renamed to productId, so the call after SetPrice reads the new price.

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

namespace Errors.CAC004_Fixed;

public class ProductCatalog
{
    private readonly Dictionary<string, decimal> prices = new() { ["TEA-01"] = 4.50m };

    [Cache]
    public decimal GetPrice( string productId )
    {
        Console.WriteLine( $"Reading the price of {productId}." );

        return this.prices[productId];
    }

    [InvalidateCache( nameof(GetPrice) )]
    public void SetPrice( string productId, decimal price ) // The parameter has the same name as in GetPrice.
    {
        this.prices[productId] = price;
    }
}

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

        var catalog = new ProductCatalog();
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
        catalog.SetPrice( "TEA-01", 5.00m );
        Console.WriteLine( catalog.GetPrice( "TEA-01" ) );
    }
}
Reading the price of TEA-01.
4.50
4.50
Reading the price of TEA-01.
5.00

See Also

Reference

InvalidateCacheAttribute
NotCacheKeyAttribute

Other Resources

Removing Items From the Cache
Customizing Cache Keys