You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JsonPatchDocument更新List<string>后数据库未同步的问题

问题:JsonPatch更新List属性后EF Core未同步变更到数据库

拥有如下Music模型,其中包含List<string>类型的Types属性:

public class Music
{
    public int Id { get; set; }
    public List<string> Types { get; set; }
}

由于数据库不支持集合类型列,通过EF Core值转换器将Types映射为逗号分隔的字符串:

modelBuilder.Entity<Music>().HasKey(x => x.Id);
modelBuilder.Entity<Music>()
    .Property(x => x.Types)
    .HasConversion(
        x => string.Join(',', x),
        x => x.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList());

编写了接收JsonPatchDocument的API端点用于更新该集合:

[HttpPatch("{id}")]
public async Task<IActionResult> EditAsync([FromRoute] long id, [FromBody] JsonPatchDocument<Music> patchDoc)
{
    var persisted = await Db.Music.FindAsync(id);
    if (persisted is null) return NotFound();

    patchDoc.ApplyTo(persisted, ModelState);
    if (ModelState.TryGetValue(nameof(Music), out var modelStateEntry))
    {
        foreach (var error in modelStateEntry.Errors)
            Logger.LogError(error.ErrorMessage); 
        return BadRequest(modelStateEntry.Errors);
    }

    await Db.SaveChangesAsync();
    return new ObjectResult(persisted);
}

日志显示:

  • 原始对象的JSON:
{
 "id": 1,
"types" : [
      "One",
      "Two",
      "Three"
    ]
}
  • 调用ApplyTo()后,对象的JSON已正确更新:
{
 "id": 1,
"types" : [
      "One",
      "Two",
      "Test"
    ]
}

但执行SaveChangesAsync()后,数据库无任何变更,日志输出:SaveChanges completed for 'OperationsDbContext' with 0 entities written to the database.


原因分析

EF Core的变更追踪机制默认仅检测引用类型的引用是否发生变化。List<string>是引用类型,当通过JsonPatch修改集合内部元素(替换、添加、删除)时,List的引用本身并未改变,EF Core会判定该属性未发生修改;再加上使用了值转换器,EF Core无法自动感知集合内部的内容变更,因此不会生成数据库更新语句。


解决方案

方法1:手动标记属性为已修改

在patchDoc.ApplyTo()之后、SaveChangesAsync()之前,手动告诉EF CoreTypes属性已变更:

patchDoc.ApplyTo(persisted, ModelState);
// 新增标记属性变更的代码
Db.Entry(persisted).Property(x => x.Types).IsModified = true;

方法2:替换List实例

创建一个新的List实例替换原引用,让EF Core检测到引用变化:

patchDoc.ApplyTo(persisted, ModelState);
// 新增生成新List实例的代码
persisted.Types = persisted.Types.ToList();

方法3:自定义值比较器

通过ValueComparer告诉EF Core如何比较集合内容,而非仅比较引用:

modelBuilder.Entity<Music>()
    .Property(x => x.Types)
    .HasConversion(
        x => string.Join(',', x),
        x => x.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList())
    // 新增值比较器配置
    .Metadata.SetValueComparer(new ValueComparer<List<string>>(
        (c1, c2) => c1.SequenceEqual(c2),
        c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
        c => c.ToList()));

内容的提问来源于stack exchange,提问作者Zvi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 21:14:53