EF CodeFirs 代碼遷移、數據遷移

来源:http://www.cnblogs.com/chenwolong/archive/2016/12/08/migrations.html
-Advertisement-
Play Games

最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。 十年河東十年河西,莫欺少年窮 學無止境,精益求精 標題叫EF CodeFirs 代碼遷移、數據遷移。 那麼:到底叫代碼遷移還是數據遷移?我在網上看了大半天,怎麼叫的都有,後來查了MSDN,MSDN上叫代碼遷 ...


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

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

   學無止境,精益求精

   標題叫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]數據

   

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

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

   @陳卧龍的博客


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 記憶體管理單元MMU(memory management unit)的主要功能是虛擬地址(virtual memory addresses)到物理地址(physical addresses)的轉換。除此之外,它還可以實現記憶體保護(memory protection)、緩存控制(cache contro... ...
  • 上次創建了欄目模型,這次主要做欄目的前臺顯示。涉及到數據存儲層、業務邏輯層和Web層。用到了遷移,更新資料庫和註入的一些內容。 一、添加數據存儲層 1、添加Ninesky.DataLibrary(與上次添加方法相同) 在解決方案(Ninesky)上點右鍵->添加->新建項目 選擇.NET Core ... ...
  • 本篇博文介紹了#define條件編譯的用途、用法,並結合具體實例進行說明;本文還說明瞭使用條件編譯時需要註意的事項,以及環境變數(或條件編譯符號)的設置方法。 ...
  • 返回目錄 題目有點意思,大家都知道Dictionary<K,V>不是線程安全的類型,而List<T>是線程安全的嗎?在今天之前大叔沒有去測試過,而就在今天也是一個VIP問我,說在我的代碼中使用了並行,然後為一個List賦值,說的直接一點就是:List元素是全局的,在各個線程里分別去操作它,測試數據是 ...
  • 在項目的web.config文件中添加 <connectionStrings> <add name="SQLConnectionString" connectionString="資料庫連接字元串"/> </connectionStrings> 頁面上使用需要添加命名空間 using System. ...
  • 1.許可權控制使用controller和 action來實現,許可權方式有很多種,最近開發項目使用控制控制器方式實現代碼如下 二.單點登錄方式使用application方式來實現 1.用戶登錄成功後記錄當前信息 2.使用ActionFilter來實現單點登錄,每次點擊控制器都去查詢過濾是否在其它地方登錄 ...
  • 什麼是工作隊列 工作隊列是為了避免等待一些占用大量資源或時間操作的一種處理方式。我們把任務封裝為消息發送到隊列中,消費者在後臺不停的取出任務並且執行。當運行了多個消費者工作進程時,隊列中的任務將會在每個消費者間進行共用。 使用工作隊列的好處就是能夠並行的處理任務。如果隊列中堆積了很多任務,只要添加更... ...
  • 之前的代碼 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...