前言
紧接着前面一篇博文Entity Framework CodeFirst尝试。
我们知道无论是“Database First”还是“Model First”当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Code First如何更新已有的模型呢?今天我们简单介绍一下Entity Framework的数据迁移功能。
Entity Framework配置
当我们对项目进行Entity Framework进行安装引用的时候,同时生成了两个配置文件
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默认的连接配置,只有在没有配置连接字符串时生效。
配置了数据库链接字符串的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> <connectionStrings> <add name= "CodeFirstTest" connectionString= "Data Source=.;Database=CodeFirstTest;UID=sa;PWD=sa123;" 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> |
CodeFirst 数据迁移
现在让我们在上一篇文章的Entity Framework CodeFirst尝试 的基础上给Order添加一个"Employee”属性,然后运行,不出意外的话你将看到如下异常:
![](https://i-blog.csdnimg.cn/blog_migrate/71870874e0840caf7fc5e331b6f46ab1.png)
从异常信息我们可以看出,EF已经检测到模型发生了改变,建议我们使用”Code First Migrations”对模型进行更新。
在开始Code First数据库迁移之前,我们先对上一节编写的OrderTestContext类进行修改添加默认构造函数,因为Code First Migrations将会使用数据库上下文的默认构造函数进行数据迁移操作(尽管没有默认构造函数所有的数据操作都能正常进行,但是对于数据迁移这是必须的),因此我们需要添加一个默认构造函数,并且该构造函数中必须传入我们的数据库连接名称,否则将会把更新应用到EF默认数据库上。下面是我们的OrderTestContext:
public class OrderTestContext:DbContext { public OrderTestContext(): base ( "CodeFirstTest" ) { } public OrderTestContext( string connectionName) : base (connectionName) { } public DbSet<Order> Orders { get ; set ;} public DbSet<OrderDetail> OrderDetails { get ; set ; } } |
下面我们将借助于”Code First Magrations” 进行模型更新。然后找到如下图所示的位置
![](https://i-blog.csdnimg.cn/blog_migrate/5f4ae8c9efd4e9d1d8e5e46990ee8b1e.png)
1.在“程序包管理器控制台”键入命令:Enable-Migrations -ProjectName CodeFirstTest
如果多次执行此命令可以添加-Force参数
![](https://i-blog.csdnimg.cn/blog_migrate/8097948e1660a7b6e5a76aabdf713af8.png)
添加后,项目中添加了一个名为Migrations的文件夹
查看Configuration文件中的代码为:
namespace CodeFirstTest.Migrations { using System; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; internal sealed class Configuration : DbMigrationsConfiguration<CodeFirstTest.OrderTestContext> { public Configuration() { AutomaticMigrationsEnabled = false ; } protected override void Seed(CodeFirstTest.OrderTestContext 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" } // ); // } } } |
方法Seed中可以进行数据迁移后的数据初始化工作,将在每次迁移之后运行。如上代码所示,AddOrUpdate是IDbSet<TEntity>的扩展方法,如果指定条件的数据不存在,则会添加,如果存在,会更新。所以,如果数据是通过此方法来初始化的,在与业务更新之后,再次进行数据迁移后,还是会被还原。
还有一个名为InitialCreate的类,配置生成数据库的细节:
namespace CodeFirstTest.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(), 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" ); } } } |
3.执行“Add-Migration AddEmployee”命令,添加一个名为AddEmployee的迁移
![](https://i-blog.csdnimg.cn/blog_migrate/825acbf697a8886ddce26638d4431d15.png)
4.执行“Update-Database”命令,更新数据库架构
![](https://i-blog.csdnimg.cn/blog_migrate/45850e7bed0b1e2b5022d16e1ed2d94a.png)
如果更新数据库存在冲突而不能执行更新,可以添加 -Force强制执行,例如:“Update-Database -Force”
5.设置自动迁移
每次都通过控制台来进行迁移太过麻烦,可以设置为自动迁移。
1. AutomaticMigrationsEnabled:获取或设置 指示迁移数据库时是否可使用自动迁移的值。
2. AutomaticMigrationDataLossAllowed:获取或设置 指示是否可接受自动迁移期间的数据丢失的值。如果设置为false,则将在数据丢失可能作为自动迁移一部分出现时引发异常。
代码调用实例
using ( var db = new OrderTestContext( "CodeFirstTest" )) { Order Order = new Order(); Order.Customer = "aehyok" ; Order.OrderDate = DateTime.Now; db.Orders.Add(Order); db.SaveChanges(); IQueryable<Order> Orders = from Orderes in db.Orders select Orderes; foreach (Order O in Orders) { Console.WriteLine( "OrderID is {0},Customer is {1}" , O.ID, O.Customer); } } |
调用结果展示
![](https://i-blog.csdnimg.cn/blog_migrate/29b044cd7f24bccdac71d539ba1ad2b7.png)
有两条数据,一条是上一篇博文添加的数据,第二条就是今天测试添加的。