-
Notifications
You must be signed in to change notification settings - Fork 2k
Update SQLite AUTOINCREMENT documentation for EF Core 10 #5115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: live
Are you sure you want to change the base?
Changes from 4 commits
0811647
00a9555
055ab79
7c1aa86
eba15dc
c00dee1
1440b77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| --- | ||
| title: SQLite Database Provider - Value Generation - EF Core | ||
| description: Value Generation Patterns Specific to the SQLite Entity Framework Core Database Provider | ||
| author: AndriySvyryd | ||
| ms.date: 09/26/2025 | ||
| uid: core/providers/sqlite/value-generation | ||
| --- | ||
| # SQLite Value Generation | ||
|
|
||
| This page details value generation configuration and patterns that are specific to the SQLite provider. It's recommended to first read [the general page on value generation](xref:core/modeling/generated-properties). | ||
|
|
||
| ## AUTOINCREMENT columns | ||
|
|
||
| By convention, numeric primary key columns that are configured to have their values generated on add are set up with SQLite's AUTOINCREMENT feature. Starting with EF Core 10, SQLite AUTOINCREMENT is a first-class feature with full support through conventions and the Fluent API. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Link to the SQLite doc page explaining what AUTOINCREMENT is. I'm not sure what the 2nd sentence exactly means for the user... I'd maybe instead have a small Limitations sections at the bottom of the page listing the concrete problems that existed before EF 10, but as it is this sentence doesn't actually seem to tell the user anything concrete. |
||
|
|
||
| ### Configuring AUTOINCREMENT | ||
|
|
||
| By convention, integer primary keys are automatically configured with AUTOINCREMENT when they don't have an explicitly assigned value. However, you may need to explicitly configure a property to use SQLite AUTOINCREMENT when the property has a value conversion from a non-integer type, or when overriding conventions: | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [!code-csharp[Main](../../../../samples/core/Sqlite/ValueGeneration/SqliteAutoincrementWithValueConverter.cs?name=SqliteAutoincrementWithValueConverter&highlight=6)] | ||
|
|
||
| Starting with EF Core 10, you can also use the new Fluent API: | ||
|
|
||
| ```csharp | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<BlogPost>() | ||
| .Property(b => b.Id) | ||
| .HasConversion<int>() | ||
| .UseAutoincrement(); | ||
| } | ||
| ``` | ||
|
|
||
| This is equivalent to using the more general value generation API: | ||
|
|
||
| ```csharp | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<Blog>() | ||
| .Property(b => b.Id) | ||
| .ValueGeneratedOnAdd(); | ||
| } | ||
| ``` | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Disabling AUTOINCREMENT for default SQLite value generation | ||
|
|
||
| In some cases, you may want to disable AUTOINCREMENT and use SQLite's default value generation behavior instead. You can do this using the Metadata API: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just add a few words on when/why someone would want this (or at least link to the docs)? Otherwise that's just documenting our APIs without their meaning/why they exist. |
||
|
|
||
| [!code-csharp[Main](../../../../samples/core/Sqlite/ValueGeneration/SqliteValueGenerationStrategyNone.cs?name=SqliteValueGenerationStrategyNone&highlight=5)] | ||
|
|
||
| Starting with EF Core 10, you can also use the strongly-typed Metadata API: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the preview, the inline code snippet just below seems identical to the one linked to just above (the external linking makes this quite hard to notice when reviewing, FWIW). Accidental duplication? |
||
|
|
||
| ```csharp | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<Post>() | ||
| .Property(p => p.Id) | ||
| .Metadata.SetValueGenerationStrategy(SqliteValueGenerationStrategy.None); | ||
| } | ||
| ``` | ||
|
|
||
| Alternatively, you can disable value generation entirely: | ||
|
|
||
| ```csharp | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<Blog>() | ||
AndriySvyryd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .Property(b => b.Id) | ||
| .ValueGeneratedNever(); | ||
| } | ||
| ``` | ||
|
|
||
| This means that it's up to the application to supply a value for the property before saving to the database. Note that this still won't disable the default value generation server-side, so non-EF usages could still get a generated value. To completely disable value generation the user can change the column type from `INTEGER` to `INT`. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Link to https://sqlite.org/lang_createtable.html#rowids_and_the_integer_primary_key for the last part . |
||
|
|
||
| ## Migration behavior | ||
|
|
||
| When EF Core generates migrations for SQLite AUTOINCREMENT columns, the generated migration will include the `Sqlite:Autoincrement` annotation: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is internal implementation details which IMHO doesn't belong here (we don't do this for any other features anywhere else in the docs). |
||
|
|
||
| ```csharp | ||
| migrationBuilder.CreateTable( | ||
| name: "Blogs", | ||
| columns: table => new | ||
| { | ||
| Id = table.Column<int>(type: "INTEGER", nullable: false) | ||
| .Annotation("Sqlite:Autoincrement", true), | ||
| Title = table.Column<string>(type: "TEXT", nullable: true) | ||
| }, | ||
| constraints: table => | ||
| { | ||
| table.PrimaryKey("PK_Blogs", x => x.Id); | ||
| }); | ||
| ``` | ||
|
|
||
| This ensures that the AUTOINCREMENT feature is properly applied when the migration is executed against the SQLite database. | ||
|
Check failure on line 94 in entity-framework/core/providers/sqlite/value-generation.md
|
||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -628,6 +628,14 @@ await context.Blogs.ExecuteUpdateAsync(s => | |
|
|
||
| Thanks to [@aradalvand](https://github.com/aradalvand) for proposing and pushing for this change (in [#32018](https://github.com/dotnet/efcore/issues/32018)). | ||
|
|
||
| <a name="sqlite"></a> | ||
|
|
||
| ## SQLite | ||
|
|
||
| ### Improved AUTOINCREMENT support | ||
|
|
||
| SQLite AUTOINCREMENT is now a first-class feature with full support through conventions and the Fluent API. Previously, properties with value converters couldn't configure AUTOINCREMENT and would cause false pending model change warnings. | ||
AndriySvyryd marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, the actual user-facing improvement (or at least the main one) is us properly supporting autoincrement on value-converted properties (the rest of it doesn't seem very user-facing). IMHO this merits a one-line bullet below under improvements, rather than a whole top-level "SQLite" section which then contains only this. |
||
|
|
||
| <a name="other-improvements"></a> | ||
|
|
||
| ## Other improvements | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| using Microsoft.EntityFrameworkCore; | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| namespace EFCore.Sqlite.ValueGeneration; | ||
|
|
||
| internal class SqliteAutoincrementContext : DbContext | ||
| { | ||
| public DbSet<Blog> Blogs { get; set; } | ||
|
|
||
| #region SqliteAutoincrement | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<Blog>() | ||
| .Property(b => b.Id) | ||
| .UseAutoincrement(); | ||
| } | ||
| #endregion | ||
|
|
||
| protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | ||
| => optionsBuilder.UseSqlite("Data Source=sample.db"); | ||
| } | ||
|
|
||
| public class Blog | ||
| { | ||
| public int Id { get; set; } | ||
| public string Title { get; set; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| using Microsoft.EntityFrameworkCore; | ||
|
|
||
| namespace EFCore.Sqlite.ValueGeneration; | ||
|
|
||
| public readonly struct BlogId | ||
| { | ||
| public BlogId(int value) => Value = value; | ||
| public int Value { get; } | ||
|
|
||
| public static implicit operator int(BlogId id) => id.Value; | ||
| public static implicit operator BlogId(int value) => new(value); | ||
| } | ||
|
|
||
| internal class SqliteAutoincrementWithValueConverterContext : DbContext | ||
AndriySvyryd marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| public DbSet<BlogPost> Blogs { get; set; } | ||
|
|
||
| #region SqliteAutoincrementWithValueConverter | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<BlogPost>() | ||
| .Property(b => b.Id) | ||
| .HasConversion<int>() | ||
| .UseAutoincrement(); | ||
| } | ||
| #endregion | ||
|
|
||
| protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | ||
| => optionsBuilder.UseSqlite("Data Source=sample.db"); | ||
| } | ||
|
|
||
| public class BlogPost | ||
| { | ||
| public BlogId Id { get; set; } | ||
| public string Title { get; set; } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>disable</Nullable> | ||
| <OutputType>Library</OutputType> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.1.25451.107" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using Microsoft.EntityFrameworkCore; | ||
| using Microsoft.EntityFrameworkCore.Sqlite.Metadata; | ||
|
|
||
| namespace EFCore.Sqlite.ValueGeneration; | ||
|
|
||
| internal class SqliteValueGenerationStrategyNoneContext : DbContext | ||
| { | ||
| public DbSet<Post> Posts { get; set; } | ||
|
|
||
| #region SqliteValueGenerationStrategyNone | ||
| protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
| { | ||
| modelBuilder.Entity<Post>() | ||
| .Property(p => p.Id) | ||
| .Metadata.SetValueGenerationStrategy(SqliteValueGenerationStrategy.None); | ||
| } | ||
| #endregion | ||
|
|
||
| protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | ||
| => optionsBuilder.UseSqlite("Data Source=sample.db"); | ||
| } | ||
|
|
||
| public class Post | ||
| { | ||
| public int Id { get; set; } | ||
| public string Content { get; set; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "sdk": { | ||
| "version": "10.0.100-rc.1.25451.107" | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.