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

EF Core更新阴影属性集合时出错,寻求解决方案

问题:EF Core更新包含阴影属性集合的实体时出错

错误场景

我尝试用以下代码更新BaseQuestion模型:

appContext.BaseQuestions.Update(question);
await appContext.SaveChangesAsync();

遇到如下错误:

The value of shadow key property 'Question.Formulas#FormulaInfo.Id' is unknown when attempting to save changes. This is because shadow property values cannot be preserved when the entity is not being tracked. Consider adding the property to the entity's .NET type.

相关代码

实体类定义

BaseQuestion类

public class BaseQuestion
{
    public Guid Id { get; set; }
    public string Header { get; set; }
    public string Description { get; set; }
    public bool IsSolved { get; set; }           
    public Question? Question { get; set; }      
}

Question类

public class Question
{
    public Guid Id { get; set; }          
    public string QuestionBody { get; set; }
    public Guid? BaseQuestionId { get; set; }
    public BaseQuestion? BaseQuestion { get; set; }     
    public ICollection<FormulaInfo>? Formulas { get; set; }
}   

EF Core配置

BaseQuestion配置

public void Configure(EntityTypeBuilder<BaseQuestion> builder)
{
    builder
       .HasKey(bq => bq.Id);

    builder
        .Property(bq => bq.Id)
        .ValueGeneratedNever();   

    builder
        .Navigation(bq => bq.Question)
        .AutoInclude();

    builder
        .HasOne(bq => bq.Question)
        .WithOne(q => q.BaseQuestion)
        .HasForeignKey<Question>(q => q.BaseQuestionId)
        .OnDelete(DeleteBehavior.Cascade);            
} 

Question配置

public void Configure(EntityTypeBuilder<Question> builder)
{
    builder
        .HasKey(q => q.Id);           

    builder
        .Property(q => q.Id)
        .ValueGeneratedNever();

    builder
       .Navigation(q => q.BaseQuestion)
       .AutoInclude();

    builder.OwnsMany(q => q.Formulas, f =>
    {
        f.WithOwner().HasForeignKey("OwnerId");
        f.Property<Guid>("Id");
        f.HasKey("Id");
    });   
}

解决方案

问题核心是:你给FormulaInfo定义的Id是阴影属性,当实体处于未被跟踪状态(比如从外部传入的对象转换而来)时,EF Core无法保留阴影属性的值,导致更新时无法识别集合元素的身份。

提供两种可行解决办法:

方案1:给FormulaInfo显式添加Id属性

直接在FormulaInfo类中添加Id属性,替代阴影属性定义:

public class FormulaInfo
{
    public Guid Id { get; set; }
    // 其他原有属性...
}

然后修改Question的配置,去掉阴影属性相关代码:

builder.OwnsMany(q => q.Formulas, f =>
{
    f.WithOwner().HasForeignKey("OwnerId");
    f.HasKey(fi => fi.Id); // 使用实体自身的Id作为主键
});

这种方式最直接,EF Core能正常跟踪集合中每个元素的Id,更新时不会出现识别问题。

方案2:确保更新前实体被EF Core跟踪

如果不想修改FormulaInfo类,可以先从数据库加载目标实体(此时实体处于跟踪状态),再将传入的属性值映射到跟踪实体上,最后保存:

// 从数据库加载带关联数据的跟踪实体
var trackedBaseQuestion = await appContext.BaseQuestions
    .Include(bq => bq.Question)
    .ThenInclude(q => q.Formulas)
    .FirstOrDefaultAsync(bq => bq.Id == question.Id);

if (trackedBaseQuestion != null)
{
    // 映射BaseQuestion的基础属性
    trackedBaseQuestion.Header = question.Header;
    trackedBaseQuestion.Description = question.Description;
    trackedBaseQuestion.IsSolved = question.IsSolved;

    // 映射Question的属性
    if (trackedBaseQuestion.Question != null && question.Question != null)
    {
        trackedBaseQuestion.Question.QuestionBody = question.Question.QuestionBody;

        // 处理Formulas集合:先清空现有数据,再添加新元素
        trackedBaseQuestion.Question.Formulas.Clear();
        if (question.Question.Formulas != null)
        {
            foreach (var formula in question.Question.Formulas)
            {
                trackedBaseQuestion.Question.Formulas.Add(formula);
            }
        }
    }

    await appContext.SaveChangesAsync();
}

这种方式利用EF Core的跟踪机制维护阴影属性的Id,不过需要额外的数据库查询和属性映射操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:25:32