一、文章參數
二、開篇碎語
三、主要內容
四、篇后總結
一、文章參數
- 開發工具:
visual studio 2015 community update 3 + .net core tools(preview2) + sqlserver2012 express
- 開發環境:
win10(版本14393)+ .net core(版本 1.0.0-preview2-003121)
- 項目名稱:
AirMusic
- 項目模板:
Asp.net core WebApi(這里不用mvc模板,是因為mvc模板初始的內容太多,這里用不到)
- AirMusic源碼地址:
二、開篇碎語
記得去年第一次做項目,用了asp.net mvc5,因此也第一接觸了EntityFramework(版本是EF6)。 現在打算用asp.net core來完成一個項目,air music是學習asp.net core時新建的demo項目,以后學習core中遇到的一些問題一般會從這個項目產生,今天這篇文章是在migration初始化數據庫時發生的一些問題。
三、主要內容
1、初始化的實體模型

public class Song { public int Id { get; set; } [Required] public string SongName { get; set; } public virtual ICollection<ArtistSong> Artists { get; set; } public virtual ICollection<SongListSong> SongLists { get; set; } } //歌手 public class Artist { public int Id { get; set; } [Required] public string Name { get; set; } public virtual ICollection<Album> Albums { get; set; } public virtual ICollection<ArtistSong> Songs { get; set; } } //song with artist n:n table public class ArtistSong { [Key] public int ArtistId { get; set; } [Key] public int SongId { get; set; } } //專輯 public class Album { public int Id { get; set; } [Required] public string Title { get; set; } public virtual ICollection<Song> Songs { get; set; } } //歌單 public class SongList { public int Id { get; set; } [Required] public string Title { get; set; } public string Describe { get; set; } public virtual ApplicationUser User { get; set; } public virtual ICollection<SongListSong> Songs { get; set; } } // song with songlist n:n table public class SongListSong { [Key] public int SongListId { get; set; } [Key] public int SongId { get; set; } }
2、引用EFcoore相關的Nuget包
-
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0"
(依賴了好多ef重要組件) -
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
(引用了才可以試用migration) -
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0"
(我覺得是ef連接sqlserver的必須組件)
還有就是必須在project.json里的tools節點中添加"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",不然輸入命令”Add Migration“時就會報出下面這句錯誤。
"No parameterless constructor………“這句錯誤是因為ApplicationDbContext(數據庫上下文類)沒有寫下面這個構造函數報的錯誤。
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
3、配置數據庫連接
Webapi模板已經創建好了appsetting.json文件,這個文件的作用和web.config是一樣的


4、文章包袱:Migration過程中遇到的問題
在ApplicationDbContext重寫的方法OnModelCreating
中,有一行代碼
base.OnModelCreating(builder)
當我把這行代碼注釋掉時,遷移過程中就會報出如下錯誤:
很明顯,錯誤發生的原因是IdentityUser沒有被映射到ApplicationDbContext,所以可以知道這句代碼的作用,就是用來映射Identity的幾個實體類的,沒有這句,數據庫中就不會自動生成Users、Roles……等Table了,我還沒有去github看這個方法的源碼,但我覺得實際上就是第一次Add-Migration時,生成的源文件ApplicationDbContextModelSnapshot.cs 中的代碼:

protected override void BuildModel(ModelBuilder modelBuilder) { modelBuilder .HasAnnotation("ProductVersion", "1.0.0-rtm-21431") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole", b => { b.Property<string>("Id"); b.Property<string>("ConcurrencyStamp") .IsConcurrencyToken(); b.Property<string>("Name") .HasAnnotation("MaxLength", 256); b.Property<string>("NormalizedName") .HasAnnotation("MaxLength", 256); b.HasKey("Id"); b.HasIndex("NormalizedName") .HasName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b => { b.Property<int>("Id") .ValueGeneratedOnAdd(); b.Property<string>("ClaimType"); b.Property<string>("ClaimValue"); b.Property<string>("RoleId") .IsRequired(); b.HasKey("Id"); b.HasIndex("RoleId"); b.ToTable("AspNetRoleClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser", b => { b.Property<string>("Id"); b.Property<int>("AccessFailedCount"); b.Property<string>("ConcurrencyStamp") .IsConcurrencyToken(); b.Property<string>("Discriminator") .IsRequired(); b.Property<string>("Email") .HasAnnotation("MaxLength", 256); b.Property<bool>("EmailConfirmed"); b.Property<bool>("LockoutEnabled"); b.Property<DateTimeOffset?>("LockoutEnd"); b.Property<string>("NormalizedEmail") .HasAnnotation("MaxLength", 256); b.Property<string>("NormalizedUserName") .HasAnnotation("MaxLength", 256); b.Property<string>("PasswordHash"); b.Property<string>("PhoneNumber"); b.Property<bool>("PhoneNumberConfirmed"); b.Property<string>("SecurityStamp"); b.Property<bool>("TwoFactorEnabled"); b.Property<string>("UserName") .IsRequired() .HasAnnotation("MaxLength", 256); b.HasKey("Id"); b.HasIndex("NormalizedEmail") .HasName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() .HasName("UserNameIndex"); b.ToTable("AspNetUsers"); b.HasDiscriminator<string>("Discriminator").HasValue("IdentityUser"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b => { b.Property<int>("Id") .ValueGeneratedOnAdd(); b.Property<string>("ClaimType"); b.Property<string>("ClaimValue"); b.Property<string>("UserId") .IsRequired(); b.HasKey("Id"); b.HasIndex("UserId"); b.ToTable("AspNetUserClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b => { b.Property<string>("LoginProvider"); b.Property<string>("ProviderKey"); b.Property<string>("ProviderDisplayName"); b.Property<string>("UserId") .IsRequired(); b.HasKey("LoginProvider", "ProviderKey"); b.HasIndex("UserId"); b.ToTable("AspNetUserLogins"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b => { b.Property<string>("UserId"); b.Property<string>("RoleId"); b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); b.HasIndex("UserId"); b.ToTable("AspNetUserRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserToken<string>", b => { b.Property<string>("UserId"); b.Property<string>("LoginProvider"); b.Property<string>("Name"); b.Property<string>("Value"); b.HasKey("UserId", "LoginProvider", "Name"); b.ToTable("AspNetUserTokens"); }); modelBuilder.Entity("AirMusic.Models.ApplicationUser", b => { b.HasBaseType("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRoleClaim<string>", b => { b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") .WithMany("Claims") .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserClaim<string>", b => { b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser") .WithMany("Claims") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin<string>", b => { b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser") .WithMany("Logins") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserRole<string>", b => { b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityRole") .WithMany("Users") .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); b.HasOne("Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser") .WithMany("Roles") .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); }
一個讓我很困惑的問題在進行第一次Migration時出現了。AirMusic的實體中,有這樣一種關系:
但是神奇的事情發生了(我不想這樣的):

migrationBuilder.CreateTable( name: "ArtistSongs", columns: table => new { ArtistId = table.Column<int>(nullable: false), SongId = table.Column<int>(nullable: false), //ArtistId1 = table.Column<int>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_ArtistSongs", x => new { x.ArtistId, x.SongId }); table.ForeignKey( name: "FK_ArtistSongs_Artists_ArtistId", column: x => x.ArtistId, principalTable: "Artists", principalColumn: "Id", onDelete: ReferentialAction.Cascade); //table.ForeignKey( // name: "FK_ArtistSongs_Artists_ArtistId1", // column: x => x.ArtistId1, // principalTable: "Artists", // principalColumn: "Id", // onDelete: ReferentialAction.Restrict); table.ForeignKey( name: "FK_ArtistSongs_Songs_SongId", column: x => x.SongId, principalTable: "Songs", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "SongListSongs", columns: table => new { SongListId = table.Column<int>(nullable: false), SongId = table.Column<int>(nullable: false), //SongListId1 = table.Column<int>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_SongListSongs", x => new { x.SongListId, x.SongId }); table.ForeignKey( name: "FK_SongListSongs_Songs_SongId", column: x => x.SongId, principalTable: "Songs", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_SongListSongs_SongLists_SongListId", column: x => x.SongListId, principalTable: "SongLists", principalColumn: "Id", onDelete: ReferentialAction.Cascade); //table.ForeignKey( // name: "FK_SongListSongs_SongLists_SongListId1", // column: x => x.SongListId1, // principalTable: "SongLists", // principalColumn: "Id", // onDelete: ReferentialAction.Restrict); });
自動添加了帶artist1和songlist1的字段,我很希望知道的朋友告訴我解決的辦法!!我實在不知道怎么讓EF不自動添加這個多余的字段,所以我把那些多余的字段都注釋掉后,才update-database到數據庫:
song-artist[N:N] , song-songlist[N:N]
在ApplicationDbContext的OnModelCreating方法里,可以手動的配置數據庫中的關系,像什么組合主鍵啦,組合外鍵啦等等各種約束,都可以 實現。特別是數據庫中實體的關系配置(1:1,1:N,N:N),例如:用方法builder.HasOne().WithMany()就可以建立[1:N]的關系。AirMusic初次Migration中,我也手動的配置了一些關系:
var entityAS=builder.Entity<ArtistSong>(); entityAS.HasKey("ArtistId", "SongId"); entityAS.HasOne<Artist>() .WithMany() .HasForeignKey("ArtistId"); var entitySS = builder.Entity<SongListSong>(); entitySS.HasKey("SongListId", "SongId"); entitySS.HasOne<SongList>() .WithMany() .HasForeignKey("SongListId");
上面代碼的作用是,ArtistId和SongId設為ArtistSong的組合主鍵,ArtistSong的ArtistId設為Artist的外鍵。entitySS的作用也大致相同。
四、篇后總結
第一次完整的寫一篇博文,晚上去吃飯是電腦自動重啟更新了,vscode里的代碼都沒保存,打開博客園文章管理發現什么都沒了,難過的就去聽歌睡覺了。第二天起來打算從新來過時,發現有一行“自動保存恢復”,那個感覺就和中了100塊的彩票一樣。
希望有人看完這篇文章吧,新寫手最需要的就是多給建議呀!謝謝