EF7 使用 SQLite 生成错误的迁移

EF7 generates wrong migrations with SQLite

我尝试将 ASP.Net 5 与 Entity Framework 7 和 SQLite 一起使用,我发现了迁移器的 st运行ge 行为。

我参加了 Visual Studio 2015 并使用模板 "ASP.NET 5 Template Web Application" 创建了一个新项目。

之后我删除了依赖项

"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final"

并添加了两个依赖项,然后称为恢复包。

"EntityFramework.Sqlite": "7.0.0-rc1-final",
"Microsoft.Data.Sqlite": "1.0.0-rc1-final"

像那样

"dependencies": {
"EntityFramework.Commands": "7.0.0-rc1-final",
"EntityFramework.Sqlite": "7.0.0-rc1-final",
"Microsoft.Data.Sqlite": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final",
"Microsoft.AspNet.Diagnostics.Entity": "7.0.0-rc1-final",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
"Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc1-final"
}

接下来我去 Startup.cs 并像那样更改函数 ConfigurationServices

 public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        var dbFilePath =     Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "test.db");
        services.AddEntityFramework()
         .AddSqlite()
         .AddDbContext<ApplicationDbContext>(options =>
             options.UseSqlite("Filename=" + dbFilePath));
        System.Console.WriteLine("Use DB file:" + dbFilePath);

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.AddMvc();

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
        services.AddInstance<IConfigurationRoot>(Configuration);
    }

然后我从文件夹 "Migrations" 中删除了所有文件并构建了解决方案。 所有人都已准备好创建迁移。 我在项目文件夹中 运行 git bash 并输入下一个命令:

blush@BLUSH /d/dev/testMVC
$ dnx ef migrations add firstMigration
Use DB file:D:\dev\testMVC\test.db
Done. To undo this action, use 'ef migrations remove'

$ dnx ef database update
Use DB file:D:\dev\testMVC\test.db
Applying migration '20160304130532_firstMigration'.
Done.

好的,我在文件夹 "Migrations" 中看到了一个数据库文件和文件,看起来不错。 而且我猜想下一个生成的迁移将是空的,而不会更改对象模型。 我尝试这样做并输入下一个命令:

$ dnx ef migrations add testMigration
Use DB file:D:\dev\testMVC\test.db
Done. To undo this action, use 'ef migrations remove'

我去看了生成的迁移 class 我看到了这段代码:

 protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropForeignKey(name: "FK_IdentityRoleClaim<string>_IdentityRole_RoleId", table: "AspNetRoleClaims");
            migrationBuilder.DropForeignKey(name: "FK_IdentityUserClaim<string>_ApplicationUser_UserId", table: "AspNetUserClaims");
            migrationBuilder.DropForeignKey(name: "FK_IdentityUserLogin<string>_ApplicationUser_UserId", table: "AspNetUserLogins");
            migrationBuilder.DropForeignKey(name: "FK_IdentityUserRole<string>_IdentityRole_RoleId", table: "AspNetUserRoles");
            migrationBuilder.DropForeignKey(name: "FK_IdentityUserRole<string>_ApplicationUser_UserId", table: "AspNetUserRoles");
            migrationBuilder.AddForeignKey(
                name: "FK_IdentityRoleClaim<string>_IdentityRole_RoleId",
                table: "AspNetRoleClaims",
                column: "RoleId",
                principalTable: "AspNetRoles",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            migrationBuilder.AddForeignKey(
                name: "FK_IdentityUserClaim<string>_ApplicationUser_UserId",
                table: "AspNetUserClaims",
                column: "UserId",
                principalTable: "AspNetUsers",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            migrationBuilder.AddForeignKey(
                name: "FK_IdentityUserLogin<string>_ApplicationUser_UserId",
                table: "AspNetUserLogins",
                column: "UserId",
                principalTable: "AspNetUsers",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            migrationBuilder.AddForeignKey(
                name: "FK_IdentityUserRole<string>_IdentityRole_RoleId",
                table: "AspNetUserRoles",
                column: "RoleId",
                principalTable: "AspNetRoles",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            migrationBuilder.AddForeignKey(
                name: "FK_IdentityUserRole<string>_ApplicationUser_UserId",
                table: "AspNetUserRoles",
                column: "UserId",
                principalTable: "AspNetUsers",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
       ... 
       }

如果我尝试应用此迁移,请捕获下一条错误消息:

$ dnx ef database update
Use DB file:D:\dev\testMVC\test.db
Applying migration '20160304133144_testMigration'.
System.NotSupportedException: SQLite cannot support this migration operation.
   в Microsoft.Data.Entity.Migrations.SqliteMigrationsSqlGenerator.Generate(Drop
ForeignKeyOperation operation, IModel model, RelationalCommandListBuilder builde
r)
   в Microsoft.Data.Entity.Migrations.MigrationsSqlGenerator.<>c.<.cctor>b__50_1
0(MigrationsSqlGenerator g, MigrationOperation o, IModel m, RelationalCommandLis
tBuilder b)
   в Microsoft.Data.Entity.Migrations.MigrationsSqlGenerator.Generate(MigrationO
peration operation, IModel model, RelationalCommandListBuilder builder)
   в Microsoft.Data.Entity.Migrations.MigrationsSqlGenerator.Generate(IReadOnlyL
ist`1 operations, IModel model)
   в Microsoft.Data.Entity.Migrations.SqliteMigrationsSqlGenerator.Generate(IRea
dOnlyList`1 operations, IModel model)
   в Microsoft.Data.Entity.Migrations.Internal.Migrator.GenerateUpSql(Migration
migration)
   в Microsoft.Data.Entity.Migrations.Internal.Migrator.<>c__DisplayClass12_4.<G
etMigrationCommands>b__10()
   в Microsoft.Data.Entity.Migrations.Internal.Migrator.Migrate(String targetMig
ration)
   в Microsoft.Data.Entity.Design.MigrationsOperations.UpdateDatabase(String tar
getMigration, String contextType)
   в Microsoft.Data.Entity.Commands.Program.Executor.<>c__DisplayClass7_0.<Updat
eDatabase>b__0()
   в Microsoft.Data.Entity.Commands.Program.Executor.Execute(Action action)
SQLite cannot support this migration operation.

还有一个问题:我应该怎么做才能解决这个问题?

这是 SQLite 的限制。参见 SQLite Limitations in EF

出现这种情况是因为SQLite本身不支持"ADD FOREIGN KEY"。解决此问题的方法是进行 table 重建。 Making Other Kinds Of Table Schema Changes on sqlite.org 提供了有关如何构造查询以执行此操作的说明。您将很快看到这在 SQLite 上有多混乱,以及为什么 EF 不为您自动执行此操作。

如果您不需要 SQLite 数据库中的数据,您最好删除它并使用 context.Database.EnsureCreated() 使用新模型从头开始重建 table。

我在使用 Visual Studio Community 2017 for Mac

时遇到了这个问题

我发现最简单的方法是删除数据库并重新创建迁移。我当然买得起,因为项目处于开发的早期阶段:

  1. 删除数据库文件(app.db)
  2. 发出命令dotnet ef migrations remove直到只有第一个迁移(00000000000000_CreateIdentitySchema)
  3. 添加新迁移dotnet ef migrations add CreatedEntities(例如)
  4. 更新数据库dotnet ef database update

如果 SQLite 仅用于 in-memory 单元测试,试试这个:

if (context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
{
    context.Database.EnsureCreated();
}
else
{
    context.Database.Migrate();
}