Closed
Description
Background and Motivation
If you're declaring your own Configuration section like this:
{
"MyOwnRedis": {
"DefaultLease": "00:10:00",
"KeyPrefix": "my-app"
"ConnectionString": "this is the actual connection string for RedisCacheOptions"
}
}
and thus doing:
public class MyOwnRedisOptions
{
public const string SectionName = "MyOwnRedis";
public TimeSpan DefaultLease { get; set; }
public string KeyPrefix { get; set; } = default!;
public string ConnectionString { get; set; } = default!;
}
services
.AddOptions<MyOwnRedisOptions>
.BindConfiguration(MyOwnRedisOptions.SectionName)
.Validate(options => !string.IsNullOrWhiteSpace(options.ConnectionString), $"The configuration key for {MyOwnRedisOptions.SectionName}:{nameof(MyOwnRedisOptions.ConnectionString)} cannot be null or whitespace.")
.Validate(options => options...............);
//...
the moment you're trying to call AddStackExchangeRedisCache
you're ending up in a weird situation:
services.AddStackExchangeRedisCache(options =>
{
option.Connection = builders.Configuration.Get<string>($"{MyOwnRedisOptions.SectionName}:{nameof(MyOwnRedisOptions.ConnectionString)})
};
or
// this has the benefits or still running your Options Validation entiely
services.AddStackExchangeRedisCache(_ => {}) // this is super weird
services
.AddOptions<RedisCacheOptions>()
.Configure<IOptions<MyOwnRedisOptions>>((redisCacheOptions, myOwnRedisOptions) =>
{
redisCacheOptions.Configuration = myOwnRedisOptions.Value.ConnectionString;
});
};
Proposed API
- Add an overload where the
setupAction
is not required - Avoid nullable on
setupAction
Please provide the specific public API signature diff that you are proposing. For example:
namespace Microsoft.Extensions.DependencyInjection;
public static class StackExchangeRedisCacheServiceCollectionExtensions
{
+ public static IServiceCollection AddStackExchangeRedisCache(this IServiceCollection services)
+ public static IServiceCollection AddStackExchangeRedisOutputCache(this IServiceCollection services)
}
Usage Examples
services
.AddOptions<MyOwnRedisOptions>
.BindConfiguration(MyOwnRedisOptions.SectionName)
.Validate(options => !string.IsNullOrWhiteSpace(options.ConnectionString), "configuration for redis ConnectionString cannot be null or ...... ")
.Validate(options => options...............);
//...
services
.AddOptions<RedisCacheOptions>()
.Configure<IOptions<MyOwnRedisOptions>>((redisCacheOptions, myOwnRedisOptions) =>
{
redisCacheOptions.Configuration = myOwnRedisOptions.Value.ConnectionString;
});
};
services.AddStackExchangeRedisCache()
Alternative Designs
Not exactly a fan of using null
as optional overload in general. It opens the code to more nullability check or bug tracking NRE like "well I was sure I gave it a value".
PS: Only writing it for AddStackExchangeRedisCache
but it would be the the same for the OutputCache
as well
namespace Microsoft.Extensions.DependencyInjection;
public static class StackExchangeRedisCacheServiceCollectionExtensions
{
+ public static IServiceCollection AddStackExchangeRedisCache(this IServiceCollection services, Action<RedisCacheOptions> setupAction = null)
{
ArgumentNullThrowHelper.ThrowIfNull(services);
- ArgumentNullThrowHelper.ThrowIfNull(setupAction);
services.AddOptions();
- services.Configure(setupAction);
+ if(setupAction != null)
+ {
+ services.Configure(setupAction);
+ }
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCacheImpl>());
return services;
}
or
namespace Microsoft.Extensions.DependencyInjection;
public static class StackExchangeRedisCacheServiceCollectionExtensions
{
+ public static IServiceCollection AddStackExchangeRedisCache(this IServiceCollection services, Action<RedisCacheOptions> setupAction = null)
{
ArgumentNullThrowHelper.ThrowIfNull(services);
- ArgumentNullThrowHelper.ThrowIfNull(setupAction);
services.AddOptions();
- services.Configure(setupAction);
+ services.Configure(setupAction ?? _ => {});
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCacheImpl>());
return services;
}
Risks
The risk of bad setup/configuration is still owned by inner implementation such as RedisCache
/ RedisCacheImpl
etc ...