最稳组合是Microsoft.Data.Sqlite+EF Core,但须手动启用外键、避免迁移中NOT NULL列、用绝对路径构造数据库文件,且DbContext必须注册为Scoped。

直接用 Microsoft.Data.Sqlite + EF Core 是目前最稳的组合,但必须手动启用外键、禁用迁移中的 NOT NULL 默认值、且数据库文件路径要用绝对路径——否则 Update-Database 会静默失败或报 “no such table”。
EF Core 连接 SQLite 必须显式启用外键约束
SQLite 默认关闭外键检查,EF Core 的导航属性、级联删除、Required 关系在运行时全失效,但编译和迁移都不报错,查数据时才发现关联为空。
- 配置时必须加
b.EnableForeignKeyConstraints(true),不能只写UseSqlite(connectionString) - 正确写法示例:
services.AddDbContext<AppDbContext>(options =><br> options.UseSqlite(connectionString, b => b.EnableForeignKeyConstraints(true))<br>);
- 如果用
dotnet ef migrations add生成了迁移,再补这个配置,已生成的迁移不会自动重写——得手动在Up()里加Sql("PRAGMA foreign_keys = ON;");
Migration 中的 NOT NULL 列会直接让 Update-Database 失败
SQLite 不支持在 ALTER TABLE ADD COLUMN 时指定 NOT NULL(除非带默认值),而 EF Core 默认给 Required 属性生成这种语句,结果执行 Update-Database 报错:Cannot add a NOT NULL column with default value NULL。
- 解决方法不是删掉 Required,而是改用 Fluent API 显式控制:
modelBuilder.Entity<User>().Property(x => x.Email).IsRequired(false); - 或者在迁移的
Up()方法里,用Sql()手动分两步:先ADD COLUMN email TEXT,再UPDATE ... SET email = 'default@example.com',最后ALTER TABLE ... ALTER COLUMN email SET NOT NULL - 别依赖
HasDefaultValue自动补默认值——SQLite 对 DEFAULT 的语法支持有限,尤其在 ALTER 场景下容易崩
连接字符串路径不对,迁移和运行时都找不到 db 文件
EF Core 的 Update-Database 在设计时工作目录是项目根目录,但运行时(比如 WPF 双击 exe 启动)当前目录可能是 C:\Windows\System32,导致它在错误位置建空 app.db,而你的代码读的是项目输出目录下的同名文件。
- 永远用
Path.Combine(AppContext.BaseDirectory, "app.db")构造路径,别写相对路径"app.db" - 确保目录存在:
Directory.CreateDirectory(Path.GetDirectoryName(dbPath));,SQLite 不会自动创建父目录 - 如果用
dotnet run,BaseDirectory 是bin/Debug/net8.0/;发布后是发布目录——这两处 db 文件物理位置不同,别混着测
DbContext 生命周期必须是 Scoped,不能 Singleton 或 static
SQLite 是文件锁模型,一个 DbContext 实例长期持有连接会导致并发写入阻塞、事务无法提交,甚至 SqliteException: database is locked。
- ASP.NET Core 中注册为
Scoped是底线:services.AddDbContext<AppDbContext>(...);(默认就是 Scoped) - WinForms/WPF 中别在窗体类里 new static DbContext 字段——每次查询都应新建实例或从 DI 容器解析
- EF Core 不会自动释放底层
SqliteConnection,若手动调用context.Database.GetDbConnection().Open(),务必自己Close()或包在using
最容易被忽略的是:迁移成功不代表运行时能查到数据。因为迁移用的设计时路径和运行时路径不是同一个文件——连 db 文件都错了,表再对也没用。


















