Chr*_*ris 4 c# entity-framework entity-framework-core ef-core-6.0
我一直在寻找一种解决方案,在 EF Core 中编写“通用”更新方法,该方法更新实体(包括相关集合)的所有已更改属性。原因是我将实体名称的翻译存储在不同的表中。我发现这个解决方案一开始似乎工作得很好,但后来我注意到当我在实体正在相关表中添加新的名称翻译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();
}
}
Run Code Online (Sandbox Code Playgroud)
以下是逆向工程的 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; }
}
Run Code Online (Sandbox Code Playgroud)
以下是它们在 中的定义方式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");
});
Run Code Online (Sandbox Code Playgroud)
我不明白的是,这dbEntity.TblProjectTranslations.Add(newTranslation)似乎是问题所在。当我用 替换这一行时await Context.TblProjectTranslations.AddAsync(newTranslation),错误“神奇地”消失了,但是两种方法不是应该做基本上相同的事情吗?以下是我在问题发生前调试函数时捕获的anupdatedEntity和 a的示例:dbEntity
updatedEntity:
ProjectId: 41
TblProjectTranslations: 1 Entry with:
Language: "en"
Project: null
ProjectId: 41
ProjectName: "TestNameEN"
dbEntity:
ProjectId: 41
TblProjectTranslations: No entries
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?我什至在这两个表的数据库中都没有任何触发器,这对于其他一些人来说似乎是导致此错误的原因。
在尝试了一些示例模型、代码和解释之后,我终于能够重现它。罪魁祸首似乎是复合键及其字符串部分的默认值:
entity.HasKey(e => new { e.ProjectId, e.Language }); // (1)
entity.Property(e => e.Language)
.HasMaxLength(5)
.HasColumnName("language")
.HasDefaultValueSql("('-')"); // (2)
Run Code Online (Sandbox Code Playgroud)
如果你使用也会发生同样的情况
.HasDefaultValue("-")
Run Code Online (Sandbox Code Playgroud)
这种组合以某种方式导致 EF 考虑添加到父集合导航属性的项目
dbEntity.TblProjectTranslations.Add(newTranslation);
Run Code Online (Sandbox Code Playgroud)
asModified而不是Added,这后来导致了有问题的错误。
由于我在 EF Core 文档中找不到此类行为解释,并且通常看起来是错误的,因此我建议将其报告给EF Core GitHub 问题跟踪器。
同时,我看到的可能的解决方法是
删除键列的.HasDefaultValueSql/.HasDefaultValue
不要将子集合添加到父集合中,而是将其直接添加到上下文或相应的集合中DbSet(可以选择提前设置引用导航属性“以防万一”):
newTranslation.Project = dbEntity;
Context.Add(newTranslation);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12351 次 |
| 最近记录: |