EFCore数据迁移

EFCore数据迁移

数据迁移

初始化

  1. Code First

    首先创建 DbContext ,使用以下命令添加迁移,初始化数据库。

    1
    2
    dotnet ef migrations add <migration name>
    dotnet ef database update
  2. Data First

    根据 已经存在 的数据库,使用以下命令创建数据库上下文与实体类

    1
    dotnet ef dbcontext scaffold <connection string> <provider(E.g. Microsoft.EntityFrameworkCore.SqlServer)>

Tipdotnet ef --help , dotnet ef migrations --help , dotnet ef database --help , dotnet ef dbcontext --help 。这些命令可以查询其他配置。dotnet-ef命令行官方文档(.NET Core CLI)

字段的特性

FullName 没有 Set 方法,不会再数据库新建这个列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
[Display(Name = "First Name")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime EnrollmentDate { get; set; }
[Display(Name = "Full Name")]
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}

public ICollection<Enrollment> Enrollments { get; set; }
}
}

导航属性,如果是集合,类型可以是 ICollection<T>HashSet<T> , List<T>。如果设置为 ICollection<T> ,EF 默认是一个 HashSet<T>

1
public ICollection<CourseAssignment> CourseAssignments { get; set; }

InstructorOfficeAssignment 关系为 1 - 0…1,意味着讲师可能没有办公室。InstructorID 是外键,同时也是 主键 ,EF只能识别为外键, [Key] 这个特性将 InstructorID 标志为主键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
public class OfficeAssignment
{
[Key]
public int InstructorID { get; set; }
[StringLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }

public Instructor Instructor { get; set; }
}
}

[DatabaseGenerated(DatabaseGeneratedOption.None)] 设置主键不由数据库自动生成。在课程这个表中,CourseID 可能需要极大力度的用户自定义,比如 A课程是 1000 ,B课程是 2000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }

[StringLength(50, MinimumLength = 3)]
public string Title { get; set; }

[Range(0, 5)]
public int Credits { get; set; }

public int DepartmentID { get; set; }

public Department Department { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
public ICollection<CourseAssignment> CourseAssignments { get; set; }
}
}

指定一个货币类型的列。2333…mysql不支持 money ,应该换为 float(9,2)

1
2
[Column(TypeName="money")]
public decimal Budget { get; set; }

可空字段,作为外键。如果非空呢?EF默认层级删除,意味着删除外键id指向的数据,会同时删除外键所在表对应数据。

1
2
public int? InstructorID { get; set; }
public Instructor Administrator { get; set; }

fluent API 取消严格的层级删除

1
2
3
4
modelBuilder.Entity<Department>()
.HasOne(d => d.Administrator)
.WithMany()
.OnDelete(DeleteBehavior.Restrict)

只能使用fluent API 设置复合主键

1
2
modelBuilder.Entity<CourseAssignment>()
.HasKey(c => new { c.CourseID, c.InstructorID });

记一次迁移失败

  1. 错误原因

    Course (原来已有数据),添加一列 DepartmentID ,此列非空,并且 Department 表还未创建。DepartmentID 默认是外键,存在外键约束。 EF在迁入的时候不知道,不知道给一个什么默认值,所以报错。

    外键约束
    FOREIGN KEY 约束用于预防破坏表之间连接的动作。
    FOREIGN KEY 约束也能防止非法数据插入外键列,因为它 必须是它指向的那个表中的值之一

  2. 错了怎么回滚

    • 在数据库中迁移表添加一行记录,错的这个版本的。这里是 20180411020802_ComplexDataModel,假装这次迁移已经成功……

      | MigrationId | ProductVersion |
      | :—–: | :—: |
      | 20180410142846_InitialCreat | 2.0.1-rtm-125 |
      | 20180410152220_MaxLengthOnNames | 2.0.1-rtm-125 |
      | 20180410152629_ColumnFirstName | 2.0.1-rtm-125 |
      | 20180410153611_ColumnSetRequied | 2.0.1-rtm-125 |
      | 20180411020802_ComplexDataModel | 2.0.1-rtm-125 |

    • 执行 dotnet ef database update <last version> ,当然你要保证这一版本的 Down 方法里面的正确性(有些 Up 方法里的可能还没执行到,需要注释 Down 方法里部分)

      1
      dotnet ef database update ColumnSetRequied
  3. 解决外键的问题

    1. 注释掉 Up 方法里的添加 DepartmentID
    2. 在创建 Department 这个表之后,添加以下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      migrationBuilder.Sql("INSERT INTO dbo.Department (Name, Budget, StartDate) VALUES ('Temp', 0.00, GETDATE())");
      // Default value for FK points to department created above, with
      // defaultValue changed to 1 in following AddColumn statement.

      migrationBuilder.AddColumn<int>(
      name: "DepartmentID",
      table: "Course",
      nullable: false,
      defaultValue: 1);