65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Migration 的 Code First 方法在 MVC 中

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (26投票s)

2016年3月20日

CPOL

5分钟阅读

viewsIcon

51636

downloadIcon

369

如何使用 Migration 的 Code First 方法在 MVC 中

引言

在应用程序中实现 Entity Framework 有三种不同的方法

  1. 数据库优先 (Database First)
  2. 模型优先 (Model First)
  3. 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。NameEmailPassword列的长度与我们在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; }
    }

如果我们运行应用程序并添加一个学生,我们将收到以下异常

现在我们有三个选项

  1. DropCreateDatabaseIfModelChanges:我们可以在MyDataContext构造函数中设置它。但是,如果我们设置了它,那么每当我们的模型发生更改时,我们的数据库都将被删除并重新创建。因此,数据库中的所有数据都将丢失。
  2. 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 包管理器,然后打开程序包管理器控制台

逐个运行以下命令

  1. PM> Uninstall-Package EntityFramework
  2. PM> Install-Package EntityFramework
  3. PM> Enable-Migrations -ContextTypeName CodeFirstApproach.Models.MyDataContext

    一旦我们运行了以上命令,我们的解决方案中就会添加一个migrations文件夹,其中包含一个配置类文件和一个带有日期时间戳的另一个文件。此文件包含表的初始状态。

    现在使用MigrationName(在我的例子中是AddAddressProperty)运行以下命令。

  4. PM> Add-Migration AddAddressProperty

    上述命令将在Migrations文件夹中创建另一个文件,其中包含自上次迁移以来你在模型中所做的更改。

    现在更新你的数据库。

  5. 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日:初始版本
© . All rights reserved.