我有一个简单的项目,我最初使用 SQL Server 作为后端,使用 EF Core 和代码优先方法(遵循本教程:https://learn.microsoft.com/en-us/ef/core/get-started/aspnetcore/new-db)
现在我想将实现切换到SQLite。我想我可以从SQL Server运行数据库创建的初始迁移并将其应用于SQLite。似乎它无法正常工作:例如。主键上的自动增量没有应用,然后我看到一些不一致(sqlite抱怨应该'INTEGER'
'int'
)等。
那么,这是否意味着迁移依赖于后端?如果是,那么不应该在嵌套文件夹中创建它们(例如。 ./Migrations/SQLServer
)?
谁能解释一下这是如何工作的?
注意:抱歉,我是 EF 代码优先和迁移的新手...谢谢!
Add-Migration
/dotnet ef migrations add
命令生成的迁移是特定于后端的,但您可以手动编辑它们以使其与多个后端兼容。最简单的方法是生成两组迁移,如@alwayslearning的回答中所述,然后将它们合并为一个迁移集。
例如,Id
列可能如下所示:
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn)
.Annotation("Sqlite:Autoincrement", true);
您还可以有条件地执行后端之间可能根本不同的操作:
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
// TODO
}
else if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
{
// TODO
}
是的,迁移取决于后端。每个后端都有其细节,因此迁移通常具有后端细节,例如列规范。
在某些ORM中,可以以抽象方式映射"代码优先"模型,并让后端特定的提供者将此抽象模型映射到后端特定类型,但在任何相对复杂的模型中,很难保持整个映射后端的独立性。
要迁移到 SQLite,您可以更改 Startup 中的配置.cs将 DbContext 注册为类似
services.AddDbContext<YourContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")))
然后,可以使用"dotnet ef migrations add"命令指定迁移的显式路径。此问题包含有关设置显式文件夹路径的更多详细信息。
public class CompositeMigrationsAnnotationsProvider : IMigrationsAnnotationProvider
{
private readonly IMigrationsAnnotationProvider[] _providers;
public CompositeMigrationsAnnotationsProvider(MigrationsAnnotationProviderDependencies dependencies)
{
_providers = new IMigrationsAnnotationProvider[] {
new SqlServerMigrationsAnnotationProvider(dependencies),
new SqliteMigrationsAnnotationProvider(dependencies)
};
}
public IEnumerable<IAnnotation> For(IModel model) => _providers.SelectMany(p => p.For(model));
public IEnumerable<IAnnotation> For(IProperty property) => _providers.SelectMany(p => p.For(property));
public IEnumerable<IAnnotation> For(IIndex index) => _providers.SelectMany(p => p.For(index));
public IEnumerable<IAnnotation> For(IKey key) => _providers.SelectMany(p => p.For(key));
public IEnumerable<IAnnotation> For(IForeignKey foreignKey) => _providers.SelectMany(p => p.For(foreignKey));
public IEnumerable<IAnnotation> For(IEntityType entityType) => _providers.SelectMany(p => p.For(entityType));
public IEnumerable<IAnnotation> For(ISequence sequence) => _providers.SelectMany(p => p.For(sequence));
public IEnumerable<IAnnotation> For(ICheckConstraint checkConstraint) => _providers.SelectMany(p => p.For(checkConstraint)); //EF Core 3.x
public IEnumerable<IAnnotation> ForRemove(IModel model) => _providers.SelectMany(p => p.ForRemove(model));
public IEnumerable<IAnnotation> ForRemove(IIndex index) => _providers.SelectMany(p => p.ForRemove(index));
public IEnumerable<IAnnotation> ForRemove(IProperty property) => _providers.SelectMany(p => p.ForRemove(property));
public IEnumerable<IAnnotation> ForRemove(IKey key) => _providers.SelectMany(p => p.ForRemove(key));
public IEnumerable<IAnnotation> ForRemove(IForeignKey foreignKey) => _providers.SelectMany(p => p.ForRemove(foreignKey));
public IEnumerable<IAnnotation> ForRemove(IEntityType entityType) => _providers.SelectMany(p => p.ForRemove(entityType));
public IEnumerable<IAnnotation> ForRemove(ISequence sequence) => _providers.SelectMany(p => p.ForRemove(sequence));
public IEnumerable<IAnnotation> ForRemove(ICheckConstraint checkConstraint) => _providers.SelectMany(p => p.ForRemove(checkConstraint)); //EF Core 3.x
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IMigrationsAnnotationProvider, CompositeMigrationsAnnotationsProvider>();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.UseIdentityColumns(); //EF Core 3.x
modelBuilder.ForSqlServerUseIdentityColumns(); //EF Core 2.x
}
}