如何在EntityFramework 7的列中使用非固定JSON结构?
问题解决方法
场景说明
你当前使用EF Core的OwnsOne+ToJson()配置,强制要求JSON必须包含所有定义的属性(哪怕值为null),否则抛出异常。需要实现:
- 缺失的JSON属性自动映射为
null - 同一JSON列可对应
Config中不同类型的属性
方案1:调整JSON序列化配置,允许缺失属性映射为null
EF Core默认的JSON序列化设置对缺失属性的处理较严格,可通过配置JsonObjectCreationHandling.Populate让缺失属性自动设为null,无需强制写出所有键:
public class ConfigEC : IEntityTypeConfiguration<Config> { public void Configure(EntityTypeBuilder<Config> builder) { builder.HasKey(e => e.Name); builder.OwnsOne(c => c.ValueJson, options => { options.ToJson(jsonOptions => { // 关键设置:允许从缺失属性生成null对象 jsonOptions.SerializerOptions.RequireObjectCreationHandling = JsonObjectCreationHandling.Populate; // 序列化时忽略null值,减少JSON冗余 jsonOptions.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; }); options.OwnsOne(c => c.ClanoviKartice); options.OwnsOne(c => c.ClanoviKartice2); }); } }
配置后,JSON中仅包含ClanoviKartice时,ValueJson.ClanoviKartice2会自动被设为null,不会触发异常。
方案2:自定义转换器,实现同一JSON列映射多类型属性
如果需要让同一JSON列支持多种不同结构的JSON,可放弃OwnsOne,改用HasConversion自定义序列化逻辑:
步骤1:定义多类型接口与实体
// 统一接口标记不同JSON类型 public interface IConfigJsonValue { } public class ClanoviKartice : IConfigJsonValue { public DateTime? DatumZadnjegPrintanja { get; set; } } public class ClanoviKartice2 : IConfigJsonValue { public DateTime? DatumZadnjegPrintanja { get; set; } }
步骤2:修改Config类
public class Config { public string Name { get; set; } public string Description { get; set; } public DateTime? DateCreated { get; set; } public int? ValueInt { get; set; } public decimal? ValueDecimal { get; set; } public string ValueText { get; set; } public DateTime? ValueDate { get; set; } // 映射到数据库的JSON列 public IConfigJsonValue ValueJson { get; set; } }
步骤3:配置EF转换逻辑
public class ConfigEC : IEntityTypeConfiguration<Config> { public void Configure(EntityTypeBuilder<Config> builder) { builder.HasKey(e => e.Name); // 配置JSON列的序列化/反序列化逻辑 builder.Property(e => e.ValueJson) .HasConversion( // 序列化:将对象转为JSON字符串 v => JsonSerializer.Serialize(v, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }), // 反序列化:根据实际类型解析JSON v => JsonSerializer.Deserialize<IConfigJsonValue>(v, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }), // 自定义值比较器,解决EF的变更追踪问题 new ValueComparer<IConfigJsonValue>( (c1, c2) => JsonSerializer.Serialize(c1) == JsonSerializer.Serialize(c2), c => c.GetHashCode(), c => JsonSerializer.Deserialize<IConfigJsonValue>(JsonSerializer.Serialize(c)) )) // 根据数据库类型选择列类型:SQL Server用nvarchar(max)/json,PostgreSQL用jsonb .HasColumnType("json"); } }
这种方式下,同一ValueJson列可存储ClanoviKartice或ClanoviKartice2结构的JSON,缺失属性自动设为null。
内容的提问来源于stack exchange,提问作者Hrvoje
相关产品推荐
相关产品推荐

