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

.NET 9与PostgreSQL 9.0.1升级后EF Core无法持久化HashSet<T>

问题描述

将项目从.NET 6升级至.NET 9,同时把PostgreSQL升级到9.0.1版本。领域模型中有如下属性:

public HashSet<Guid> Property { get; set; }

数据库中该属性存储为jsonb列,默认值设为'[]'::jsonb。升级后出现以下错误:

System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.HashSet`1[System.Guid]' to type 'System.Collections.Generic.IList`1[System.Guid]'.

推测问题与升级后EF Core对HashSet<T>和jsonb列的序列化/反序列化机制有关,此前在.NET 6及旧版PostgreSQL环境中功能正常。

尝试手动配置序列化但未解决问题:

builder
    .Property(x => x.Property)
    .HasColumnType("jsonb")
    .HasDefaultValueSql("'[]'::jsonb")
    .HasConversion(
        v => JsonSerializer.Serialize(v, new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }),
        v => JsonSerializer.Deserialize<HashSet<Guid>>(v, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new HashSet<Guid>()
);

咨询以下问题:

  1. EF Core或PostgreSQL是否存在影响HashSet<T>与jsonb列序列化/反序列化的破坏性变更?
  2. 如何在升级后的环境中正确持久化和查询HashSet<T>与jsonb列?
  3. 有无规避该InvalidCastException的解决方案或最佳实践?

解决方案与说明

1. 破坏性变更说明

  • EF Core 8+(含.NET 9对应的版本)对集合类型的JSON序列化逻辑做了调整:默认会将JSON数组反序列化为List<T>而非HashSet<T>,当模型属性定义为HashSet<T>时,内部尝试将List<T>强制转换为HashSet<T>就会抛出InvalidCastException。
  • Npgsql(PostgreSQL的EF Core驱动)在新版本中同步了这一行为,不再自动将JSON数组映射为HashSet<T>,优先使用List<T>作为默认集合类型。

2. 正确持久化与查询配置

有两种可靠的配置方式:

方式一:自定义值转换器并指定集合类型

修改值转换器,明确指定反序列化生成HashSet<T>,同时添加值比较器让EF Core识别属性类型:

builder.Property(x => x.Property)
    .HasColumnType("jsonb")
    .HasDefaultValueSql("'[]'::jsonb")
    .HasConversion(
        v => JsonSerializer.Serialize(v, JsonSerializerOptions.Default),
        v => JsonSerializer.Deserialize<HashSet<Guid>>(v, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new HashSet<Guid>(),
        new ValueConverterMappingHints { ValueComparer = ValueComparer.CreateDefault<HashSet<Guid>>() }
    );

方式二:Npgsql驱动专属配置

如果使用Npgsql驱动,可直接指定JSON列映射为HashSet<T>:

// 单个属性配置
builder.Property(x => x.Property)
    .HasColumnType("jsonb")
    .HasDefaultValueSql("'[]'::jsonb")
    .HasConversion<HashSet<Guid>>();

若需全局配置,可在DbContext的OnConfiguring中设置:

optionsBuilder.UseNpgsql(connectionString, o => 
    o.UseSystemTextJson().ConfigureJsonOptions(opt => {
        // 可添加其他JSON配置,比如枚举转换器
        opt.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
    })
);

3. 规避InvalidCastException的解决方案

临时快速修复

在模型属性的setter中主动转换集合类型,避免强制转换错误:

private HashSet<Guid> _property = new();
public HashSet<Guid> Property
{
    get => _property;
    set => _property = value != null ? new HashSet<Guid>(value) : new HashSet<Guid>();
}

最佳实践

  1. 优先使用自定义值转换器配置,明确指定集合类型的序列化/反序列化逻辑,不依赖默认行为。
  2. 升级前核对EF Core和Npgsql的版本变更日志,重点关注集合类型、JSON处理相关的破坏性变更。
  3. 确保模型属性初始化时设置为new HashSet<Guid>(),与数据库默认值'[]'::jsonb匹配,避免空引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 22:30:55