entity:实体,所有需要被持久化的对象。
目前.NET平台官方的ORM工具就是Entity Framework,根据所依据的.NET框架不同,又分为:
两者大同小异,我们的课程先讲一遍.NET core,最后再对照.NET core把EF6快速的拉一遍。
PS:其他流行的、由开源社区支持维护的ORM还有NHibernate、Dapper、FreeSql……
演示:通过Nuget引入EF框架
这是EF的的两个核心对象。
因为要OOD(Object-Oriented Dessign/Develop),即从一开始就首先考虑对象(entity)而不是数据库,我们选择使用Code First。声明一个Student对象:
public class Student { public int Id { get; set; } //或者:public int StudentId { get; set; } public string Name { get; set; } public Bed SleepIn { get; set; }
一个Bed对象:
public class Bed
注意:每一个Entity都应该有一个Key定义/辨别该Entity,EF默认使用属性名为 Id 或 类名+Id 的属性作为 Key。否则后面会报错。
通常我们都是自己创建一个类来继承DbContext
public class SqlDbContext : DbContext
EF通过provider的形式支持多种数据库,详见:Database Providers
所以还需要在DbContext子类中override OnConfiguring()方法,指定数据库:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { string connectionString = @"Data Source=(localdb)\ProjectModels;Initial Catalog=17bang;Integrated Security=True;"; optionsBuilder.UseSqlServer(connectionString);
为了适配SQL Server(点出UseSqlServer()),还需要安装dll:
Microsoft.EntityFrameworkCore.SqlServer
如果是要连接mysql的话,详见Linq……
然后,通过声明/添加DbSet<E>属性,告知EF哪些entity需要被持久化。
//注意这里:不能是字段 public DbSet<Student> Students { set; get; } //EF据此确定哪些class需要映射到数据库或者,在方法中OnModelCreating()中使用方法配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Teacher>();
顾名思义,OnModelCreating在Moldel创建时被EF自动调用,通过modelBuilder.Entity<T> ()还可以设置T的自定义映射细节。
默认的(by convention),EF会把以下三种class映射到数据库
可以根据DbContext中指定的entity,自动建表。
为了使用EF Migrations工具,我们还需要在起始项目中引入dll:(ASP.NET Core version 2.1后版本已自动安装)
Microsoft.EntityFrameworkCore.Tools
然后,在VS中打开 Package Manager Console(Tools > NuGet Package Manager > Package Manager Console)
输入:GET-HELP about_EntityFrameworkCore
显示以下内容,表明Migration Tool安装成功
PS:如果操作系统是windows7,可能还需要额外的安装相应的.NET Framework/Core
根据entity“准备”好表结构映射。
演示成功运行命令:Add-Migration <name>之后(<name>由开发人员指定,注意要有意义),
EF在DbContext所在项目生成一个文件夹Migrations:
以及两种类文件:
ModelSnapshot(快照):记录的是当前EF的映射情况
migration文件
由EF将现有的entity和ModelSnapShot.cs做对比生成,供之后Update-Database使用(前进/回滚)
如果是mysql的话,(EF6)需要在DbContext上添加特性:
[DbConfigurationType(typeof(MySqlEFConfiguration))] public class MysqlDbContext : DbContext
//Student中添加一行Age public int Age { get; set; }
我们就可以再执行一次Add-Migration。
每执行一次Add-Migration,添加migration文件并更新ModelSnapShot(所以它始终只有一个)
删除(不是revert)“最近”(注意只能是最近)的一次migration,表现为:<datetime_stamp><migration_name>.cs同时被删除(演示:略)
仅仅执行Add-Migration,数据库不会变化(演示)
执行Update-Database命令,才会利用Migrations文件夹下内容,同步数据库表结构。(3.1.30第一次会自动建库,已有数据库会报异常)
演示:检查数据库,可以看到
该表由Migration自动生成并维护。
顾名思义,_MigrationHistory记录了和Migration的历史。
这样,当执行Update-Database命令时,EF会对比表_MigrationHistory和C#项目中的Migrations文件夹,决定需要执行的Migration
我们还可能会回退到某一个版本(复习:git),这时数据库需要响应的回滚。
在Update-Database时我们可以指定“版本号”,形成“回退/前进”效果:
@想一想@:为什么需要数据库里的_EFMigrationsHistory?
生成sql代码,常用于部署。可使用参数:
生成的SQL只包含_EFMigrationHistory数据。
(演示:略)
注意指定好:
Migration会:
可以使用C#代码快速的建库建表。
首先生成一个SqlDbContext对象
SqlDbContext context = new SqlDbContext();
然后删除/创建数据库:
context.Database.EnsureDeleted(); context.Database.EnsureCreated();
Enusure:存在才删除,不存在才创建。
演示:建立的数据库没有__EFMigrationsHistory表
之前我们使用的都是EF的默认配置。
表名:
modelBuilder.Entity<NewStudent>().ToTable("Student");
[Table("Student")] public class NewStudent //NewStudent 对应着 Student表列名:
modelBuilder.Entity<NewStudent>().Property(s => s.IsFemale) .HasColumnName("Gender");
[Column("Gender")] public bool IsFemale { get; set; }
EF对各种基本数据类型的映射都比较完美,比如C#的int自动对应SQL的int,bool对应bit……
注意EF会将DateTime自动映射成SQL中的DateTime2。
这是因为C#中DateTime的默认值是0001-01-01 00:00:00.0000000
枚举会自动转为int类型演示:DayOfWeek Oncall
默认是nvarchar(max),不能建立索引。所以有时候会指定
最大长度,nvarchar(50)
[MaxLength(50)] public string Name { get; set; }
或者固定长度,nchar(50)
[StringLength(50)] public string Name { get; set; }
OnModelCreating()中没有StringLength,所以:
modelBuilder.Entity<Student>().Property(s => s.Name) .HasMaxLength(50) .IsFixedLength() //指定固定长度
还可以用字符串直接指定(简单粗暴)
modelBuilder.Entity<Student>() .Property(s => s.Oncall).HasColumnType("NVARCHAR(20)");
对于C#中有默认值的值类型属性(比如int),EF默认是映射为NOT NULL的;要想EF默认映射为可以NULL值,需要将其声明为可空类型,比如int?。
对于引用类型(如字符串),EF默认是映射为可以NULL,要想变成NOT NULL:
modelBuilder.Entity<Student>() .Property(s => s.Name).IsRequired();或者
[Required] public string Name { get; set; }
比如Age只能在0-150之间。你可以会认为使用Range特性即可:
[Range(0, 150)] //不会自动生成CHECK约束 public int Age { get; set; }但这是不行的,需要在OnModelCreating中进行配置(EF core3.0之后版本):
modelBuilder.Entity<Student>() .HasCheckConstraint("CK_Age","Age Between 0 AND 150")
演示查看生成的SQL
ALTER TABLE [dbo].[TB_Student] ADD CONSTRAINT [CK_Age] CHECK ([Age]>=(0) AND [Age]<=(150));
EF默认将Entity中的Id或者<entity>Id作为主键。如果我们要指定其他主键(不推荐),可以:
modelBuilder.Entity<Student>() .HasKey(s => s.Name);
或者:
[Key] public string Name { get; set; }
联合主键因为要跨多列,所以只能设置在OnModelCreating()中:(后文详述)
modelBuilder.Entity<StudentAndTeacher>() .HasKey(st => new { st.TeacherId, st.StudentId });
除了主键,EF不会自动添加索引。
我们需要自己添加:
modelBuilder.Entity<Student>() .HasIndex(s => s.Name) .IsUnique(); //默认是非唯一的
modelBuilder.Entity<Student>() //联合键索引 .HasIndex(s=>new {s.Name, s.Age })
或者
[Index("Name", IsUnique = true)] public class Student
默认所有的属性都会被映射。
如果想要排除掉一些属性,可以使用:
modelBuilder.Entity<Student>().Ignore(s => s.Age);
甚至可以直接ignore掉整个Entity:
modelBuilder.Ignore<Student>();
或者
[NotMapped] public string Age { get; set; }
ORM概述第1/2/3题
多快好省!前端后端,线上线下,名师精讲
更多了解 加: