前言
剛開始接觸EF Core時本着探索的精神去搞,搞着搞着發現出問題了,后來就一直沒解決,覺得很是不爽,借着周末好好看看這塊內容。
EntityFramework Core遷移出現對象在數據庫中已存在
在EF Core之前對於遷移的命令有很多,當進行遷移出現對象已在數據庫中存在時我們通過如何命令即可解決:
Add-Migration Initial -IgnoreChanges
但是在EF Core對於遷移現如今只存在如下兩個命令:
dotnet ef migrations add <<migration_name>>
dotnet ef database update
當我們第一次進行初始化遷移時,表結構完全生成通過 dotnet ef migration add initial 來初始化表,當下次再進行遷移時因為這樣或者那樣無意的操作導致出現如下結果
翻譯成英語則是如下的情況:
There is already an object named in the database
如下為第一次初始化的遷移文件,如下:
public partial class initial : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Blog", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Code = table.Column<string>(nullable: false), Count = table.Column<int>(nullable: false), Name = table.Column<string>(nullable: true), Url = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Blog", x => x.Id); }); migrationBuilder.CreateTable( name: "Book", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Name = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Book", x => x.Id); }); migrationBuilder.CreateTable( name: "Category", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Name = table.Column<string>(nullable: true), ProductId = table.Column<int>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Category", x => x.Id); }); migrationBuilder.CreateTable( name: "Product", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), Code = table.Column<string>(nullable: true), Name = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Product", x => x.Id); }); migrationBuilder.CreateTable( name: "Post", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), BlogId = table.Column<int>(nullable: false), Content = table.Column<string>(nullable: true), Title = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Post", x => x.Id); table.ForeignKey( name: "FK_Post_Blog_BlogId", column: x => x.BlogId, principalTable: "Blog", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ProductCategory", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), CategoryId = table.Column<int>(nullable: false), ProductId = table.Column<int>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ProductCategory", x => x.Id); table.ForeignKey( name: "FK_ProductCategory_Category_CategoryId", column: x => x.CategoryId, principalTable: "Category", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_ProductCategory_Product_ProductId", column: x => x.ProductId, principalTable: "Product", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_Post_BlogId", table: "Post", column: "BlogId"); migrationBuilder.CreateIndex( name: "IX_ProductCategory_CategoryId", table: "ProductCategory", column: "CategoryId"); migrationBuilder.CreateIndex( name: "IX_ProductCategory_ProductId", table: "ProductCategory", column: "ProductId"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Book"); migrationBuilder.DropTable( name: "Post"); migrationBuilder.DropTable( name: "ProductCategory"); migrationBuilder.DropTable( name: "Blog"); migrationBuilder.DropTable( name: "Category"); migrationBuilder.DropTable( name: "Product"); }
此時為了解決上述問題前提是最初的遷移類文件還在,我們需要將Up方法里面的數據全部刪除而對於Down方法里面的數據可刪除可不刪除
public partial class initial : Migration { protected override void Up(MigrationBuilder migrationBuilder) {} protected override void Down(MigrationBuilder migrationBuilder) { ........ } }
通過Up方法來創建表或者修改列,通過Down方法來刪除表或者修改列。上面我們創建了BlogType列,此時我們在映射時將其刪除同時在Blog類中刪除此字段,如下:
public class BlogMap : EntityMappingConfiguration<Blog> { public override void Map(EntityTypeBuilder<Blog> b) { b.ToTable("Blog"); b.HasKey(k => k.Id); b.Property(p => p.Count); b.Property(p => p.Url); b.Property(p => p.Name); b.Property(p => p.Code).IsRequired(); //b.Property(p => p.BlogType).HasColumnType("TINYINT").IsRequired(); } }
此時我們再來進行遷移如下:
dotnet ef migrations add removeBlogType
此時將不會再報錯且生成的removeBlogType遷移類文件如下:
public partial class removeBlogType : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.DropColumn( name: "BlogType", table: "Blog"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<byte>( name: "BlogType", table: "Blog", type: "TINYINT", nullable: false, defaultValue: (byte)0); } }
我們需要再次確保生成的遷移類文件是否是我們需要修改的字段或者對列進行修改的方法是否正確,確保無誤后,接下來再來通過 dotnet ef database update 更新到數據庫中
如上通過意外情況導致上述錯誤,若是將最初遷移的整個文件夾刪除了腫么辦,這個時候真的沒有好的辦法了,我能想到的是:最好事先在建立項目時建立數據庫對比文件,此時就會派上用場不用一個個類去核對表同時也利於部署時進行數據遷移。
總結
本文我們講到了在EF Core遷移時可能出現的意外情況,若是刪除了最初的遷移類文件也給出了能夠想到的方案,不知看到此文的你有何高見?連續發表的EF Core文章都是在項目使用中遇到的問題,所以借此機會重新過了一遍,歡迎一起探討。