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

如何在EF Core PostgreSQL的JSON列中启用List<T>多态并支持LINQ查询?

实现多态JSON列表的序列化、反序列化及LINQ查询

1. 配置JSON序列化以支持多态

要让EF Core正确识别并处理ContactInformation的子类,必须配置JSON序列化器添加类型鉴别符,确保反序列化时能还原具体的子类实例。

使用System.Text.Json

在项目中配置序列化选项,注册多态类型映射:

using System.Text.Json;
using System.Text.Json.Serialization;

private static void ConfigurePolymorphicSerialization(JsonTypeInfo typeInfo)
{
    if (typeInfo.Type == typeof(ContactInformation))
    {
        typeInfo.PolymorphismOptions = new JsonPolymorphismOptions
        {
            // 指定类型鉴别符的JSON键名
            TypeDiscriminatorPropertyName = "$type",
            IgnoreUnrecognizedTypeDiscriminators = true,
            // 注册所有派生类型
            DerivedTypes =
            {
                new JsonDerivedType(typeof(ContactTelephone), nameof(ContactTelephone)),
                new JsonDerivedType(typeof(ContactEmail), nameof(ContactEmail))
            }
        };
    }
}

然后在EF Core的DbContext配置中全局应用该选项:

services.AddDbContext<YourDbContext>(options =>
    options.UseNpgsql("你的数据库连接字符串", npgsqlOpts =>
        npgsqlOpts.UseJsonOptions(jsonOpts =>
            jsonOpts.SerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
            {
                Modifiers = { ConfigurePolymorphicSerialization }
            }
        )
    )
);

(可选)使用Newtonsoft.Json

若偏好Newtonsoft.Json,可通过TypeNameHandling保留类型信息:

services.AddDbContext<YourDbContext>(options =>
    options.UseNpgsql("你的数据库连接字符串", npgsqlOpts =>
        npgsqlOpts.UseNewtonsoftJson(jsonOpts =>
            jsonOpts.TypeNameHandling = TypeNameHandling.Auto
        )
    )
);

2. 调整实体模型配置

建议将JSON列类型从json改为jsonb——PostgreSQL对jsonb支持更高效的查询和索引:

public class YourEntity
{
    [Column(TypeName = "jsonb")]
    public List<ContactInformation> ContactInformation { get; set; } = new();
}

3. 编写支持多态的LINQ查询

EF Core无法直接将C#类型检查(如x is ContactEmail)翻译成PostgreSQL语句,需借助Npgsql提供的JSON函数实现。

使用JSON Path查询(推荐)

通过EF.Functions.JsonPathExists直接在数据库层面执行JSON路径筛选:

using Npgsql.EntityFrameworkCore.PostgreSQL.Json;

var targetEmail = "me@home.com";
var result = dbContext.YourEntities
    .Where(e => EF.Functions.JsonPathExists(
        e.ContactInformation.AsJson(),
        $"$[*] ? (@.\"$type\" == 'ContactEmail' && @.EmailAddress == '{targetEmail}')"
    ))
    .ToList();

(可选)使用JsonContains进行精确匹配

若需精确匹配某个完整的ContactEmail对象,可使用EF.Functions.JsonContains:

var targetContact = new ContactEmail
{
    Name = "",
    EmailAddress = "me@home.com"
};
var targetJson = JsonSerializer.Serialize(targetContact, new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ConfigurePolymorphicSerialization } }
});

var result = dbContext.YourEntities
    .Where(e => EF.Functions.JsonContains(e.ContactInformation.AsJson(), $"[{targetJson}]"))
    .ToList();

4. 验证序列化与反序列化

保存实体时,子类对象会被序列化为包含$type字段的JSON数组;查询时,EF Core会根据$type自动反序列化为对应的子类实例,你可以在内存中正常使用类型转换:

var entity = dbContext.YourEntities.First();
var emailContacts = entity.ContactInformation.OfType<ContactEmail>().ToList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 00:15:35