dotnetcore / EasyCaching

:boom: EasyCaching is an open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier!
MIT License
1.95k stars 322 forks source link

AddEasyCaching access to IServiceProvider - Redis Services are resolved at runtime via another Service #273

Open Simonl9l opened 3 years ago

Simonl9l commented 3 years ago

Hi - We're trying to use the caching in an environment were the Redis endpoints are discoverable via a service registry (consul). It would be great if the AddEasyCaching configuration action had access to the service provider.

Do you have any recommendation to best active this with what is available today, given that the service (per RedisEndpoints) below is registered elsewhere, and it being not best practice to cal' BuildServiceProvider() directly.

The EF Core DBContext AddDBContext service extension for configuration as an example does provide access to the service provider,

It would be great if in future versions I could do something like the following:

 services.AddEasyCaching((serviceProvider, option) =>
{
    option.UseRedis(config =>
    {
        config.DBConfig.AllowAdmin = true;
        serviceProvider.GetRequiredService<RedisEndpoints>().ForEach(endpoint => config.DBConfig.Endpoints.Add(new ServerEndPoint(endpoint.Host, endpoint.Port)));
    }, providerName);
});
Simonl9l commented 3 years ago

An additional path here is that we're already using the StackExchangeRedis package ourselves elsewhere, (that we have the ConnectionMultiplexer encapsulated in another service).

Would there be a possible future alternate configuration approach where we can use a variation of the MyAddEasyCaching helper with with access to the IServiceProvider context sour that we can just retrieved the ConnectionMultiplexer and pass that straight in ?

catcherwong commented 3 years ago

@Simonl9l Thanks for your interest in this project.

We will take a look ASAP.

Simonl9l commented 3 years ago

@catcherwong thanks! very impressed by what we see with this in conjunction with the EF Core Second Level Cache Interceptor so all help greatly appreciated!

Simonl9l commented 3 years ago

@catcherwong hi - any word on when this might be addressed?

catcherwong commented 3 years ago

@Simonl9l I'm very sorry, I don't have enough time to deal with this issue right now.

Simonl9l commented 3 years ago

@catcherwong OK - do please let us know what you do have enough "ASAP" time to take a look...

catcherwong commented 3 years ago

Redis endpoints are discoverable via a service registry (consul). It would be great if the AddEasyCaching configuration action had access to the service provider.

If your Redis endpoints are discoverable via a service registry, you can do something combine service registry and Microsoft.Extensions.Configuration, so that you can read the endpoints from the service registry.

catcherwong commented 3 years ago

Would there be a possible future alternate configuration approach where we can use a variation of the MyAddEasyCaching helper with with access to the IServiceProvider context

I am not sure AddEasyCaching access IServiceProvider is a good idea or not. This one needs to hear if other people have other opinions.

Simonl9l commented 3 years ago

@catcherwong thanks for the followup - per my code above, the endpoints are not available from configuration but via another service call to a registry service implementation (that makes HTTP calls via an HttpClient to consul API, that also needs to be first registered in the service container that would not be available in the Hosts ConfigureAppConfiguration).

The challenge is with the startup service configuration code in that we have a chicken-egg problem, that until the services container is built and made accessible via an IServicerProvider, I can't access that service in the EasyCaching .UseRedis configuration.

The can't is more one should not by best practice call BuildServiceProvider multiple times (onc really should really leave to the HostBuilder), or with some implicit expectation of registration ordering.

By making the IServiceProvider available with the configuration options (as is by default otherwise available, and made accessible in the default service registration code - via a `Func<IServiceProvider., ...>, and many other extensions out there) it makes each service configuration/startup more a lazy loading concept, once the service is initially requested, eliminating this issue with service dependencies.

As I understand it, when the service provider is built and all the available services are registered, that when a given service is requested the first time, that services configuration function is executed. Any dependent services will be identified by the service container as they are also retrieved - or others DI'd - and as applicable their startup function will each be executed first.

Whist I have something that works it's not ideal. With out the IServiceProvider being available in the service extension, this is the code I need to use today that is not best practice (note the call to services.BuildServiceProvider():

services.AddEasyCaching(option =>
{
    var endpoints = services.BuildServiceProvider().GetRequiredService<RedisEndpoints>().Endpoints;

    option.UseRedis(config =>
    {
        config.DBConfig.AllowAdmin = true;
        endpoints.ForEach(endpoint => config.DBConfig.Endpoints.Add(new ServerEndPoint(endpoint.Host, endpoint.Port)));
    }, providerName);
});

where the services variable is that scoped inside the public void ConfigureServices(IServiceCollection services) as what ever point it is in that functions implementation.

Does this explain the needs better - just trying to use dotnet core service registration as designed...?

shamiz01 commented 3 years ago

chegoone iran,tehran https://chegoone.info/download-youtube-videos/ Download tutorial from YouTube

cmcjcharters commented 2 years ago

Just a bump on this thread, having a mechanism to access IServiceProvider (e.g. a new parameter Func<IServiceProvider , ...> as suggested by @Simonl9l would be immensely helpful, and as mentioned, better aligned with the intent of service registration by avoiding explicit use of services.BuildServiceProvider()