From b9bf925c45e852f308c4d925d8c2ad39a22b9981 Mon Sep 17 00:00:00 2001 From: movingsam Date: Sun, 8 Mar 2026 00:32:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(efcore):=20=E4=BF=AE=E5=A4=8D=20EF=20Core?= =?UTF-8?q?=2010=20JSON=20=E6=98=A0=E5=B0=84=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复在 EF Core 10 中使用 JSON 值对象时出现的映射错误: ## 问题 在 EF Core 10 中,GwRouteMatch 类的嵌套集合属性(Headers 和 QueryParameters) 导致 "Unable to determine the relationship" 错误。 ## 解决方案 1. 在 PlatformDbContext 中使用 modelBuilder.Ignore<> 忽略相关类型 2. 将 OwnsOne().ToJson() 配置改为使用值转换器(Value Converter) 将对象序列化为 JSON 字符串存储到 jsonb 列 3. 在 GwRouteMatch 类的 Headers 和 QueryParameters 属性上添加 [NotMapped] 特性 4. 添加 [JsonInclude] 特性确保序列化包含这些属性 ## 技术细节 - 使用 HasColumnType("jsonb") 存储 JSON 数据 - 使用值转换器处理对象序列化/反序列化 - 保持与 PostgreSQL jsonb 类型的兼容性 ## 文件变更 - 修改: Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs - 修改: Fengling.Platform.Infrastructure/PlatformDbContext.cs 关联任务: IMPL-4 (EF Core 兼容性修复) 关联重构计划: WFS-gateway-refactor --- .../GatewayAggregate/GwRouteMatch.cs | 8 ++++ .../PlatformDbContext.cs | 40 ++++++++++++++----- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs index 69a5704..cb01eea 100644 --- a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs +++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs @@ -1,3 +1,7 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; + namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate; /// @@ -24,11 +28,15 @@ public class GwRouteMatch /// /// Header 匹配规则 /// + [NotMapped] + [JsonInclude] public List? Headers { get; set; } /// /// 查询参数匹配规则 /// + [NotMapped] + [JsonInclude] public List? QueryParameters { get; set; } } diff --git a/Fengling.Platform.Infrastructure/PlatformDbContext.cs b/Fengling.Platform.Infrastructure/PlatformDbContext.cs index b5b71bc..75a8095 100644 --- a/Fengling.Platform.Infrastructure/PlatformDbContext.cs +++ b/Fengling.Platform.Infrastructure/PlatformDbContext.cs @@ -4,6 +4,9 @@ using Fengling.Platform.Domain.AggregatesModel.TenantAggregate; using Fengling.Platform.Domain.AggregatesModel.UserAggregate; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System.Text.Json; namespace Fengling.Platform.Infrastructure; @@ -25,6 +28,12 @@ public class PlatformDbContext(DbContextOptions options) throw new ArgumentNullException(nameof(modelBuilder)); } + // 忽略这些类型,让它们只作为 JSON 值对象使用 + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Ignore(); + modelBuilder.Entity(entity => { entity.Property(e => e.PhoneNumber).HasMaxLength(20); @@ -100,17 +109,28 @@ public class PlatformDbContext(DbContextOptions options) ) .HasMaxLength(50); - // 值对象映射为 JSON 列 - entity.OwnsOne(e => e.Match, navigationBuilder => - { - navigationBuilder.ToJson(); - }); + // 值对象映射为 JSON 列 - 使用值转换器 + var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + entity.Property(e => e.Match) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonOptions), + v => JsonSerializer.Deserialize(v, jsonOptions)!, + new ValueComparer( + (c1, c2) => JsonSerializer.Serialize(c1, jsonOptions) == JsonSerializer.Serialize(c2, jsonOptions), + c => c == null ? 0 : JsonSerializer.Serialize(c, jsonOptions).GetHashCode(), + c => JsonSerializer.Deserialize(JsonSerializer.Serialize(c, jsonOptions), jsonOptions)!)) + .HasColumnType("jsonb"); - // 转换规则映射为 JSON 列 - entity.OwnsMany(e => e.Transforms, navigationBuilder => - { - navigationBuilder.ToJson(); - }); + // 转换规则映射为 JSON 列 - 使用值转换器 + entity.Property(e => e.Transforms) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonOptions), + v => JsonSerializer.Deserialize>(v, jsonOptions), + new ValueComparer>( + (c1, c2) => JsonSerializer.Serialize(c1, jsonOptions) == JsonSerializer.Serialize(c2, jsonOptions), + c => c == null ? 0 : JsonSerializer.Serialize(c, jsonOptions).GetHashCode(), + c => JsonSerializer.Deserialize>(JsonSerializer.Serialize(c, jsonOptions), jsonOptions)!)) + .HasColumnType("jsonb"); entity.HasIndex(e => e.TenantCode); entity.HasIndex(e => e.ServiceName);