我們知道無論是“Database First”還是“Model First”當模型發生改變了都可以通過Visual Studio設計視圖進行更新,那么對於Code First如何更新已有的模型呢?今天我們簡單介紹一下Entity Framework的數據遷移功能。
Entity Framework配置
在開始今天的話題之前先來看一下Entity Framework的配置,因為有很多朋友因為配置文件的問題造成“Migrations”命令執行失敗。
在建立一個應用程序之后我們可以通過在項目上右鍵“Nuget Packages Manage”進行EF包安裝或者直接在“Package Manager Console”中輸入“Install-Package EntityFramework”命令進行Entity Framework包安裝。這個過程不僅下載並引入了EF框架相關程序集還進行了包的配置和應用用程序配置。下面是這兩個配置文件:
packages.config文件:
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="EntityFramework" version="5.0.0" targetFramework="net45" /> </packages>
App.config文件:
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> </entityFramework> </configuration>
packages.config內容比較簡單,首先是EF自身版本,然后在安裝過程中根據當前應用的.NET Framework版本配置了“targetFramework”,因為不同的.NET Framework版本對應的EF程序集不同,這在安裝過程中會自動識別並配置。
App.config中自動添加了“entityFramework”配置節,在EF包安裝過程中自動根據當前環境配置了“defaultConnectionFactory”, “defaultConnectionFactory”是EF默認的連接配置,只有在沒有配置連接字符串時生效。在上一篇文章中我們提到如果不進行連接字符串配置EF會自動識別並創建數據庫到“.\SQLEXPRESS”或者“LocalDb”,事實上就是通過這里識別的,可以看出我機器上沒有“.\SQLEXPRESS”就自動使用了“LocalDb”,配置默認連接到“LocalDbConnectionFactory”。
在本例中我們需要將連接指向“.\SQL2008”上,有兩種方式建立自己的連接:一種是直接配置“defaultConnectionFactory”,將默認生成的“System.Data.Entity.Infrastructure.LocalDbConnectionFactory”改成“System.Data.Entity.Infrastructure.SqlConnectionFactory”,然后在參數中編寫我們的連接字符串(注意默認生成的數據庫名為“項目名稱.數據庫上下文名稱”)。
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"> <parameters> <parameter value="Data Source=.\SQL2008; UID=sa;PWD=123; MultipleActiveResultSets=True" /> </parameters> </defaultConnectionFactory> </entityFramework> </configuration>
另一種是通過傳統的 “connectionStrings” 配置,只要配置了連接字符串“defaultConnectionFactory”將不再生效,EF會自動使用該連接。在接下來的示例中我們將采用這種方式。
<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <add name="CodeFirstDb" connectionString="Data Source=.\SQL2008;Database=CodeFirstDb;UID=sa;PWD=123;MultipleActiveResultSets=true" providerName="System.Data.SqlClient"></add> </connectionStrings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> </entityFramework> </configuration>
Code First數據庫遷移
現在讓我們在上一篇文章Entity Framework 5.0系列之EF概覽中的“Code First”示例的基礎上給Order添加一個”Employee”屬性,然后運行,不出意外的話你將看到如下異常:

從異常信息我們可以看出,EF已經檢測到模型發生了改變,建議我們使用”Code First Migrations”對模型進行更新。事實上EF就是依靠上一篇文章中提到的“dbo.__MigrationHistory”表對我們的模型進行判斷的,因為這張表中存儲了我們的模型結構(在Model列中),如果運行程序時你跟蹤SQL Server會發現EF執行了如下SQL進行模型修改判斷:
SELECT Count(*) FROM sys.databases WHERE [name]=N'CodeFirstDb' go SELECT TABLE_SCHEMA SchemaName, TABLE_NAME Name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' go SELECT Count(*) FROM sys.databases WHERE [name]=N'CodeFirstDb' go SELECT [GroupBy1].[A1] AS [C1] FROM ( SELECT COUNT(1) AS [A1] FROM [dbo].[__MigrationHistory] AS [Extent1] ) AS [GroupBy1] go SELECT TOP (1) [Project1].[C1] AS [C1], [Project1].[MigrationId] AS [MigrationId], [Project1].[Model] AS [Model] FROM ( SELECT [Extent1].[MigrationId] AS [MigrationId], [Extent1].[Model] AS [Model], 1 AS [C1] FROM [dbo].[__MigrationHistory] AS [Extent1] ) AS [Project1] ORDER BY [Project1].[MigrationId] DESC go
在開始Code First數據庫遷移之前,我們先對上一節編寫的OrderContext類進行修改添加默認構造函數,因為Code First Migrations將會使用數據庫上下文的默認構造函數進行數據遷移操作(盡管沒有默認構造函數所有的數據操作都能正常進行,但是對於數據遷移這是必須的),因此我們需要添加一個默認構造函數,並且該構造函數中必須傳入我們的數據庫連接名稱(如果使用上面我們說的第一種數據庫連接配置方式則可以不用傳遞該參數),否則將會把更新應用到EF默認數據庫上。下面是我們的OrderContext:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; namespace CodeFirst { public class OrderContext:DbContext { public OrderContext() : base("CodeFirstDb") { } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } }
下面我們將借助於”Code First Magrations” 進行模型更新。
在Visual Studio中執行:Tools->Library Package Manager->Package Manager Console來打開包管理命令行:

在命令行中輸入“Enable-Migrations -StartUpProjectName CodeFirst”然后回車(“CodeFirst”是你的項目名稱,如果在“Package Manager Console”中選擇了默認項目可以不設置“-StartUpProjectName”參數;如果多次執行此命令可以添加-Force參數):

![]() |
注意:如果在運行“Enable-Migrations -StartUpProjectName CodeFirst”命令過程中收到如下錯誤,請嘗試下面給出的方法: Enable-Migrations : The term 'Enable-Migrations' is not recognized as the name of a cmdlet, function, script file, or operable 1. 在Package Manager Console中執行命令:Import-Module F:\[7]CSharp\EntityFramework\CodeFirst\packages\EntityFramework.5.0.0\tools\EntityFramework.PS3.psd1(后面的路徑按照你EF包的實際位置修改,這個問題是由於沒有導入相應命令造成的) 2. 執行“Install-Package EntityFramework -IncludePrerelease”命令重新安裝最新版EF(也可以通過-Version參數指定具體版本)。 3. 更新Nuget,具體更新步驟見Installing NuGet。 4. 另外,命令執行過程中其他錯誤可以按照錯誤提示解決。 |
如果沒有錯誤你的項目中將自動生成一個名為”Migrations“的文件夾,里面包含兩個文件: Configuration.cs和201308211510117_InitialCreate.cs(201308211510117是時間戳)。
Configuration.cs:是遷移配置代碼,一般我們不需要修改。
namespace CodeFirst.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<CodeFirst.OrderContext> { public Configuration() { AutomaticMigrationsEnabled = true; } protected override void Seed(CodeFirst.OrderContext context) { // This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method // to avoid creating duplicate seed data. E.g. // // context.People.AddOrUpdate( // p => p.FullName, // new Person { FullName = "Andrew Peters" }, // new Person { FullName = "Brice Lambson" }, // new Person { FullName = "Rowan Miller" } // ); // } } }
201308211510117_InitialCreate.cs:以代碼的形式記錄了本地數據庫的表結構定義。
namespace CodeFirst.Migrations { using System; using System.Data.Entity.Migrations; public partial class InitialCreate : DbMigration { public override void Up() { CreateTable( "dbo.Orders", c => new { Id = c.Int(nullable: false, identity: true), Customer = c.String(), OrderDate = c.DateTime(nullable: false), }) .PrimaryKey(t => t.Id); CreateTable( "dbo.OrderDetails", c => new { Id = c.Int(nullable: false, identity: true), Product = c.String(), UnitPrice = c.String(), OrderId = c.Int(nullable: false), }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Orders", t => t.OrderId, cascadeDelete: true) .Index(t => t.OrderId); } public override void Down() { DropIndex("dbo.OrderDetails", new[] { "OrderId" }); DropForeignKey("dbo.OrderDetails", "OrderId", "dbo.Orders"); DropTable("dbo.OrderDetails"); DropTable("dbo.Orders"); } } }
現在在” Package Manager Console”中執行“Add-Migration AddEmployee”命令,該命令會自動比較模型和當前數據庫中的表結構,然后執行“AddEmployee”添加一個”Employee”列,此時在”Migrations“文件夾會生成一個名為“201308240757094_AddEmployee.cs”的類(201308240757094是時間戳):

201308240757094_AddEmployee.cs內容如下:
namespace CodeFirst.Migrations { using System; using System.Data.Entity.Migrations; public partial class AddEmployee : DbMigration { public override void Up() { AddColumn("dbo.Orders", "Employee", c => c.String()); } public override void Down() { DropColumn("dbo.Orders", "Employee"); } } }
從這個類中我們可以看出事實上是給“Order”添加了“Employee”列,當然我們也可以手動修改這個類。
接下來,在“Package Manager Console”中執行“Update-Database -StartUpProjectName CodeFirst –Verbose”命令(添加-Verbose參數可以查看到更新腳本),進行數據庫結構更新:

此時查看數據庫,發現“Order”表已經多了一列:

OK,今天我們的內容就先到此,關於如何根據數據庫生成代碼類及其更多內容我們在后面的文章中再一起探討。

