使用 Migration 的 Code First 方法在 MVC 中
如何使用 Migration 的 Code First 方法在 MVC 中
引言
在应用程序中实现 Entity Framework 有三种不同的方法
- 数据库优先 (Database First)
- 模型优先 (Model First)
- Code First
使用哪种方法不是本文的重点,但这里有一些可以帮助我们决定使用哪种方法的要点。
数据库优先:如果我们有现有的数据库,那么我们应该选择数据库优先方法。在此方法中,我们可以根据数据库生成模型。在此方法中,我们创建一个 .edmx 文件,所有信息都保存在其中。
模型优先:如果我们没有现有的数据库,那么我们可以通过此方法创建一个概念模型,然后稍后创建 SQL 脚本来创建数据库。
代码优先:如果我们没有现有的数据库,那么我们可以采用代码优先方法。在此方法中,Entity Framework 不会创建任何 .edmx 文件或隐藏代码。使用此方法,我们设计模型类,并借助这些模型类自动创建数据库。
在本文中,我们将看到如何使用代码优先实现代码优先方法和 CRUD 操作。我们将看到在代码优先的情况下如何使用迁移。本教程对想要从头开始学习代码优先方法的初学者非常有帮助。
代码优先方法
在代码优先方法中,我们不创建 .edmx 文件。我们创建 Model
类,并借助这些模型类自动创建数据库。
开始编码
1. 打开 Visual Studio,转到文件 --> 新建-->项目,然后创建一个新的 MVC 项目(我使用的是 MVC4 和 Visual Studio 2012),如下图所示,并为其命名。(在我的例子中,名称
是CodeFirstApproach
。)
2. 在Model文件夹中添加一个名为Student的模型,并应用System.ComponentModel.DataAnnotations
命名空间中的一些属性。
public class Student
{
[Key]
public int ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string Email { get; set; }
[StringLength(50)]
[DataType(DataType.Password)]
public string Password { get; set; }
}
代码优先的主要参与者(Context 类)
Context
类是代码优先的主要参与者。在 Model 文件夹中创建一个context
类并为其命名(在我的例子中,名称是MyDataContext.cs),并继承自DBContext
类,然后在MyDataContext
类中添加一个构造函数,调用基类构造函数,并将连接字符串传递给基类构造函数,如下所示。
注意:对于 DbContext 类,请使用 System.Data.Entity 命名空间。
public class MyDataContext : DbContext
{
public MyDataContext():base("Data Source=.;Initial Catalog=CodeFirst;
trusted_connection=true")
{
}
public DbSet<Student> Students { get; set; }
}
注意:我们可以从 web.config 文件中获取连接字符串的名称,而不是完整的连接字符串。在这种情况下,我们将仅传递连接字符串名称而不是完整的连接字符串。
添加控制器和操作方法
添加一个名为StudentController
的控制器,并添加一个名为AddStudent
的操作方法。
public class StudentController : Controller
{
[HttpGet]
public ActionResult AddStudent()
{
return View();
}
}
添加一个视图(右键单击视图,然后单击添加视图)。选择创建强类型视图,然后选择我们在Model文件夹中创建的Student类作为Model
类。在脚手架模板中选择创建。
运行应用程序,然后在 URL 中输入 Student/AddStudent,格式为 ControllerName/ActionName。
在StudentController
中创建另一个将处理 post 请求的操作方法。在此操作方法中,我们将创建我的上下文类的对象,并将 student
实体添加到 Students DbSet
中。
public class StudentController : Controller
{
[HttpGet]
public ActionResult AddStudent()
{
return View();
}
[HttpPost]
public ActionResult AddStudent(Student std)
{
using(MyDataContext objContext = new MyDataContext())
{
objContext.Students.Add(std);
objContext.SaveChanges();
}
return View();
}
}
填写学生表单并单击创建按钮。我们将看到一个新创建的数据库(CodeFirst
)和一个表(Student
),如下图所示
在上面的Table
结构中,我们可以看到Students
表是自动创建的。代码优先会自动将 ID 创建为主键,并在 ID 列上添加 Identity。Name
、Email
和Password
列的长度与我们在Model
类上的属性上指定的StringLength
相同。
通过StudentForm
添加更多记录后,我们的student
表中将包含以下数据
如果我们要在Model
中添加一个额外的列,比如address
,该怎么办?那么完整的模型是
public class Student
{
[Key]
public int ID { get; set; }
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
public string Email { get; set; }
[StringLength(50)]
[DataType(DataType.Password)]
public string Password { get; set; }
public string Address { get; set; }
}
如果我们运行应用程序并添加一个学生,我们将收到以下异常
现在我们有三个选项
DropCreateDatabaseIfModelChanges
:我们可以在MyDataContext
构造函数中设置它。但是,如果我们设置了它,那么每当我们的模型发生更改时,我们的数据库都将被删除并重新创建。因此,数据库中的所有数据都将丢失。DropCreateDatabaseAlways
:我们也可以在MyDataContext
构造函数中设置它。每当我们对datacontext
执行任何操作时,它都会删除并始终创建数据库。因此,数据库中的所有数据将再次丢失。
我们可以将这些属性设置如下
public MyDataContext():base("Data Source=.;Initial Catalog=CodeFirst;
trusted_connection=true")
{
Database.SetInitializer<MyDataContext>
(new DropCreateDatabaseIfModelChanges<MyDataContext>());
}
或
public MyDataContext():base("Data Source=.;Initial Catalog=CodeFirst;
trusted_connection=true")
{
Database.SetInitializer<MyDataContext>
(new DropCreateDatabaseAlways<MyDataContext>());
}
在以上两种选项中,我们都丢失了数据。那么,如果我们不想丢失数据并想在表中添加额外的列,该怎么办?如上一个异常中的一些附加信息所示,我们可以利用迁移方法来处理这种情况。
代码优先迁移方法
要使用代码优先迁移,我们首先需要启用项目中的迁移。
要启用迁移,请转到工具-->NuGet 包管理器,然后打开程序包管理器控制台。
逐个运行以下命令
- PM> Uninstall-Package EntityFramework
- PM> Install-Package EntityFramework
- PM> Enable-Migrations -ContextTypeName CodeFirstApproach.Models.MyDataContext
一旦我们运行了以上命令,我们的解决方案中就会添加一个migrations文件夹,其中包含一个配置类文件和一个带有日期时间戳的另一个文件。此文件包含表的初始状态。
现在使用
MigrationName
(在我的例子中是AddAddressProperty
)运行以下命令。 - PM> Add-Migration AddAddressProperty
上述命令将在Migrations文件夹中创建另一个文件,其中包含自上次迁移以来你在模型中所做的更改。
现在更新你的数据库。
- PM> Update-Database
现在检查
Students
表,将有一个名为address
的新列。
现在,每次我们在模型中添加一个列时,都需要执行上述步骤。哦,不……自动迁移随时可以帮助我们。
自动迁移
在此配置文件的构造函数中,将AutomaticMigrationsEnabled = true
设置为 true,并在MyDataContext Constructor
中设置以下初始化程序
public MyDataContext():base("Data Source=.;Initial Catalog=CodeFirst;trusted_connection=true")
{
Database.SetInitializer
(new MigrateDatabaseToLatestVersion<MyDataContext, Configuration>());
}
现在在模型中添加另一个属性,比如City
,然后再次运行应用程序。哇,这次它自动添加到了你的表中。
在代码优先中插入主数据(使用 SQL 查询)
假设我们想在模型中添加一个PhoneNo
属性,并为其提供一些默认值。在代码优先方法中,可以使用 SQL 查询轻松完成此操作。
在 PM Console 窗口中运行以下命令
PM> Add-Migration AddPhoneNoProperty
这将在 Migration 文件夹中创建一个文件。打开该文件并添加一个 SQL update
语句,如下所示
public partial class AddPhoneNoProperty : DbMigration
{
public override void Up()
{
AddColumn("dbo.Students", "PhoneNo", c => c.String());
Sql("update students set PhoneNo=9711965544 where PhoneNo is null");
}
public override void Down()
{
DropColumn("dbo.Students", "PhoneNo");
}
}
这将向student
表添加一个PhoneNo
列,并带有一些默认值,如下所示
历史
- 2016年3月20日:初始版本