.NET Core WebAPI集成Redis遇两问题:连接配置空值与反序列化空引用
.NET Core WebAPI集成Redis的两类问题排查与解决
问题描述
学习.NET Core WebAPI并集成Redis时,出现两类问题:
- 运行时抛出
System.ArgumentNullException,提示"Value cannot be null. (Parameter 'configuration')",错误指向ConnectionHelper类中ConnectionMultiplexer.Connect(ConfigurationManager.AppSetting["RedisURL"])代码; CacheService类中JsonConvert.DeserializeObject<T>(value)返回null,同时存在"Possible null reference argument for parameter 'value' in 'T? JsonConvert.DeserializeObject(string value)'"的警告。
错误栈信息
System.ArgumentNullException: Value cannot be null. (Parameter 'configuration') at StackExchange.Redis.ConfigurationOptions.DoParse(String configuration, Boolean ignoreUnknown) in /_/src/StackExchange.Redis/ConfigurationOptions.cs:line 752 at StackExchange.Redis.ConfigurationOptions.Parse(String configuration, Boolean ignoreUnknown) in /_/src/StackExchange.Redis/ConfigurationOptions.cs:line 582 at StackExchange.Redis.ConfigurationOptions.Parse(String configuration) in /_/src/StackExchange.Redis/ConfigurationOptions.cs:line 572 at StackExchange.Redis.ConnectionMultiplexer.Connect(String configuration, TextWriter log) in /_/src/StackExchange.Redis/ConnectionMultiplexer.cs:line 655 at ApiProject.Cache.ConnectionHelper.<>c.<.cctor>b__0_0() in C:\Users\Mustafa\source\repos\ApiProject\Cache\ConnectionHelper.cs:line 10 at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) --- End of stack trace from previous location --- at System.Lazy`1.CreateValue() at ApiProject.Cache.ConnectionHelper.get_Connection() in C:\Users\Mustafa\source\repos\ApiProject\Cache\ConnectionHelper.cs:line 18 at ApiProject.Cache.CacheService.ConfigureRedis() in C:\Users\Mustafa\source\repos\ApiProject\Cache\CacheService.cs:line 14 at ApiProject.Cache.CacheService..ctor() in C:\Users\Mustafa\source\repos\ApiProject\Cache\CacheService.cs:line 10 at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired) at lambda_method16(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass7_0.<CreateActivator>b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
相关代码
ConnectionHelper类
public class ConnectionHelper { static ConnectionHelper() { ConnectionHelper.lazyConnection = new Lazy<ConnectionMultiplexer>(() => { return ConnectionMultiplexer.Connect(ConfigurationManager.AppSetting["RedisURL"]); }); } private static Lazy<ConnectionMultiplexer> lazyConnection; public static ConnectionMultiplexer Connection { get { return lazyConnection.Value; } } }
CacheService类
public class CacheService : ICacheService { private IDatabase _db; public CacheService() { ConfigureRedis(); } private void ConfigureRedis() { _db = ConnectionHelper.Connection.GetDatabase(); } public T GetData<T>(string? key) { var value = _db.StringGet(key); if (!string.IsNullOrEmpty(value)) { return JsonConvert.DeserializeObject<T>(value); } return default; } public bool SetData<T>(string key, T value, DateTimeOffset expirationTime) { TimeSpan expiryTime = expirationTime.DateTime.Subtract(DateTime.Now); var isSet = _db.StringSet(key, JsonConvert.SerializeObject(value), expiryTime); return isSet; } public object RemoveData(string key) { bool _isKeyExist = _db.KeyExists(key); if (_isKeyExist == true) { return _db.KeyDelete(key); } return false; } }
appsettings.json配置
{ "RedisURL": "127.0.0.1:6379", "JWT": { "ValidAudience": "http://localhost:7184", "ValidIssuer": "http://localhost:7184", "Secret": "JWTAuthentication@777" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Data Source=.;Initial Catalog=ApiTest_Database;Integrated Security=True;MultipleActiveResultSets=True;" } }
问题解决
问题1:ConfigurationManager获取RedisURL为空
原因:.NET Core中ConfigurationManager是传统.NET Framework的配置读取方式,无法直接读取appsettings.json,需通过依赖注入IConfiguration读取配置。
解决步骤:
- 在
Program.cs中注册Redis连接,通过依赖注入获取配置:
builder.Services.AddSingleton<IConnectionMultiplexer>(sp => { var configuration = sp.GetRequiredService<IConfiguration>(); var redisUrl = configuration.GetValue<string>("RedisURL"); return ConnectionMultiplexer.Connect(redisUrl); });
- 修改
CacheService,通过构造函数注入IConnectionMultiplexer,删除原有的ConfigureRedis方法和静态ConnectionHelper类:
public class CacheService : ICacheService { private IDatabase _db; public CacheService(IConnectionMultiplexer connectionMultiplexer) { _db = connectionMultiplexer.GetDatabase(); } // 其余方法保持不变 }
问题2:JsonConvert.DeserializeObject返回null及空引用警告
原因:
RedisValue的空值判断应使用自身的IsNull属性,而非字符串判断;- 反序列化时未处理格式错误,且参数可能为null触发警告。
解决步骤:
修改GetData<T>方法:
public T? GetData<T>(string? key) { if (string.IsNullOrEmpty(key)) return default; var value = _db.StringGet(key); if (value.IsNull) return default; try { return JsonConvert.DeserializeObject<T>(value.ToString()); } catch (JsonException ex) { // 可添加日志记录反序列化异常 return default; } }
说明:
- 先校验key的有效性,避免无效查询;
- 使用
RedisValue.IsNull准确判断缓存是否存在; - 增加异常捕获处理JSON格式错误;
- 返回类型改为
T?(.NET 6+可空引用类型),消除空引用警告。
内容的提问来源于stack exchange,提问作者kmr
相关产品推荐
相关产品推荐

