Search code examples
c#asp.net-coreautofacrate-limiting

AspNetCoreRateLimit .NET Core 3.0 - Cannot resolve parameter IMemoryCache cache


Since switching to .NET Core 3.0 and 3.1 I've been getting the following error with AspNetCoreRateLimit when the application/API starts up:

'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'AspNetCoreRateLimit.MemoryCacheRateLimitCounterStore' can be invoked with the available services and parameters: Cannot resolve parameter 'Microsoft.Extensions.Caching.Memory.IMemoryCache cache' of constructor 'Void .ctor(Microsoft.Extensions.Caching.Memory.IMemoryCache)'.

My service configuration is like this:

services.AddControllers();

        services.AddApiVersioning(options =>
        {
            options.ReportApiVersions = true;
            options.ApiVersionReader = new UrlSegmentApiVersionReader();
        })
        .AddVersionedApiExplorer(options =>
        {
            options.GroupNameFormat = "'v'VVV";
            options.SubstituteApiVersionInUrl = true;
        })

        // Register the Swagger generation with the default options
        .AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>()
        .AddSwaggerGen(options =>
        {
            options.OperationFilter<SwaggerDefaultValues>();
            options.CustomSchemaIds(x => x.FullName);
        });

        services.AddCors();

        //add API throttling configuration
        services.Configure<CView.Core.Web.Settings.IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"))
            .AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>()
            .AddSingleton<IMemoryCache, MemoryCache>()
            .AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>()
            .AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>()
            .AddResponseCompression()
            .Configure<ExceptionHandlingOptions>(Configuration.GetSection("ExceptionHandlingOptions"))
            .Configure<ApiBehaviorOptions>(opt => { opt.SuppressModelStateInvalidFilter = true; })
            .Configure<RabbitMqMessageBus>(GetRabbitMqConfigurationSection())
            .AddMassTransit(x =>
            {
                x.AddBus(ConfigureRabbitMq);
                x.AddConsumer<CompanyNameUpdatedConsumer>();
            });

        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        services.AddSingleton<IHostedService, RabbitMqHostedService>();
        services.AddAutoMapper(Assembly.GetAssembly(typeof(AutoMapperModule))); //If you have other mapping profiles defined, that profiles will be loaded too.
        services.Configure<Auth0Options>(Configuration.GetSection("Auth0"));

        var auth0Domain = $"{Configuration["Auth0:Domain"]}";

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.Authority = auth0Domain;
            options.Audience = Configuration["Auth0:Audience"];
        });

I understand the error is saying it can't resolve the dependency IMemoryCache and by adding the following to the startup I'm able to get rid of it:

services.AddSingleton<IMemoryCache, MemoryCache>()

But what concerns me is this didn't happen in earlier versions of .NET Core, it's not in any of the AspNetCoreRateLimit docs and I don't really know understand the implications of simply adding injecting the MemoryCache are!

Can anyone help me figure out what I'm missing/am doing wrong and why this has started happening in the new versions of .NET Core but just works in .NET Core 2.1?


Solution

  • You are adding an IRateLimitCounterStore to the pipeline here:

    .AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>()
    

    You can see from the source that the MemoryCacheRateLimitCounterStore class takes an IMemoryCache in its constructor:

    public MemoryCacheRateLimitCounterStore(IMemoryCache cache) : base(cache)
    {
    }
    

    If you don't provide an IMemoryCache to your pipeline, this class can't be constructed through DI (that's what the error is telling you).

    Looking at the history of the source file, it appears to have always required that parameter to its constructor. Perhaps, in version 2.1, some other service was adding an IMemoryCache behind the scenes but is no longer adding one for you in 3.0.

    There is no real concern by adding a memory cache - it was always being added somehow as long as you've been using MemoryCacheRateLimitCounterStore. Seems you just need to add it yourself at this point.