EF更新含Guid主键与Identity列的记录时触发无法更新标识列错误
问题场景
我有一个应用程序,使用以下EF Core脚手架命令生成数据库实体:
dotnet ef dbcontext scaffold "Server=MyServer;Database=MyDatabase;Trusted_Connection=True;Trust Server Certificate=true" Microsoft.EntityFrameworkCore.SqlServer -o Entities -f --no-onconfiguring
生成完成后,目标表的RecNo列被配置为:
entity.Property(e => e.RecNo).ValueGeneratedOnAdd();
但尝试更新该表记录时,出现以下错误:
Microsoft.EntityFrameworkCore.DbUpdateException: 保存实体更改时发生错误。请查看内部异常了解详细信息。
Microsoft.Data.SqlClient.SqlException (0x80131904): 无法更新标识列 'RecNo'。
原本以为ValueGeneratedOnAdd()会在新增和更新时都生效,实际并非如此,希望找到解决办法,优先在脚手架阶段处理。
解决方案
1. 脚手架阶段直接修正(推荐)
方法一:使用--data-annotations参数生成数据注解
EF Core脚手架默认生成Fluent API配置,改用--data-annotations参数后,会自动识别SQL Server的标识列,并生成对应的[DatabaseGenerated(DatabaseGeneratedOption.Identity)]注解,明确告知EF该列仅在新增时由数据库生成,更新时无需修改。
修改后的脚手架命令:
dotnet ef dbcontext scaffold "Server=MyServer;Database=MyDatabase;Trusted_Connection=True;Trust Server Certificate=true" Microsoft.EntityFrameworkCore.SqlServer -o Entities -f --no-onconfiguring --data-annotations
生成的实体类中RecNo列会带有如下配置:
[Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int RecNo { get; set; }
这种配置下,EF在生成Update语句时会自动忽略该列,避免报错。
方法二:自定义脚手架模板(适配多次生成场景)
如果需要长期复用特定配置,可以自定义EF Core脚手架模板:
- 先安装模板工具:
dotnet new install Microsoft.EntityFrameworkCore.Templates - 导出模板到本地目录:
dotnet new ef-templates -o EFTemplates - 修改
EntityTypeConfiguration.cs.t4模板中标识列的配置逻辑,确保生成的Fluent API明确标记该列的生成规则:
<# if (property.IsIdentity) { #> entity.Property(e => e.<#= property.Name #>) .ValueGeneratedOnAdd() .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); <# } #>
后续脚手架生成时使用自定义模板即可自动应用正确配置。
2. 手动修改生成代码(临时应急)
如果脚手架阶段无法调整,生成代码后可手动修改配置:
方式一:修改Fluent API配置
在DbContext的实体配置中添加SetAfterSaveBehavior:
entity.Property(e => e.RecNo) .ValueGeneratedOnAdd() .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
该配置强制EF在更新实体时忽略RecNo列,无论属性值是否被修改,都不会将其包含在Update语句中。
方式二:添加数据注解
在实体类的RecNo属性上添加数据库生成注解:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
若RecNo是表的主键,还需添加[Key]注解(脚手架通常会自动生成)。
内容的提问来源于stack exchange,提问作者Jim Wilcox

