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块的彩票一样。

希望有人看完这篇文章吧,新写手最需要的就是多给建议呀!谢谢

posted @ 2016-08-07 09:01  boomyao  阅读(4929)  评论(3编辑  收藏  举报