Asp.net core中Migration工具使用的交流分享


 

一、文章參數

二、開篇碎語

三、主要內容

四、篇后總結


一、文章參數

  • 開發工具:

    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源碼地址:

    https://github.com/boomyao/Blogs

二、開篇碎語

記得去年第一次做項目,用了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; }
    }
Store Models

 

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);
                });
        }
View Code

一個讓我很困惑的問題在進行第一次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);
                });
初次Migration中發生意外的代碼(注釋的那些)

自動添加了帶artist1songlist1的字段,我很希望知道的朋友告訴我解決的辦法!!我實在不知道怎么讓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塊的彩票一樣。

希望有人看完這篇文章吧,新寫手最需要的就是多給建議呀!謝謝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM