EF Core反向生成SQLite自增主键类时的ValueGeneratedNever问题
I get exactly what you're dealing with—EF Core's scaffolding tool misconfigures SQLite's AUTOINCREMENT primary keys as ValueGeneratedNever(), which causes duplicate ID errors when adding new records. Since you want to stick with your database-first workflow and can't switch to the old Entity Framework on UWP, here are several solid solutions:
1. Customize the EF Core Scaffolding Template
This is a one-time fix that ensures every scaffold run generates the correct ID configuration. Here's how to do it:
- First, list the available scaffolding templates to find the right one:
dotnet ef dbcontext scaffold --list-templates - Export the default templates to a local folder in your project:
dotnet ef dbcontext scaffold --export-templates - Open the
EntityType.cs.tmplfile (the template for generating entity configurations) and locate the section that handles primary key generation strategies. For SQLite columns marked asINTEGER PRIMARY KEY AUTOINCREMENT, modify the logic to output.ValueGeneratedOnAdd()instead of.ValueGeneratedNever(). - Next time you run the scaffold command, point it to your custom template folder:
(ReplaceScaffold-DbContext "DataSource=MyApp.sqlite;" Microsoft.EntityFrameworkCore.Sqlite -OutputDir Models -force -TemplatePath ./Templates./Templateswith the path to your custom template directory.)
2. Use Partial Classes + IEntityTypeConfiguration
Since the scaffolded model and context classes are partial, you can add your own configuration that overrides the scaffolded settings—this way, your fixes won't get wiped when you regenerate the code.
- Create a new configuration class in your
Modelsfolder:using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace YourAppNamespace.Models { public class LastSyncTimeConfiguration : IEntityTypeConfiguration<LastSyncTime> { public void Configure(EntityTypeBuilder<LastSyncTime> builder) { // Override the ID generation strategy builder.Property(e => e.Id) .HasColumnName("ID") .ValueGeneratedOnAdd(); } } } - Then, create a partial
MyAppContextclass (separate from the scaffolded one) to apply this configuration:
Now, even when you regenerate the context, your partial class won't be overwritten, and your correct ID configuration will take precedence.using Microsoft.EntityFrameworkCore; namespace YourAppNamespace.Models { public partial class MyAppContext : DbContext { protected override void OnModelCreating(ModelBuilder modelBuilder) { // Apply our custom configuration first modelBuilder.ApplyConfiguration(new LastSyncTimeConfiguration()); // Call the scaffolded OnModelCreating logic base.OnModelCreating(modelBuilder); } } }
3. Switch to Code-First Workflow (UWP-Friendly)
While you prefer database-first, UWP fully supports EF Core's code-first approach. This lets you define your model's ID strategy directly in code, eliminating scaffolding errors entirely.
Here's what your model would look like:
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace YourAppNamespace.Models { public class LastSyncTime { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } [Required] public string SyncTime { get; set; } [Required] public string TableUpdated { get; set; } } }
You'd then use Add-Migration and Update-Database to create and update your SQLite database—this ensures the auto-increment behavior is configured correctly from the start.
4. Manual Fix (Quick but Not Ideal)
If you need a temporary workaround, you can manually replace .ValueGeneratedNever() with .ValueGeneratedOnAdd() every time you scaffold. But this is repetitive and error-prone, so it's only good for quick fixes, not long-term use.
For your use case, either the custom scaffold template or partial class configuration is the best fit—they let you keep your database-first workflow without manual edits. Code-first is also a great option if you're open to adjusting your workflow for more control.
内容的提问来源于stack exchange,提问作者Dan Is Fiddling By Firelight

