Limitações do Provedor de Banco de Dados EF Core do SQLite

O provedor SQLite tem várias limitações de migrações. A maioria dessas limitações é resultado de limitações no mecanismo de banco de dados SQLite subjacente e não são específicas do EF.

Limitações de modelagem

A biblioteca relacional comum (compartilhada por provedores de banco de dados relacionais do Entity Framework) define APIs para conceitos de modelagem que são comuns à maioria dos mecanismos de banco de dados relacionais. Alguns desses conceitos não são compatíveis com o provedor SQLite.

Limitações da consulta

O SQLite não dá suporte nativo aos seguintes tipos de dados. O EF Core pode ler e gravar valores desses tipos, e também há suporte para a consulta por igualdade (where e.Property == value). No entanto, outras operações, como comparação e ordenação, exigirão avaliação no cliente.

  • DateTimeOffset
  • Decimal
  • TimeSpan
  • UInt64

Em vez de DateTimeOffset, é recomendável usar valores DateTime. Ao manipular vários fusos horários, recomendamos converter os valores em UTC antes de salvar e converter novamente no fuso horário apropriado.

O tipo Decimal fornece um alto nível de precisão. No entanto, se você não precisar desse nível de precisão, recomendamos usar o duplo. Você pode usar um conversor de valor para continuar usando decimal em suas classes.

modelBuilder.Entity<MyEntity>()
    .Property(e => e.DecimalProperty)
    .HasConversion<double>();

Limitações de migrações

O mecanismo de banco de dados SQLite não dá suporte a várias operações de esquema compatíveis com a maioria dos outros bancos de dados relacionais. Se você tentar aplicar uma das operações sem suporte a um banco de dados SQLite, uma NotSupportedException será lançada.

Uma recompilação será tentada para executar determinadas operações. Recompilações só são possíveis para artefatos de banco de dados que fazem parte do modelo do EF Core. Se um artefato de banco de dados não fizer parte do modelo, por exemplo, se ele tiver sido criado manualmente dentro de uma migração, um NotSupportedException ainda será lançado.

Operação Compatível?
AddCheckConstraint ✔ (rebuild)
AddColumn
AddForeignKey ✔ (rebuild)
AddPrimaryKey ✔ (rebuild)
AddUniqueConstraint ✔ (rebuild)
AlterColumn ✔ (rebuild)
CreateIndex
CreateTable
DropCheckConstraint ✔ (rebuild)
DropColumn ✔ (rebuild)
DropForeignKey ✔ (rebuild)
DropIndex
DropPrimaryKey ✔ (rebuild)
DropTable
DropUniqueConstraint ✔ (rebuild)
RenameColumn
RenameIndex ✔ (rebuild)
RenameTable
EnsureSchema ✔ (no-op)
DropSchema ✔ (no-op)
Inserir
Atualizar
Delete

Solução alternativa de limitações de migrações

Você pode solucionar algumas dessas limitações escrevendo manualmente o código em suas migrações para executar uma recompilação. As recompilações de tabela envolvem a criação de uma nova tabela, a cópia de dados para a nova tabela, a remoção da tabela antiga, a renomeação da nova tabela. Você precisará usar o método Sql(string) para executar algumas dessas etapas.

Consulte Como fazer outros tipos de alterações de esquema de tabela na documentação do SQLite para obter mais detalhes.

Limitações de script idempotente

Ao contrário de outros bancos de dados, o SQLite não inclui uma linguagem processual. Por isso, não há como gerar a lógica if-then exigida pelos scripts de migração idempotentes.

Se você souber a última migração aplicada a um banco de dados, poderá gerar um script dessa migração para a migração mais recente.

dotnet ef migrations script CurrentMigration

Caso contrário, recomendamos usar dotnet ef database update para aplicar migrações. Você pode especificar o arquivo de banco de dados ao executar o comando.

dotnet ef database update --connection "Data Source=My.db"

Proteção contra migrações simultâneas

O EF9 introduziu um mecanismo de bloqueio ao executar migrações. O objetivo é proteger contra várias execuções de migração simultâneas, pois isso pode deixar o banco de dados em um estado corrompido. Esse é um dos possíveis problemas resultantes da aplicação de migrações em runtime usando o método DbContext.Database.Migrate() (consulte Aplicação de migrações para obter mais informações). Para atenuar isso, o EF cria um bloqueio exclusivo no banco de dados antes que qualquer operação de migração seja aplicada.

Infelizmente, o SQLite não tem um mecanismo de bloqueio interno, portanto, o EF cria uma tabela separada (__EFMigrationsLock) e a usa para bloqueio. O bloqueio é liberado quando a migração é concluída e o código de propagação conclui a execução. No entanto, se, por algum motivo, a migração falhar de forma não recuperável, o bloqueio poderá não ser liberado corretamente. Se isso acontecer, as migrações consecutivas serão impedidas de executar o SQL e, portanto, nunca serão concluídas. Você pode desbloqueá-las manualmente excluindo a tabela __EFMigrationsLock no banco de dados.

Confira também