一、文章参数
二、开篇碎语
三、主要内容
四、篇后总结
一、文章参数
- 开发工具:
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、初始化的实体模型
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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是一样的
![](https://i-blog.csdnimg.cn/blog_migrate/f97ba88f7983ff185858633de5101353.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cd2838f581da61aa19b9078ef961775c.png)
4、文章包袱:Migration过程中遇到的问题
在ApplicationDbContext重写的方法OnModelCreating
中,有一行代码
base.OnModelCreating(builder)
当我把这行代码注释掉时,迁移过程中就会报出如下错误:
很明显,错误发生的原因是IdentityUser没有被映射到ApplicationDbContext,所以可以知道这句代码的作用,就是用来映射Identity的几个实体类的,没有这句,数据库中就不会自动生成Users、Roles……等Table了,我还没有去github看这个方法的源码,但我觉得实际上就是第一次Add-Migration时,生成的源文件ApplicationDbContextModelSnapshot.cs 中的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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的实体中,有这样一种关系:
但是神奇的事情发生了(我不想这样的):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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块的彩票一样。
希望有人看完这篇文章吧,新写手最需要的就是多给建议呀!谢谢