EF Core 7 JSON列新增字段后迁移及异常问题咨询
关于JSON列的迁移工具
EF Core(包括7.x版本)没有专门针对JSON列内部结构变更的迁移工具。EF的迁移系统仅负责管理数据库schema对象(如表、列、约束等),JSON列的内部结构属于数据层面的变更,不属于数据库schema范畴,因此迁移不会自动生成对应的处理逻辑。
解决KeyNotFoundException的方法
异常核心原因是:现有JSON数据中没有新增的AllowMultipleValues和Hidden字段,EF Core 7在处理OwnsMany+ToJson映射的JSON集合时,会尝试从JsonElement中读取这些字段,当键不存在时直接抛出异常。以下是三种可行的解决方式:
1. 批量更新现有JSON数据,补全缺失字段
直接通过SQL语句给现有JSON数组中的每个ReportParam对象添加缺失字段并设置默认值(bool类型默认设为false)。以SQL Server为例:
UPDATE ReportTemplates SET ReportParams = ( SELECT j.Name, j.Title, j.Description, j.DataType, j.Prompt, j.AllowBlank, j.Nullable, CAST(0 AS BIT) AS AllowMultipleValues, CAST(0 AS BIT) AS Hidden FROM OPENJSON(ReportTemplates.ReportParams) WITH ( Name NVARCHAR(MAX) '$.Name', Title NVARCHAR(MAX) '$.Title', Description NVARCHAR(MAX) '$.Description', DataType NVARCHAR(MAX) '$.DataType', Prompt NVARCHAR(MAX) '$.Prompt', AllowBlank BIT '$.AllowBlank', Nullable BIT '$.Nullable' ) j FOR JSON PATH );
执行完成后,现有数据的JSON结构会包含新增字段,EF查询时即可正常映射。
2. 改用自定义值转换器替代OwnsMany+ToJson
放弃EF自动的JSON集合映射,改用手动配置的ValueConverter,依赖System.Text.Json的默认反序列化行为——缺失的属性会自动赋值为类型默认值(bool为false):
修改DbContext中的模型配置:
// 移除原有的 OwnsMany 配置 // builder.OwnsMany(s => s.ReportParams, ob => ob.ToJson()); // 配置自定义JSON序列化 builder.Property(s => s.ReportParams) .HasColumnType("nvarchar(max)") // PostgreSQL 替换为 "jsonb" .HasConversion( // 序列化 v => JsonSerializer.Serialize(v, new JsonSerializerOptions { WriteIndented = false }), // 反序列化 v => JsonSerializer.Deserialize<List<ReportParam>>(v) ?? new List<ReportParam>(), // 自定义值比较器(用于EF跟踪变更) new ValueComparer<List<ReportParam>>( (list1, list2) => list1.SequenceEqual(list2), list => list.Aggregate(0, (hash, item) => HashCode.Combine(hash, item.GetHashCode())), list => list.ToList() ) );
这种方式无需修改现有数据,但缺点是无法直接通过EF LINQ查询JSON内部的属性(如过滤AllowMultipleValues为true的模板),需要手动编写SQL或使用EF.Functions.JsonContains等方法。
3. 升级到EF Core 8
EF Core 8对JSON列的映射逻辑做了优化,读取JSON中的Owned实体时,若属性缺失会自动使用该属性的默认值,而非抛出异常。如果项目允许升级框架版本,这是最简洁的解决方案。
内容的提问来源于stack exchange,提问作者Mohammad Zatkhahi

