2014/9/2 技术探讨

上一回合,我们讨论了如何简单的使用Entity Framework CodeFirst功能。

结尾的时候,我们提出了一个有趣的问题,如果我们的数据实体需要发生变化呢?需要添加多一个Model类呢?修改已有实体中字段呢?我们该怎么办?该不会是把数据库删掉,然后让程序重新生成吧?很明显,答案当然不是啦。EF作为微软推荐的框架之一,没有这么差劲的。

本节,我们讨论一下内容:

1、Migration控制台

2、修改已有实体,添加/删除 数据库字段

3、添加新实体模型与数据库表映射

4、修改实体属性与数据库字段名映射

代码点击这里下载


1、打开程序包管理器控制台,并启动Migrations (迁移)

方法如下:点击工具->库程序包管理器->程序包管理器控制台

在控制台中输入“Enable-Migrations” (小技巧:这里支持Tab自动补全),启动Migrations。

这时候,项目解决方案处会多出一个文件夹“Migrations”以及其下级的文件

“Configuration.cs”文件包含本项目CodeFirst的基本配置,一般情况下我们大可不必修改里面的内容。

201303241026370_InitialCreate
复制代码
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");
        }
    }
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

“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

该文件产生的代码如下:

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");
        }
    }
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

再在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文件的代码贴出来。

AddType
复制代码
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");
        }
    }
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

 

AddUser
复制代码
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");
        }
    }
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

对比一下,答案也许各位读者已经找到了。没错,答案就在重写的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一个方法

 

BlogContext
复制代码
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");
        }
    }
复制代码
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

在Migration中,“Add-Migration”——>“Update-Migration”

好的,O了。


这节我们已经讨论完毕了,各位读者如果有什么更好的建议或者意见,欢迎留言。

 

  [ Entity FrameWork ]   [ Code First ]
知识共享许可协议 本作品由小蝶惊鸿创作,采用知识共享署名 4.0 国际许可协议进行许可,转载时请保留本文署名及链接。