@@ -3,7 +3,9 @@ namespace Microsoft.Extensions.DependencyInjection
33{
44 using System ;
55 using Foundatio . Caching ;
6+ using Foundatio . Serializer ;
67 using Microsoft . Extensions . Logging ;
8+ using Microsoft . Extensions . Options ;
79 using Neolution . Extensions . Caching . Abstractions ;
810 using Neolution . Extensions . Caching . RedisHybrid ;
911 using StackExchange . Redis ;
@@ -19,8 +21,20 @@ public static class ServiceCollectionExtensions
1921 /// <param name="services">The service collection.</param>
2022 /// <param name="redisConnectionString">The Redis connection string.</param>
2123 /// <returns>The service collection for fluent chaining.</returns>
24+ /// <exception cref="ArgumentNullException">Thrown when services or redisConnectionString is null.</exception>
25+ /// <exception cref="ArgumentException">Thrown when redisConnectionString is empty or whitespace.</exception>
2226 public static IServiceCollection AddRedisHybridCache ( this IServiceCollection services , string redisConnectionString )
2327 {
28+ if ( services == null )
29+ {
30+ throw new ArgumentNullException ( nameof ( services ) ) ;
31+ }
32+
33+ if ( string . IsNullOrWhiteSpace ( redisConnectionString ) )
34+ {
35+ throw new ArgumentException ( "Redis connection string cannot be null or empty." , nameof ( redisConnectionString ) ) ;
36+ }
37+
2438 return services . AddRedisHybridCache ( redisConnectionString , _ => { } ) ;
2539 }
2640
@@ -32,32 +46,144 @@ public static IServiceCollection AddRedisHybridCache(this IServiceCollection ser
3246 /// <param name="redisConnectionString">The Redis connection string.</param>
3347 /// <param name="configureOptions">The action to configure cache options.</param>
3448 /// <returns>The service collection for fluent chaining.</returns>
35- /// <exception cref="ArgumentNullException">Thrown when configureOptions is null.</exception>
49+ /// <exception cref="ArgumentNullException">Thrown when services, redisConnectionString, or configureOptions is null.</exception>
50+ /// <exception cref="ArgumentException">Thrown when redisConnectionString is empty or whitespace.</exception>
3651 public static IServiceCollection AddRedisHybridCache ( this IServiceCollection services , string redisConnectionString , Action < RedisHybridCacheOptions > configureOptions )
3752 {
53+ if ( services == null )
54+ {
55+ throw new ArgumentNullException ( nameof ( services ) ) ;
56+ }
57+
58+ if ( string . IsNullOrWhiteSpace ( redisConnectionString ) )
59+ {
60+ throw new ArgumentException ( "Redis connection string cannot be null or empty." , nameof ( redisConnectionString ) ) ;
61+ }
62+
3863 if ( configureOptions == null )
3964 {
4065 throw new ArgumentNullException ( nameof ( configureOptions ) ) ;
4166 }
4267
4368 services . AddSingleton < IConnectionMultiplexer > ( sp => ConnectionMultiplexer . Connect ( redisConnectionString ) ) ;
4469
70+ return services . AddRedisHybridCacheCore ( configureOptions ) ;
71+ }
72+
73+ /// <summary>
74+ /// Adds the Redis hybrid cache implementation using an existing <see cref="IConnectionMultiplexer"/> instance.
75+ /// </summary>
76+ /// <param name="services">The service collection.</param>
77+ /// <param name="multiplexer">The pre-created <see cref="IConnectionMultiplexer"/> instance.</param>
78+ /// <returns>The service collection for fluent chaining.</returns>
79+ /// <exception cref="ArgumentNullException">Thrown when services or multiplexer is null.</exception>
80+ /// <remarks>
81+ /// This overload is recommended when sharing the same Redis connection across multiple services
82+ /// (e.g., Data Protection, Session State, SignalR backplane) to avoid creating multiple connections.
83+ /// </remarks>
84+ public static IServiceCollection AddRedisHybridCache ( this IServiceCollection services , IConnectionMultiplexer multiplexer )
85+ {
86+ if ( services == null )
87+ {
88+ throw new ArgumentNullException ( nameof ( services ) ) ;
89+ }
90+
91+ if ( multiplexer == null )
92+ {
93+ throw new ArgumentNullException ( nameof ( multiplexer ) ) ;
94+ }
95+
96+ return services . AddRedisHybridCache ( multiplexer , _ => { } ) ;
97+ }
98+
99+ /// <summary>
100+ /// Adds the Redis hybrid cache implementation using an existing <see cref="IConnectionMultiplexer"/> instance.
101+ /// </summary>
102+ /// <param name="services">The service collection.</param>
103+ /// <param name="multiplexer">The pre-created <see cref="IConnectionMultiplexer"/> instance.</param>
104+ /// <param name="configureOptions">The action to configure cache options.</param>
105+ /// <returns>The service collection for fluent chaining.</returns>
106+ /// <exception cref="ArgumentNullException">Thrown when services, multiplexer, or configureOptions is null.</exception>
107+ /// <remarks>
108+ /// This overload is recommended when sharing the same Redis connection across multiple services
109+ /// (e.g., Data Protection, Session State, SignalR backplane) to avoid creating multiple connections.
110+ /// </remarks>
111+ public static IServiceCollection AddRedisHybridCache ( this IServiceCollection services , IConnectionMultiplexer multiplexer , Action < RedisHybridCacheOptions > configureOptions )
112+ {
113+ if ( services == null )
114+ {
115+ throw new ArgumentNullException ( nameof ( services ) ) ;
116+ }
117+
118+ if ( multiplexer == null )
119+ {
120+ throw new ArgumentNullException ( nameof ( multiplexer ) ) ;
121+ }
122+
123+ if ( configureOptions == null )
124+ {
125+ throw new ArgumentNullException ( nameof ( configureOptions ) ) ;
126+ }
127+
128+ // Register the provided multiplexer instance so other libraries (e.g. DataProtection)
129+ // can reuse the same connection, then configure the cache.
130+ services . AddSingleton < IConnectionMultiplexer > ( multiplexer ) ;
131+ return services . AddRedisHybridCacheCore ( configureOptions ) ;
132+ }
133+
134+ /// <summary>
135+ /// Adds the Redis hybrid cache implementation (L1 + L2 caching with message broker synchronization),
136+ /// using an already registered IConnectionMultiplexer.
137+ /// </summary>
138+ /// <param name="services">The service collection.</param>
139+ /// <param name="configureOptions">The action to configure cache options.</param>
140+ /// <returns>The service collection for fluent chaining.</returns>
141+ /// <exception cref="ArgumentNullException">Thrown when services or configureOptions is null.</exception>
142+ /// <remarks>
143+ /// Use this overload when you need to share the same IConnectionMultiplexer instance
144+ /// for other purposes (e.g., Data Protection keys). Register IConnectionMultiplexer before calling this method.
145+ /// </remarks>
146+ public static IServiceCollection AddRedisHybridCache ( this IServiceCollection services , Action < RedisHybridCacheOptions > configureOptions )
147+ {
148+ if ( services == null )
149+ {
150+ throw new ArgumentNullException ( nameof ( services ) ) ;
151+ }
152+
153+ if ( configureOptions == null )
154+ {
155+ throw new ArgumentNullException ( nameof ( configureOptions ) ) ;
156+ }
157+
158+ return services . AddRedisHybridCacheCore ( configureOptions ) ;
159+ }
160+
161+ /// <summary>
162+ /// Adds the Redis hybrid cache implementation core services and configuration.
163+ /// </summary>
164+ /// <param name="services">The service collection.</param>
165+ /// <param name="configureOptions">The action to configure cache options.</param>
166+ /// <returns>The service collection for fluent chaining.</returns>
167+ private static IServiceCollection AddRedisHybridCacheCore ( this IServiceCollection services , Action < RedisHybridCacheOptions > configureOptions )
168+ {
45169 services . AddOptions ( ) ;
46170 services . Configure ( configureOptions ) ;
47171
48- services . AddSingleton < ICacheClient > ( sp =>
172+ services . AddSingleton ( typeof ( IDistributedCache < > ) , typeof ( RedisHybridCache < > ) ) ;
173+
174+ services . AddSingleton < ISerializer > ( sp =>
49175 {
50- var options = sp . GetService < Microsoft . Extensions . Options . IOptions < RedisHybridCacheOptions > > ( ) ;
176+ var options = sp . GetService < IOptions < RedisHybridCacheOptions > > ( ) ;
51177 var enableCompression = options ? . Value ? . EnableCompression ?? false ;
52-
53- return new RedisHybridCacheClient ( new RedisHybridCacheClientOptions
54- {
55- ConnectionMultiplexer = sp . GetService < IConnectionMultiplexer > ( ) ,
56- LoggerFactory = sp . GetService < ILoggerFactory > ( ) ,
57- Serializer = new MsgPackSerializer ( enableCompression ) ,
58- } ) ;
178+ return new MsgPackSerializer ( enableCompression ) ;
59179 } ) ;
60- services . AddSingleton ( typeof ( IDistributedCache < > ) , typeof ( RedisHybridCache < > ) ) ;
180+
181+ services . AddSingleton < ICacheClient > ( sp => new RedisHybridCacheClient ( new RedisHybridCacheClientOptions
182+ {
183+ ConnectionMultiplexer = sp . GetRequiredService < IConnectionMultiplexer > ( ) ,
184+ LoggerFactory = sp . GetService < ILoggerFactory > ( ) ,
185+ Serializer = sp . GetRequiredService < ISerializer > ( ) ,
186+ } ) ) ;
61187
62188 return services ;
63189 }
0 commit comments