【问题标题】:Error "Database operation expected to affect 1 row(s) but actually affected 0 row(s)" when adding a related entity添加相关实体时出现错误“数据库操作预计会影响 1 行但实际影响 0 行”
【发布时间】:2022-02-08 02:29:50
【问题描述】:

我一直在寻找在 EF Core 中编写“通用”更新方法的解决方案,该方法更新实体的所有更改属性,包括相关集合。这样做的原因是我将实体名称的翻译存储在不同的表中。 我发现this 解决方案一开始似乎工作得很好,但后来我注意到我得到一个错误“数据库操作预计会影响 1 行但实际上影响 0 行”当我唯一改变的时候在实体中是在相关表TblProjectTranslations 中添加新名称翻译。这是我的代码:

public async Task UpdateProjectAsync(TblProject updatedEntity)
{
    TblProject dbEntity = Context.TblProjects
        .Include(dbEntity => dbEntity.TblProjectTranslations)
        .SingleOrDefault(dbEntity => dbEntity.ProjectId == updatedEntity.ProjectId);

    if (dbEntity != null)
    {
        Context.Entry(dbEntity).CurrentValues.SetValues(updatedEntity);

        foreach (TblProjectTranslation dbTranslation in dbEntity.TblProjectTranslations.ToList())
        {
            if (!updatedEntity.TblProjectTranslations
                .Any(translation => translation.ProjectId == dbTranslation.ProjectId && translation.Language == dbTranslation.Language))
            {
                Context.TblProjectTranslations.Remove(dbTranslation);
            }
        }                  

        foreach (TblProjectTranslation newTranslation in updatedEntity.TblProjectTranslations)
        {
            TblProjectTranslation dbTranslation = dbEntity.TblProjectTranslations
                .SingleOrDefault(dbTranslation => dbTranslation.ProjectId == newTranslation.ProjectId && dbTranslation.Language == newTranslation.Language);

            if (dbTranslation != null)
            {
                Context.Entry(dbTranslation).CurrentValues.SetValues(newTranslation);
            }
            else
            {
                dbEntity.TblProjectTranslations.Add(newTranslation);
            }
        }

        await Context.SaveChangesAsync();
    }
}

以下是反向工程的 EF Core 类:

public partial class TblProject
{
    public TblProject()
    {
        TblProjectTranslations = new HashSet<TblProjectTranslation>();
    }

    public int ProjectId { get; set; }

    public virtual ICollection<TblProjectTranslation> TblProjectTranslations { get; set; }
}

public partial class TblProjectTranslation
{
    public int ProjectId { get; set; }
    public string Language { get; set; }
    public string ProjectName { get; set; }

    public virtual TblProject Project { get; set; }
}

这是它们在OnModelCreating 中的定义方式:

modelBuilder.Entity<TblProject>(entity =>
{
    entity.HasKey(e => e.ProjectId);

    entity.ToTable("TBL_project");

    entity.Property(e => e.ProjectId).HasColumnName("project_ID");
});

modelBuilder.Entity<TblProjectTranslation>(entity =>
{
    entity.HasKey(e => new { e.ProjectId, e.Language });

    entity.ToTable("TBL_project_translation");

    entity.HasIndex(e => e.ProjectId, "IX_TBL_project_translation_project_ID");

    entity.Property(e => e.ProjectId).HasColumnName("project_ID");

    entity.Property(e => e.Language)
        .HasMaxLength(5)
        .HasColumnName("language")
        .HasDefaultValueSql("('-')");

    entity.Property(e => e.ProjectName)
        .IsRequired()
        .HasMaxLength(50)
        .HasColumnName("project_name")
        .HasDefaultValueSql("('-')");

    entity.HasOne(d => d.Project)
        .WithMany(p => p.TblProjectTranslations)
        .HasForeignKey(d => d.ProjectId)
        .OnDelete(DeleteBehavior.ClientSetNull)
        .HasConstraintName("TBL_project_translation_TBL_project");
});

我不明白的是dbEntity.TblProjectTranslations.Add(newTranslation) 似乎是问题所在。当我用await Context.TblProjectTranslations.AddAsync(newTranslation) 替换这一行时,错误“神奇地”消失了,但两种方式不应该做基本相同的事情吗?这是一个updatedEntitydbEntity 的示例,我在问题发生前调试函数时捕获了它们:

updatedEntity:
ProjectId: 41
TblProjectTranslations: 1 Entry with:
    Language: "en"
    Project: null
    ProjectId: 41
    ProjectName: "TestNameEN"

dbEntity:
ProjectId: 41
TblProjectTranslations: No entries

这里发生了什么?我什至在这两个表中的数据库中都没有任何触发器,这对于其他人来说似乎是导致此错误的原因。

【问题讨论】:

  • 一种异步方法,您需要阻塞直到使用 await 接收到所有数据。
  • @jdweng 我知道这一点,但你在我的代码中哪里看到我没有在应该使用的地方使用 await?
  • 使用 EF 日志查看生成的查询。
  • @IvanStoev 其他属性可能会导致这种情况吗?为了简单起见,我省略了实体的其他属性,但这些实际上只是整数、字符串和两个可为空的布尔值。问题发生时,它们都没有在更新中更改。
  • @IvanStoev 我将OnModelCreating的相应代码添加到我的问题中。

标签: c# entity-framework entity-framework-core ef-core-6.0


【解决方案1】:

在对示例模型、代码和解释进行了一些尝试之后,我终于能够重现它了。罪魁祸首似乎是复合键和它的字符串部分的默认值:

entity.HasKey(e => new { e.ProjectId, e.Language }); // (1)

entity.Property(e => e.Language)
    .HasMaxLength(5)
    .HasColumnName("language")
    .HasDefaultValueSql("('-')"); // (2)

如果你使用也会发生同样的情况

.HasDefaultValue("-")

这种组合不知何故导致 EF 考虑添加到父集合导航属性的项目

dbEntity.TblProjectTranslations.Add(newTranslation);

作为Modified而不是Added,这会导致问题中的错误。

由于我在 EF Core 文档中找不到这样的行为解释,而且总体上看起来是错误的,我建议你去报告给EF Core GitHub issue tracker

同时,我看到的可能解决方法是

  • 去掉.HasDefaultValueSql/.HasDefaultValue作为键列

  • 与其将子集合添加到父集合中,不如直接将其添加到上下文或对应的DbSet(可选地提前设置引用导航属性“以防万一”):

newTranslation.Project = dbEntity;
Context.Add(newTranslation);

【讨论】:

    猜你喜欢
    • 2018-09-21
    • 1970-01-01
    • 1970-01-01
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多