EF Core 7下PostgreSQL JSON数据懒加载反序列化方案咨询
EF Core 7实现PostgreSQL JSON数据的懒加载序列化/反序列化
需求是在PostgreSQL中通过EF Core读写JSON数据,实现按需反序列化——仅当访问Person实体的RawData属性时才将JSON字符串转换为PersonDto对象,避免每次加载实体时的不必要计算和内存分配;同时要符合Clean Architecture规范,业务层不能依赖具体的JSON序列化API。
现有代码结构如下(业务、持久化、服务分层):
// project: Business public class Person // EF Core持久化实体 { public int Id { get; private set; } public string Name { get; set; } public PersonDto? RawData { get; set; } } // vendor assembly (NuGet包) public class PersonDto // 外部系统传入的JSON数据类型 { public string Name { get; set; } public int Age { get; set; } // ...其他大量属性 } // project: Persistence public class PersonContext : DbContext { public DbSet<Person> Persons { get; private set; } } // project: Service public class Handler { private readonly PersonContext _context; public Handler(PersonContext context) => _context = context; public async Task HandleInputAsync(PersonDto input) { var person = new Person { Name = input.Name, RawData = input }; await _context.Persons.AddAsync(person); await _context.SaveChangesAsync(); } }
之前的自定义转换器方案会在实体加载时自动反序列化RawData,无法实现懒加载;而直接存储JSON字符串的方案会导致JSON字符串始终加载到内存,且业务层依赖序列化API,不符合架构规范。
实现步骤
1. 改造业务层Person实体
在Person类中添加私有字段存储原始JSON字符串,RawData属性改为延迟加载模式,通过注入的反序列化委托实现按需转换,同时隔离业务层与序列化库:
// project: Business public class Person { public int Id { get; private set; } public string Name { get; set; } // 存储数据库中的原始JSON字符串 private string? _rawDataJson; // 缓存反序列化后的PersonDto实例 private PersonDto? _rawData; // 注入的反序列化委托(由持久化层提供实现) private readonly Func<string, PersonDto>? _deserializer; // EF Core需要的无参构造函数 protected Person() { } // 业务层/DI使用的构造函数,传入反序列化逻辑 public Person(Func<string, PersonDto>? deserializer = null) { _deserializer = deserializer; } public PersonDto? RawData { get { // 仅当未反序列化且有原始JSON时,触发转换 if (_rawData == null && _rawDataJson != null && _deserializer != null) { _rawData = _deserializer(_rawDataJson); } return _rawData; } set { _rawData = value; // 设置新值时清空JSON缓存,保存时重新序列化 _rawDataJson = null; } } // 供持久化层设置原始JSON的内部方法 internal void SetRawDataJson(string? json) { _rawDataJson = json; // 重置缓存,下次访问时重新反序列化 _rawData = null; } // 供持久化层获取原始JSON的内部方法 internal string? GetRawDataJson() { return _rawDataJson; } }
2. 持久化层配置EF Core上下文
实现序列化接口,配置实体映射,监听保存事件完成JSON序列化,确保仅在保存时将PersonDto转换为JSON字符串:
// project: Persistence // 定义序列化接口,隔离具体序列化库 public interface IJsonSerializer { string Serialize<T>(T obj); T? Deserialize<T>(string json); } // 使用System.Text.Json实现序列化(可替换为Newtonsoft.Json) public class SystemTextJsonSerializer : IJsonSerializer { private readonly JsonSerializerOptions _options = new() { PropertyNameCaseInsensitive = true }; public string Serialize<T>(T obj) => JsonSerializer.Serialize(obj, _options); public T? Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, _options); } // 配置EF Core上下文 public class PersonContext : DbContext { public DbSet<Person> Persons { get; private set; } private readonly IJsonSerializer _jsonSerializer; public PersonContext(DbContextOptions<PersonContext> options, IJsonSerializer jsonSerializer) : base(options) { _jsonSerializer = jsonSerializer; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Person>(entity => { entity.Property<int>("Id").ValueGeneratedOnAdd(); entity.Property(p => p.Name).IsRequired(); // 将私有字段_rawDataJson映射到数据库的RawData列,类型为json entity.Property<string>("_rawDataJson") .HasColumnName("RawData") .HasColumnType("json") .Metadata.SetField("_rawDataJson"); // 忽略RawData属性,通过私有字段处理数据库交互 entity.Ignore(p => p.RawData); }); // 监听保存事件,在保存前将PersonDto序列化为JSON字符串 SavingChanges += (_, _) => { foreach (var entry in ChangeTracker.Entries<Person>()) { if (entry.State is EntityState.Added or EntityState.Modified) { var person = entry.Entity; if (person.RawData != null) { var json = _jsonSerializer.Serialize(person.RawData); person.SetRawDataJson(json); } } } }; } }
3. 服务层注入依赖
在服务层创建Person实例时,传入反序列化委托,确保业务层不直接依赖序列化库:
// project: Service public class Handler { private readonly PersonContext _context; private readonly IJsonSerializer _jsonSerializer; public Handler(PersonContext context, IJsonSerializer jsonSerializer) { _context = context; _jsonSerializer = jsonSerializer; } public async Task HandleInputAsync(PersonDto input) { // 传入反序列化委托,将JSON字符串转换为PersonDto var person = new Person(json => _jsonSerializer.Deserialize<PersonDto>(json)) { Name = input.Name, RawData = input }; await _context.Persons.AddAsync(person); await _context.SaveChangesAsync(); } }
方案优势
- 懒加载反序列化:只有当访问
RawData属性时才会执行反序列化,避免不必要的内存分配和计算。 - 符合Clean Architecture:业务层仅依赖抽象的反序列化委托,不直接引用任何序列化库,实现了关注点分离。
- 完全适配PostgreSQL:通过EF Core映射将私有字段直接绑定到PostgreSQL的
json列,读写性能最优。
内容的提问来源于stack exchange,提问作者mu88
相关产品推荐
相关产品推荐

