如何在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
相关产品推荐
相关产品推荐

