Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions entity-framework/core/providers/sqlite/value-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
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](https://sqlite.org/autoinc.html). Starting with EF Core 10, SQLite AUTOINCREMENT is implemented through conventions and the Fluent API, so it can be enabled or disabled as necessary.

### Configuring AUTOINCREMENT

By convention, integer primary keys are automatically configured with AUTOINCREMENT when they are not composite and don't have a foreign key on them. 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:

[!code-csharp[Main](../../../../samples/core/Sqlite/ValueGeneration/SqliteAutoincrementWithValueConverter.cs?name=SqliteAutoincrementWithValueConverter&highlight=4)]

## Disabling AUTOINCREMENT for default SQLite value generation

AUTOINCREMENT imposes extra CPU, memory, disk space, and disk I/O overhead compared to the default key generation algorithm in SQLite - [ROWID](https://sqlite.org/lang_createtable.html#rowid). The downside of `ROWID` is that it reuses values from deleted rows. If your scenario wouldn't be affected by this, you may want to disable AUTOINCREMENT and use SQLite's default value generation behavior instead. You can do this using the Metadata API:

<!--
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.Metadata.SetValueGenerationStrategy(SqliteValueGenerationStrategy.None);
}
-->
[!code-csharp[Main](../../../../samples/core/Sqlite/ValueGeneration/SqliteValueGenerationStrategyNone.cs?name=SqliteValueGenerationStrategyNone&highlight=5)]

Alternatively, you can disable value generation entirely:

<!--
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.ValueGeneratedNever();
}
-->
[!code-csharp[Main](../../../../samples/core/Sqlite/ValueGeneration/SqliteValueGeneratedNever.cs?name=SqliteValueGeneratedNever&highlight=5)]

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](https://sqlite.org/lang_createtable.html#rowids_and_the_integer_primary_key) the user can change the column type from `INTEGER` to `INT`.
1 change: 1 addition & 0 deletions entity-framework/core/what-is-new/ef-core-10.0/whatsnew.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ If `fieldName` is trusted or has been properly sanitized, the warning can be saf

## Other improvements

- AUTOINCREMENT can now be disabled for SQLite and is also supported for properties with value converters. For more information, see [SQLite Value Generation](xref:core/providers/sqlite/value-generation).
- Stop spanning all migrations with a single transaction ([#35096](https://github.com/dotnet/efcore/issues/35096)). This reverts a change done in EF9 which caused issues in various migration scenarios.
- Make SQL Server scaffolding compatible with Azure Data Explorer ([#34832](https://github.com/dotnet/efcore/pull/34832), contributed by [@barnuri](https://github.com/barnuri)).
- Associate the DatabaseRoot with the scoped options instance and not the singleton options ([#34477](https://github.com/dotnet/efcore/pull/34477), contributed by [@koenigst](https://github.com/koenigst)).
Expand Down
2 changes: 2 additions & 0 deletions entity-framework/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@
href: core/providers/sqlite/limitations.md
- name: Function mappings
href: core/providers/sqlite/functions.md
- name: Value generation
href: core/providers/sqlite/value-generation.md
- name: Spatial data
displayName: GIS
href: core/providers/sqlite/spatial.md
Expand Down
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);
}

public class SqliteAutoincrementWithValueConverterContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

#region SqliteAutoincrementWithValueConverter
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.HasConversion<int>()
.UseAutoincrement();
}
#endregion

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=sample.db");
}

public class Blog
{
public BlogId Id { get; set; }
public string Title { get; set; }
}
20 changes: 20 additions & 0 deletions samples/core/Sqlite/ValueGeneration/SqliteValueGeneratedNever.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;

namespace EFCore.Sqlite.ValueGeneration;

public class SqliteValueGeneratedNeverContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

#region SqliteValueGeneratedNever
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.ValueGeneratedNever();
}
#endregion

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=sample.db");
}
14 changes: 14 additions & 0 deletions samples/core/Sqlite/ValueGeneration/SqliteValueGeneration.csproj
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;

public class SqliteValueGenerationStrategyNoneContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

#region SqliteValueGenerationStrategyNone
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Id)
.Metadata.SetValueGenerationStrategy(SqliteValueGenerationStrategy.None);
}
#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; }
}