初识Entity Framework CodeFirst(2)
上一回合,我们讨论了如何简单的使用Entity Framework CodeFirst功能。
结尾的时候,我们提出了一个有趣的问题,如果我们的数据实体需要发生变化呢?需要添加多一个Model类呢?修改已有实体中字段呢?我们该怎么办?该不会是把数据库删掉,然后让程序重新生成吧?很明显,答案当然不是啦。EF作为微软推荐的框架之一,没有这么差劲的。
本节,我们讨论一下内容:
1、Migration控制台
2、修改已有实体,添加/删除 数据库字段
3、添加新实体模型与数据库表映射
4、修改实体属性与数据库字段名映射
代码点击这里下载
1、打开程序包管理器控制台,并启动Migrations (迁移)
方法如下:点击工具->库程序包管理器->程序包管理器控制台
在控制台中输入“Enable-Migrations” (小技巧:这里支持Tab自动补全),启动Migrations。
这时候,项目解决方案处会多出一个文件夹“Migrations”以及其下级的文件
“Configuration.cs”文件包含本项目CodeFirst的基本配置,一般情况下我们大可不必修改里面的内容。
public partial class InitialCreate : DbMigration { public override void Up() { CreateTable( "dbo.Blogs", c => new { BlogID = c.Int(nullable: false, identity: true), BlogName = c.String(), }) .PrimaryKey(t => t.BlogID); CreateTable( "dbo.Posts", c => new { PostID = c.Int(nullable: false, identity: true), Title = c.String(), Content = c.String(), BlogID = c.Int(nullable: false), }) .PrimaryKey(t => t.PostID) .ForeignKey("dbo.Blogs", t => t.BlogID, cascadeDelete: true) .Index(t => t.BlogID); } public override void Down() { DropIndex("dbo.Posts", new[] { "BlogID" }); DropForeignKey("dbo.Posts", "BlogID", "dbo.Blogs"); DropTable("dbo.Posts"); DropTable("dbo.Blogs"); } }
“201303241026370_InitialCreate.cs”(命名有系统自动生成)包含的则是初始化语句。大家可以看到里面包含了两个方法,一个是Up,用于初始化时建立数据库(表);另外一个则是Down,删除数据库(表)的。
我们必须借助Migrations 来对实体类中的修改更新到数据库的结构当中。
2、如何修改已有实体(实例:在已有model中添加一个字段)
举个例子(还是来自于MSDN),我们对Blog Model进行修改,添加一个“Url”的字段
class Blog { public int BlogID { get; set; } public string BlogName { get; set; } public string Url { get; set; } }
打开Migration控制台,输入“Add-Migration AddUrl”,这时我们发现解决方案中多了一个文件,名为:201303241155123_AddUrl
该文件产生的代码如下:
public partial class AddUrl : DbMigration { public override void Up() { AddColumn("dbo.Blogs", "Url", c => c.String()); } public override void Down() { DropColumn("dbo.Blogs", "Url"); } }
再在Migration控制台输入:“Update-Database”,更新数据库结构。
新增的字段就乖乖的新建出来了。
这里解析一下:
(1)、Migration控制台中,“Add-Migration XXX”,其中的XXX为各位读者自己命名,可以起任意的名字,这里还是建议各位读者朋友名一个有实意的名字以方便后续的工作。
(2)、生成的新代码中,它继承自DbMigration,里面包含了两个重写方法,UP和Down,Up中调用了AddColumn方法插入新字段,传入三个参数,分别是“表名”、“字段名”以及一个用于确定数据类型的lambda表达式;Drow方法则调用DropColumn,传入“表名”和“字段名”来删除该字段。有兴趣的同学可以对DbMigration进行反编译,可以看到里面包含的所有方法。
3、新增一个实体(实例:添加一个Type Model和一个User Model)
3.1、添加一个Type实体
class Type { public int TypeID { get; set; } public string TypeName { get; set; } }
修改上下文BlogContext,添加一个Types属性
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } }
调出Migration控制台,“Add-Migration AddType”——>“Update-Database” ,很好,Type表已经轻松的生成。
3.2、添加一个User Model
各位读者有没有发现,之前我们一直新建的实体,实体字段第一位都是XXID(总是int类型),这里我们做一点改动,不要XXID,只要UserName和DisplayName两个string类型的字段。
添加Model
class User { public string UserName { get; set; } public string DisplayName { get; set; } }
修改上下文
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } public DbSet<User> Users { get; set; } }
我们还像以往一样,调出Migration控制台,然后执行“Add-Migration AddUser”——>“Update-Database”
聪明的读者一定会猜得出后果会怎么样,没错,这回报错了,报错内容为:没有找到主键
这时,我们需要在User中手动添加一个主键的标签
class User { [System.ComponentModel.DataAnnotations.Key] public string UserName { get; set; } public string DisplayName { get; set; } }
重新调出Migration控制台,“Add-Migration AddUser”——>“Update-Database”
通过,User表已经生成在数据库中。
这时,我们或许会产生或多或少的暗示——不指定主键的时候,EF默认会自动的帮我们指定。
这里,我把Type实体和User实体通过调用Migration生成的两个Add文件的代码贴出来。
public partial class AddType : DbMigration { public override void Up() { CreateTable( "dbo.Types", c => new { TypeID = c.Int(nullable: false, identity: true), TypeName = c.String(), }) .PrimaryKey(t => t.TypeID); } public override void Down() { DropTable("dbo.Types"); } }
public partial class AddUser : DbMigration { public override void Up() { CreateTable( "dbo.Users", c => new { UserName = c.String(nullable: false, maxLength: 128), DisplayName = c.String(), }) .PrimaryKey(t => t.UserName); } public override void Down() { DropTable("dbo.Users"); } }
对比一下,答案也许各位读者已经找到了。没错,答案就在重写的UP中,各位是否发现,没有指明主键的Type实体中,EF会自动的把第一个字段指定为identity(自增)的int型主键,当然,前提必须为这个被指定的字段在model中的属性为int类型。而在User实体中,全部都是string类型的属性,因为EF无法通过默认的方式找到可以自动添加的主键字段,所以Migrants控制台想Add-Migrants时就会发生内容为:找不到主键的报错啦。
这里我需要补充一下,精明的读者也许发现,通过CodeFirst生成的数据表中,字段的类型也只能确定个大概,并不能进行比较精确的确定,比如通过string类型属性反向生成的字段,其字段容量大小竟然为“Max”;其实,在“Update-Database”之前,我们可以通过对“Add-Migration”中生成的Add文件(暂时想不出有什么方法形容这些文件)中的UP方法进行细小的微调,然后再执行“Update-Database”,我们就可以生成真正想要字段大小啦。
3、修改已存在的实体,数据库中对应字段的字段名
有时候,我们已经使用EF CodeFirst对数据库已经进行了反向的生成,但是,我们又想修改一下数据库中,与实体属性对应的某个字段的字段名,这个时候我们应该怎么办呢?
这里,我们只需要在上下文中override一个方法
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } public DbSet<User> Users { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<User>() .Property(u => u.DisplayName) .HasColumnName("Display_Name"); } }
在Migration中,“Add-Migration”——>“Update-Migration”
好的,O了。
这节我们已经讨论完毕了,各位读者如果有什么更好的建议或者意见,欢迎留言。