SQLite EF Core 数据库提供程序限制
SQLite 提供程序存在大量迁移限制。 其中大多数限制源自底层 SQLite 数据库引擎中存在的限制,并不特定于 EF。
建模限制
公共关系库(由 Entity Framework 关系数据库提供程序共享)定义 API,用于建模大多数关系数据库引擎所通用的概念。 SQLite 提供程序不支持其中的数个概念。
- 架构
- 序列
- 数据库生成的并发令牌(请参阅文档)
查询限制
SQLite 本身不支持以下数据类型。 EF Core 可以读取和写入这些类型的值,并且还支持查询相等性 (where e.Property == value
)。 但对比和排序等其他操作需要对客户端进行评估。
- DateTimeOffset
- 十进制
- TimeSpan
- UInt64
建议使用 DateTime 值,而不是 DateTimeOffset
。 处理多个时区时,建议将值转换为 UTC 再保存,然后转换回适当的时区。
Decimal
类型的准确度高。 但如果不需要该级别的准确度,建议改用 double。 可以使用值转换器继续在类中使用十进制。
modelBuilder.Entity<MyEntity>()
.Property(e => e.DecimalProperty)
.HasConversion<double>();
迁移限制
SQLite 数据库引擎不支持大多数其他关系数据库所支持的许多架构操作。 如果尝试在 SQLite 数据库进行某一种不受支持的操作,则会引发 NotSupportedException
。
为执行某些操作,请尝试重新构建。 仅 EF Core 模型中的数据库项目可以进行重新构建。 如果数据库项目不属于模型,例如,如果项目是在迁移期间手动创建的,则仍会引发 NotSupportedException
。
Operation | 是否支持? |
---|---|
AddCheckConstraint | ✔(重新构建) |
AddColumn | ✔ |
AddForeignKey | ✔(重新构建) |
AddPrimaryKey | ✔(重新构建) |
AddUniqueConstraint | ✔(重新构建) |
AlterColumn | ✔(重新构建) |
CreateIndex | ✔ |
CreateTable | ✔ |
DropCheckConstraint | ✔(重新构建) |
DropColumn | ✔(重新构建) |
DropForeignKey | ✔(重新构建) |
DropIndex | ✔ |
DropPrimaryKey | ✔(重新构建) |
DropTable | ✔ |
DropUniqueConstraint | ✔(重新构建) |
RenameColumn | ✔ |
RenameIndex | ✔(重新构建) |
RenameTable | ✔ |
EnsureSchema | ✔(无操作) |
DropSchema | ✔(无操作) |
插入 | ✔ |
更新 | ✔ |
删除 | ✔ |
迁移限制解决方法
可以通过在迁移时手动编写代码来重新构建,从而解决其中部分限制。 表重新构建包括创建新表、将数据复制到新表、删除旧表和重命名新表。 你将需要使用 Sql(string)
方法来执行其中部分步骤。
有关详细信息,请参阅 SQLite 文档中的更改其他类型的表架构。
幂等脚本限制
与其他数据库不同,SQLite 不包含过程式语言。 因此,无法生成幂等迁移脚本所需的 if-then 逻辑。
如果了解在数据库中进行的上一次迁移,则可以生成从该迁移到最新迁移的脚本。
dotnet ef migrations script CurrentMigration
否则,建议使用 dotnet ef database update
来进行迁移。 可以在运行命令时指定数据库文件。
dotnet ef database update --connection "Data Source=My.db"
并发迁移保护
EF9 在执行迁移时引入了锁定机制。 它旨在防止同时执行多个迁移,因为这会使数据库处于损坏状态。 这是在运行时使用 DbContext.Database.Migrate()
方法应用迁移所导致的潜在问题之一(有关详细信息,请参阅应用迁移)。 为了缓解这种情况,EF 会在应用任何迁移操作之前在数据库上创建排他锁。
遗憾的是,SQLite 没有内置的锁定机制,因此 EF 会创建一个单独的表 (__EFMigrationsLock
) 并将其用于锁定。 当迁移完成并且种子设定代码完成执行时,锁被释放。 但是,如果由于某种原因迁移以不可恢复的方式失败,则锁可能无法正确释放。 如果发生这种情况,连续的迁移将被阻止执行 SQL,因此永远不会完成。 可以通过删除数据库中的 __EFMigrationsLock
表来手动取消阻止它们。