程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> EF CodeFirs 代碼遷移、數據遷移,efcodefirs

EF CodeFirs 代碼遷移、數據遷移,efcodefirs

編輯:關於.NET

EF CodeFirs 代碼遷移、數據遷移,efcodefirs


   最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。

   十年河東十年河西,莫欺少年窮

   學無止境,精益求精

   標題叫EF CodeFirs 代碼遷移、數據遷移。

   那麼:到底叫代碼遷移還是數據遷移?我在網上看了大半天,怎麼叫的都有,後來查了MSDN,MSDN上叫代碼遷移。在此,我們也稱之為代碼遷移。

   為什麼有人將其稱為數據遷移呢?可能是因為本節內容和操作數據庫有關<增加一張表,刪除一張表,增加一個表字段,刪除一個表字段,修改一個表字段等>,所以網友稱之為數據遷移

   MSDN等權威結構為什麼稱之為代碼遷移呢?可能是因為本節的內容通過我們熟知的C#代碼就可以搞定,所以稱之為代碼遷移

   嘻嘻,上邊是我的猜測,人家可跳過

   正文:

   背景:我們為什麼要使用代碼遷移?

   我們使用CodeFirst初始化數據庫無非以下三種方式:

   一、 每次運行都會創建新的數據庫

   Database.SetInitializer<XXXXXContext>(new DropCreateDatabaseAlways<XXXXXContext>());

   二、只有第一次運行~才會創建新的數據庫~默認的方式

   Database.SetInitializer<XXXXXContext>(new CreateDatabaseIfNotExists<XXXXXContext>());

   三、 修改模型後~運行~會創建新的數據庫

   Database.SetInitializer<XXXXXContext>(new DropCreateDatabaseIfModelChanges<XXXXXContext>());

   不論我們使用哪種方式,當條件成立時,都會創建新的數據庫,而創建新的數據庫就意味著‘從頭再來’

   舉個例子:如果您的團隊開發的項目已經被客戶使用,客戶在實際的生產操作中已經創建了很多數據,如果,某一天客戶更改需求,我們難不成要采用上述方式將數據庫重建?一旦重建,客戶之前的數據就會丟失,這是任何一個客戶不能容忍的

   為了解決這個問題,我們要引入代碼遷移

   在進行代碼遷移之前,我們必須做好數據庫備份工作,確保客戶數據的安全性及完整性

   現在我們引入數據遷移示例:

   我們有以下模型類:

   Student模型類

    public class Student
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [StringLength(10)]
        public string Name { get; set; }//姓名
        [StringLength(2)]
        public string Sex { get; set; }//性別
        [Unique(ErrorMessage="學號不允許重復")]
        [StringLength(18)]
        public string StudentNum { get; set; }//學號

        [StringLength(100)]
        public string StudentAddress { get; set; }//地址
    }

   Course模型類

    public class Course
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [StringLength(20)]
        public string Name { get; set; }//課程名稱
}

   Score模型類

        [Key]
        public int Id { get; set; }

        public int StudentScore { get; set; }//學生分數

        public int StudentID { get; set; }//學生ID

        public int CourseID { get; set; }//課程ID

        public virtual Student Student { get; set; }//virtual關鍵字修飾,用於延遲加載 提高性能 只有顯式調用時 才會加載 並可以代表一個Student對象 也就是 屬性==對象

        public virtual Course Course { get; set; }//virtual關鍵字修飾,用於延遲加載 提高性能 只有顯式調用時  才會加載 並可以代表一個Course對象 也就是 屬性==對象

   StudentContext上下文

    public class StudentContext : DbContext
    {
        public StudentContext()
            : base("StudentContext")//指定連接字符串
        {
           
        }
        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<Score> Scores { get; set; }
        /// <summary>
        /// OnModelCreating方法中的modelBuilder.Conventions.Remove語句禁止表名稱正在多元化。如果你不這樣做,所生成的表將命名為Students、Courses和Enrollments。相反,表名稱將是Student、Course和Enrollment。開發商不同意關於表名稱應該多數。本教程使用的是單數形式,但重要的一點是,您可以選擇哪個你更喜歡通過包括或省略這行代碼的形式。
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

   現在客戶提出如下需求:在確保數據完整性的情況下,添加一張學校表<字段:學校名稱、學校地址,學校熱線>及增加學生聯系方式字段

   首先,我們添加學校模型類:

    public class School//新建一張學校表-----------------------------新建的表
    {
        [Key]
        public int schoolId { get; set; }
        [Required]
        [StringLength(10)]
        public string schoolName { get; set; }//學校名稱

        [StringLength(100)]
        public string StudentAddress { get; set; }//學校地址

        [StringLength(20)]
        public string StudentTel { get; set; }//學校熱線
    }

   修改學生模型類如下:

    public class Student
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [StringLength(10)]
        public string Name { get; set; }//姓名
        [StringLength(2)]
        public string Sex { get; set; }//性別
        [Unique(ErrorMessage="學號不允許重復")]
        [StringLength(18)]
        public string StudentNum { get; set; }//學號

        [StringLength(100)]
        public string StudentAddress { get; set; }//地址

        [StringLength(11)]
        public string StudentTel { get; set; }//聯系電話----------------------------新加的字段
    }

  上下文類加上DBset<School>如下:

    public class StudentContext : DbContext
    {
        public StudentContext()
            : base("StudentContext")//指定連接字符串
        {
           
        }
        public DbSet<Student> Students { get; set; }
        public DbSet<Course> Courses { get; set; }
        public DbSet<Score> Scores { get; set; }
        public DbSet<School> Schools { get; set; }//新加一張表
        /// <summary>
        /// OnModelCreating方法中的modelBuilder.Conventions.Remove語句禁止表名稱正在多元化。如果你不這樣做,所生成的表將命名為Students、Courses和Enrollments。相反,表名稱將是Student、Course和Enrollment。開發商不同意關於表名稱應該多數。本教程使用的是單數形式,但重要的一點是,您可以選擇哪個你更喜歡通過包括或省略這行代碼的形式。
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

 

 

  下面,我們進行代碼遷移<在進行代碼遷移之前,您必須重新生成您的項目>:

   首先,打開程序包管理器控制台

   PM>提示符下輸入以下命令︰

 enable-migrations

 add-migration InitialCreate

   解釋下這兩個命令的含義:enable-migrations 是指啟動遷移,此命令輸入後,系統會檢查上下文目標  add-migration InitialCreate 創建名稱為:時間戳_InitialCreate.CS的文件,其中時間戳是根據當前時間點生成的。

   我們得到如下信息

  

   至此,我們在項目中會發現多出一個名為:Migrations的文件夾,文件夾內有兩個文件:201612080727320_InitialCreate.cs 和 Configuration.cs 

   下面介紹下這兩個文件的作用:

   Configuration.cs 中有個Seed方法,您每次代碼遷移執行時,都會執行seed()方法,您可以通過Seed方法增加一些必要的數據。例如,上述我們增加了一張學校模型類,在代碼遷移時,我們希望插入一條學校數據,我們可以這樣修改seed()方法:

   下面是我修改後的Seed方法:

namespace EF_Test.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;
    using EF_Test.DAL;

    internal sealed class Configuration : DbMigrationsConfiguration<EF_Test.DAL.StudentContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(EF_Test.DAL.StudentContext context)
        {
            context.Schools.AddOrUpdate(
                 p => p.schoolId,
                 new School { schoolName = "河南城建學院", StudentAddress = "平頂山市新華區" , StudentTel="0375-88888888" }
               );
            context.SaveChanges();
        }
    }
}

   201612080727320_InitialCreate.cs 或者稱為:時間戳_InitialCreate.cs 是干嘛用的呢?我們來看看生成的代碼,如下:

namespace EF_Test.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class InitialCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.Course",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Name = c.String(nullable: false, maxLength: 20),
                    })
                .PrimaryKey(t => t.Id);
            
            CreateTable(
                "dbo.Score",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        StudentScore = c.Int(nullable: false),
                        StudentID = c.Int(nullable: false),
                        CourseID = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
                .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
                .Index(t => t.StudentID)
                .Index(t => t.CourseID);
            
            CreateTable(
                "dbo.Student",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Name = c.String(nullable: false, maxLength: 10),
                        Sex = c.String(maxLength: 2),
                        StudentNum = c.String(maxLength: 18),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 11),
                    })
                .PrimaryKey(t => t.Id);
            
        }
        
        public override void Down()
        {
            DropForeignKey("dbo.Score", "StudentID", "dbo.Student");
            DropForeignKey("dbo.Score", "CourseID", "dbo.Course");
            DropIndex("dbo.Score", new[] { "CourseID" });
            DropIndex("dbo.Score", new[] { "StudentID" });
            DropTable("dbo.Student");
            DropTable("dbo.Score");
            DropTable("dbo.Course");
        }
    }
}

   有兩個方法,Up()和Down() 都是些創建表,刪除主鍵,刪除表的一些動作。我們先不管這些,繼續我們的代碼遷移:

   PM>提示符下輸入以下命令︰

   Update-Database  或者  Update-Database -Verbose   前者僅僅是更新數據庫,不會在程序包管理器控制台中輸出執行的SQL語句,而後者則可以輸出執行的SQL語句,我們姑且使用後者吧

   執行失敗了,原因也很明確,是因為我們現有的數據庫中已經存在Course等表,再次創建會發生異常,怎麼辦?

   我們翻開 201612080727320_InitialCreate.cs 這個文件,將此文件作如下修改:

        public override void Up()
        {
          
            CreateTable(
                "dbo.School",
                c => new
                    {
                        schoolId = c.Int(nullable: false, identity: true),
                        schoolName = c.String(nullable: false, maxLength: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .PrimaryKey(t => t.schoolId);
            
         
        }
        
        public override void Down()
        {
            DropTable("dbo.School");

        }

   項目生成成功後,再次執行:Update-Database -Verbose

   

   萬事大吉,執行成功,而且執行了Seed方法,那麼我們的數據庫中應該有一張名為 School 的表,並且裡面有一條數據,如下:

   學校模型創建成功了,但是,學生表中添加的字段加上去了麼?

   根據上圖,學生表結構並沒有加上StudentTel一列,為何?我們該怎麼辦?

   在此,我們執行:add-migration 命令   add-migration 命令和 add-migration InitialCreate 命令的不同是一個指定了後綴名稱,一個沒有指定,需要我們自定義!

   根據上圖,我們創建了一個後綴為:SshoolCreate的文件,如下:

  雙擊文件,我們發現裡面沒什麼內容,我們添加如下代碼:

    public partial class SshoolCreate : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: 11));
        }

        public override void Down()
        {
            DropColumn("dbo.Student", "StudentTel");
        }
    }

   繼續執行:update-database 命令

   

   執行成功,我們到數據庫看看

   

   字段StudentTel已經加入了表Student

   至此,EF 代碼遷移也就講完了,上文比較亂,最後做個總結:

  1、 數據庫中有張名為:__MigrationHistory的表,主要用於代碼遷移記錄

   每當你成功遷移一次,都會生成一條記錄。

   2、上文中提到:時間戳_InitialCreate.CS 文件,如果在項目中有多個同類文件,項目會執行距離當前時間最近的 時間戳_InitialCreate.CS,如果距離當前時間最近的 時間戳_InitialCreate.CS 在表__MigrationHistory中有記錄,則不會執行!

   3、其實代碼遷移用到的命令也就那幾個,在此總結下:

   enable-migrations : 啟動代碼遷移並生成Configuration.CS文件

   add-migration : 啟動生成 時間戳_Name.CS文件,類似於:201612080802330_Name.CS這種文件,需要指定Name,用於多次遷移

   add-migration InitialCreate  : 啟動生成類似於:201612080802330_InitialCreate.CS這種文件

   Update-Database -Verbose  和  Update-Database 執行代碼遷移,修改數據庫表結構。 前者會在執行過程中生成相應的SQL語句

   4、常用的C#代碼:

   創建一張表:

    public partial class InitialCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.School",
                c => new
                    {
                        schoolId = c.Int(nullable: false, identity: true),
                        schoolName = c.String(nullable: false, maxLength: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .PrimaryKey(t => t.schoolId);
        }
        
        public override void Down()
        {
            DropTable("dbo.School");

        }

   刪除一張表:

    public partial class DropTable : DbMigration
    {
        public override void Up()
        {
            DropTable("dbo.School");
        }
        
        public override void Down()
        {
            CreateTable(
                "dbo.School",
                c => new
                    {
                        schoolId = c.Int(nullable: false, identity: true),
                        schoolName = c.String(nullable: false, maxLength: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .PrimaryKey(t => t.schoolId);
            
        }
    }

   修改字段長度或字段名稱

    public partial class EEE : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Student", "StudentPhone", c => c.String(maxLength: 11));
            DropColumn("dbo.Student", "StudentTel");
        }
        
        public override void Down()
        {
            AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: 11));
            DropColumn("dbo.Student", "StudentPhone");
        }
    }

  增加表字段:

    public partial class FFF : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: 11));
        }
        
        public override void Down()
        {
            DropColumn("dbo.Student", "StudentPhowwwwne");
        }
    }

   刪除表字段:

    public partial class GGG : DbMigration
    {
        public override void Up()
        {
            DropColumn("dbo.Student", "StudentPhowwwwne");
        }
        
        public override void Down()
        {
            AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: 11));
        }
    }

   等等吧,上述代碼都能生成。

   5、如果您生成了一個新的 時間戳_XXXX.CS文件,請務必使用Update-DataBase命令執行,否則,您將永遠不會生成下一個 時間戳_XXXX.CS文件,究其原因,是因為在數據表[__MigrationHistory]中沒有記錄。

   最新的[__MigrationHistory]數據

   

   根據上述數據,就證明我做了五次代碼遷移了!

   好了,本節的內容也就講完了,謝謝大家的耐心查閱!

   @陳臥龍的博客

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved