leonibr / community-extensions-cache-postgres

A PostgreSQL Implementation of IDistributedCache interface. Using Postgresql as distributed cache in Asp.Net Core. NetStandard 2.0
59 stars 17 forks source link

Dependency Injection question #6

Closed nromano32 closed 3 years ago

nromano32 commented 3 years ago

I very likely doing this wrong but I just can't figure out what. I have a class that gets called via an AWS lambda project. I'm using Lambda to solve a scalability issue. Inside of this class I create my ServiceCollection in the below method

    public static ServiceProvider BuidServiceCollection()
    {
        ServiceCollection services = new ServiceCollection();

        // Add Postgres as Token cache store
        services.AddDbContext<IntegratedTokenCacheDbContext>(options =>
            options.UseNpgsql(EnvSettings.ConnectionString));

        services.AddDistributedMemoryCache()
            .AddDistributedMemoryCache();

        services.AddDistributedPostgreSqlCache(setup =>
        {
            setup.ConnectionString = EnvSettings.ConnectionString;
            setup.SchemaName = "public";
            setup.TableName = "microsoft_token_cache";
            setup.CreateInfrastructure = false;
            setup.DefaultSlidingExpiration = TimeSpan.FromHours(2);
            setup.DisableRemoveExpired = true;
        });

        services.AddLogging()
            .AddSingleton<IMsalTokenCacheProvider, MsalDistributedTokenCacheAdapter>()
            .AddScoped<IMsalAccountActivityStore, PostgreSQLMsalAccountActivityStore>();

        return services.BuildServiceProvider(false);
    }

everything works fine until I try to get my IMsalTokenCacheProvider.

This line produces the error.

Line MsalDistributedTokenCacheAdapter msalCache = (MsalDistributedTokenCacheAdapter)_serviceProvider.GetService();

Error System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.Extensions.Hosting.IHostApplicationLifetime' while attempting to activate 'Community.Microsoft.Extensions.Caching.PostgreSql.DatabaseExpiredItemsRemoverLoop'.'

Is this an issue related to not using DI in the traditional pattern (within the program startup or main)? My library has other classes that will not need this functionality so I would like to limit it to only one class. It would seem I'm pushing against the framework. Happy to share more code if needed.

Thanks for your help - Nick

leonibr commented 3 years ago

services.AddDistributedMemoryCache() should not exist because it will be overrided by AddDistributedPostgreSqlCache. IDistributedCache in the constructor is the way to go! If you want you can implement you own, but if you do you have to register it.

leonibr commented 3 years ago

by the way what version of dotnet you are using? IHostApplicationLifetime is for dotnet 3.0, 3.1 and 5.0, if you are on a lower version you can downgrade to version 2.x.x, it does not use IHostApplicationLifetime. Take a look at the samples if you still dont succed, you can put the smallest functional part in one git repo and I will be happy to take a closer look 😃

nromano32 commented 3 years ago

Thanks for the response! I'm writing in Core 3.1 sorry should have put that in the original post. I might have solved the issue but I'm just not sure if it is a stable solution yet. So as I said this is a class library that gets activated by a AWS Lambda call. I think my problem is unclear class lifetime. So I got an expected result by wrapping my service provider in the HostBuilder

private static IHost _host = null;

Constructor Line _host = HostHelper.BuidHost(); _msalAccountActivityStore = _host.Services.GetRequiredService();

I implemented IDisposable and added (to ensure the object are destroyed) public void Dispose() { // Dispose of unmanaged resources. // Dispose(true); // Suppress finalization. _host.Dispose(); _host = null; GC.SuppressFinalize(this); }

Do you think this can work or will is get unstable results. I have not found a single example of this solution and like I said I'm very new to DI.

Thanks for the help - Nick

leonibr commented 3 years ago

Yes, lambda functions are static meaning whatever you create at the begining you should dispose at the end or create once and reuse every time, they should not persist. It seems you already have your own implementation PostgreSQLMsalAccountActivityStore , and when using DI I would specify the type, inseted of:

// I don't know how this works
(MyImplementation)_serviceProvider.GetService();
// -> I know this works
var myInstance = _serviceProvider.GetService<MyImplementation>();
nromano32 commented 3 years ago

Thx for the suggestion. I implemented it ... everything is working now.

Best - Nick