Open sandboxFocusImprove this doc

CAC005: Parameter Types of the Invalidating and Cached Methods Are Incompatible

Severity Error
Reported by PostSharp.Patterns.Caching
Message The [InvalidateCache] aspect applied to <InvalidatingMethod> cannot invalidate <CachedMethod>: the type of the '<Parameter>' parameter of the cached method is not compatible with the type of the '<Parameter>' parameter in the invalidating method.

Cause

To remove a value from the cache, the InvalidateCacheAttribute aspect rebuilds the cache key of the cached method, <CachedMethod>, by passing the arguments of the invalidating method, <InvalidatingMethod>, to the parameters of the same name. Each argument must therefore be a valid value of the parameter of the cached method: the type of the parameter of the invalidating method must be the same type, or a type derived from it or implementing it. PostSharp reports CAC005, on <InvalidatingMethod>, when this is not the case for the parameter named <Parameter>.

The check uses the rules of type assignment, not the conversions of C#: an int parameter does not match a long parameter, although C# converts an int to a long implicitly.

In the following example, the orderId parameter of GetOrderTotal is a long, but the orderId parameter of AddOrderLine is an int.

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

namespace Errors.CAC005_Error;

public class OrderService
{
    private readonly Dictionary<long, decimal> totals = new() { [1001] = 120.00m };

    [Cache]
    public decimal GetOrderTotal( long orderId )
    {
        Console.WriteLine( $"Computing the total of order {orderId}." );

        return this.totals[orderId];
    }

    [InvalidateCache( nameof(GetOrderTotal) )]
    public void AddOrderLine( int orderId, decimal amount ) // An int is not a long, although C# converts it implicitly.
    {
        this.totals[orderId] += amount;
    }
}

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

        var service = new OrderService();
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
        service.AddOrderLine( 1001, 30.00m );
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
    }
}
error CAC005: CAC005_Error.cs(21,17): The [InvalidateCache] aspect applied to Errors.CAC005_Error.OrderService.AddOrderLine(System.Int32, System.Decimal) cannot invalidate Errors.CAC005_Error.OrderService.GetOrderTotal(System.Int64): the type of the 'orderId' parameter of the cached method is not compatible with the type of the 'orderId' parameter in the invalidating method.

Resolution

  • Change the type of the parameter of the invalidating method to the type of the parameter of the cached method, or to a type derived from it.
  • If the parameter of the cached method does not affect its value, exclude it from the cache key with the NotCacheKeyAttribute attribute.

In the following example, the orderId parameter of AddOrderLine is a long, so the call after AddOrderLine computes the new total.

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

namespace Errors.CAC005_Fixed;

public class OrderService
{
    private readonly Dictionary<long, decimal> totals = new() { [1001] = 120.00m };

    [Cache]
    public decimal GetOrderTotal( long orderId )
    {
        Console.WriteLine( $"Computing the total of order {orderId}." );

        return this.totals[orderId];
    }

    [InvalidateCache( nameof(GetOrderTotal) )]
    public void AddOrderLine( long orderId, decimal amount ) // The parameter has the same type as in GetOrderTotal.
    {
        this.totals[orderId] += amount;
    }
}

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

        var service = new OrderService();
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
        service.AddOrderLine( 1001, 30.00m );
        Console.WriteLine( service.GetOrderTotal( 1001 ) );
    }
}
Computing the total of order 1001.
120.00
120.00
Computing the total of order 1001.
150.00

See Also

Reference

InvalidateCacheAttribute
NotCacheKeyAttribute

Other Resources

Removing Items From the Cache