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

.NET 8中Hangfire.PostgreSql调用GetStateData时反序列化异常排查

异常成因分析与解决建议

核心问题本质

这个异常的根源是Newtonsoft.Json无法反序列化Hangfire.PostgreSql内部的SafeDictionary<TKey,TValue>类型——该类型是PostgreSqlMonitoringApi的私有内部类,没有公开的无参构造函数,而Hangfire的序列化流程在某些场景下错误地尝试将数据库中的状态JSON反序列化为这个内部类型,而非公共的IDictionary<string,string>或Dictionary<string,string>。

具体成因

  1. 版本不兼容
    Hangfire.Core与Hangfire.PostgreSql的版本未对齐,导致两者在状态数据的序列化/反序列化逻辑上出现分歧:比如新版本的PostgreSql存储库改用SafeDictionary包装状态数据,但旧版本的Core无法识别这个内部类型的构造规则;反之亦然。

  2. 并发竞态条件
    当Hangfire后台进程正在更新任务状态(比如任务刚完成、失败或被重试)时,数据库中的状态JSON可能处于半写入的中间状态——此时你的GetStateData调用刚好读取到这个未完全写入的、带有SafeDictionary类型标记的JSON,触发反序列化失败。这也是异常间歇性出现的核心原因。

  3. 序列化配置被篡改
    如果你的项目自定义了Hangfire使用的Newtonsoft.Json序列化配置(比如修改了ConstructorHandling选项),可能导致序列化器无法识别SafeDictionary的非公开构造函数,进而抛出无默认构造的异常。

解决步骤

  1. 对齐版本
    检查并升级Hangfire.Core和Hangfire.PostgreSql到最新稳定兼容版本(优先使用NuGet上的匹配版本,比如两者都用1.8.x系列),确保序列化逻辑一致。

  2. 添加重试机制
    针对间歇性的竞态问题,在调用GetStateData时添加重试逻辑:

private StateData GetJobState(string jobId, int retryCount = 3)
{
    var retryAttempts = 0;
    do
    {
        try
        {
            using (var connection = JobStorage.Current.GetConnection())
            {
                return connection.GetStateData(jobId);
            }
        }
        catch (JsonSerializationException ex) when (ex.Message.Contains("SafeDictionary"))
        {
            retryAttempts++;
            if (retryAttempts >= retryCount) throw;
            Thread.Sleep(100);
        }
    } while (retryAttempts < retryCount);
    return null;
}
  1. 改用公共API获取状态
    避免直接调用底层的GetStateData,改用Hangfire的公共IMonitoringApi来获取任务状态,它的实现更稳定,不会依赖内部类型:
private string GetJobCurrentState(string jobId)
{
    var monitoringApi = JobStorage.Current.GetMonitoringApi();
    var jobDetails = monitoringApi.JobDetails(jobId);
    return jobDetails.History.FirstOrDefault()?.State;
}
  1. 修复序列化配置
    如果自定义了Json序列化器,确保添加允许非公开构造函数的配置:
GlobalConfiguration.Configuration.UseSerializerSettings(new JsonSerializerSettings
{
    ConstructorHandling = ConstructorHandling.AllowNonPublicConstructors
});

内容的提问来源于stack exchange,提问作者ctor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:19:55