You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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读取配置。

解决步骤:

  1. 在Program.cs中注册Redis连接,通过依赖注入获取配置:
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
{
    var configuration = sp.GetRequiredService<IConfiguration>();
    var redisUrl = configuration.GetValue<string>("RedisURL");
    return ConnectionMultiplexer.Connect(redisUrl);
});
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:39:18