前言
Entity Framework是.Net开发平台主流的数据访问框架,EFCore是一个真正的ORM(对象关系映射)框架。为什么这么说?因为很多开发人员经常用一些“轻量级”的ORM与EF进行比较,进而得出EF性能低下难以使用的结论。他们忽视了EF提供的大量复杂而重要的功能,比如工作单元,类层次映射,值对象映射等特性。
.Net Core 平台Util应用框架作者 何镇夕
Entity Framework和Entity Framework Core都是微软的ORM技术,在平时工作中应该算是使用非常频繁的一个技术,因此在这里记录一下EFCore的学习过程,也为以后的工作提供一些参考。万丈高楼平地起,这是一个入门级别的教程,主要的内容是介绍如何使用EFCore的基本功能,并且通过一些实例来探究执行过程。参考文献
《你必须掌握的Entity Framework 6.x与Core 2.0》 —Jeffcky
《Entity Framework系列教程汇总》 —捞月亮的猴子
《Entity Framework Core 2.x 入门》 —solenovex
《ASP.NET Core MVC 和 Entity Framework Core 入门教程》 —yiyuan.han
Entity Framework Core 简介
EFCore 是什么?
Entity Framework Core (EF Core)是微软推荐的基于.NET Core framework的应用程序数据访问技术。它是轻量级,可扩展并且支持跨平台开发。EF Core是一种对象关系映射器(ORM)。通过应用程序实体对象和关系数据库中的数据的映射,使得开发人员能够以面向对象的方式处理数据。
官方文档 https://docs.microsoft.com/zh-cn/ef/core/
点击了解更多关于ORM
了解ORM,先了解以下概念:
什么是“持久化” ?
持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中,当然也可以存储在磁盘文件中、XML数据文件中等等。
什么是 “持久层”?
持久层(Persistence Layer),即专注于实现数据持久化应用领域的某个特定系统的一个逻辑层面,将数据使用者和数据实体相关联。
什么是ORM?
即Object-Relationl Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了 。
为什么要做持久化和ORM设计(重要)?
在目前的企业应用系统设计中,MVC,即 Model(模型)- View(视图)- Control(控制)为主要的系统架构模式。MVC 中的 Model 包含了复杂的业务逻辑和数据逻辑,以及数据存取机制(如 JDBC的连接、SQL生成和Statement创建、还有ResultSet结果集的读取等)等。将这些复杂的业务逻辑和数据逻辑分离,以将系统的紧耦 合关系转化为松耦合关系(即解耦合),是降低系统耦合度迫切要做的,也是持久化要做的工作。MVC 模式实现了架构上将表现层(即View)和数据处理层(即Model)分离的解耦合,而持久化的设计则实现了数据处理层内部的业务逻辑和数据逻辑分离的解耦合。 而 ORM 作为持久化设计中的最重要也最复杂的技术,也是目前业界热点技术。
简单来说,按通常的系统设计,使用 JDBC 操作数据库,业务处理逻辑和数据存取逻辑是混杂在一起的。
一般基本都是如下几个步骤:
1、建立数据库连接,获得 Connection 对象。
2、根据用户的输入组装查询 SQL 语句。
3、根据 SQL 语句建立 Statement 对象 或者 PreparedStatement 对象。
4、用 Connection 对象执行 SQL语句,获得结果集 ResultSet 对象。
5、然后一条一条读取结果集 ResultSet 对象中的数据。
6、根据读取到的数据,按特定的业务逻辑进行计算。
7、根据计算得到的结果再组装更新 SQL 语句。
8、再使用 Connection 对象执行更新 SQL 语句,以更新数据库中的数据。
9、最后依次关闭各个 Statement 对象和 Connection 对象。
由上可看出代码逻辑非常复杂,这还不包括某条语句执行失败的处理逻辑。其中的业务处理逻辑和数据存取逻辑完全混杂在一块。而一个完整的系统要包含成 千上万个这样重复的而又混杂的处理过程,假如要对其中某些业务逻辑或者一些相关联的业务流程做修改,要改动的代码量将不可想象。另一方面,假如要换数据库 产品或者运行环境也可能是个不可能完成的任务。而用户的运行环境和要求却千差万别,我们不可能为每一个用户每一种运行环境设计一套一样的系统。
所以就要将一样的处理代码即业务逻辑和可能不一样的处理即数据存取逻辑分离开来,另一方面,关系型数据库中的数据基本都是以一行行的数据进行存取的,而程序 运行却是一个个对象进行处理,而目前大部分数据库驱动技术(如ADO.NET、JDBC、ODBC等等)均是以行集的结果集一条条进行处理的。所以为解决 这一困难,就出现 ORM 这一个对象和数据之间映射技术。
举例来说,比如要完成一个购物打折促销的程序,用 ORM 思想将如下实现(引自《深入浅出Hibernate》):
业务逻辑如下:
这样代码就非常清晰了,而且与数据存取逻辑完全分离。设计业务逻辑代码的时候完全不需要考虑数据库JDBC的那些千篇一律的操作,而将它交给 CustomerManager 和 PromotionManager 两个类去完成。这就是一个简单的 ORM 设计,实际的 ORM 实现框架比这个要复杂的多。
EFCore原理是什么?
可以简单理解为类和数据库之间的映射,要解决一个表对多个类,或者一个类对多个表等问题。
详细了解可以参考Jeffcky的这篇博文:EF Core迁移原理
为什么使用EFCore?
- 主要是提高生产力,而不是因为性能
- 支持很多数据库
- 可以使用Linq
- 注重领域(Domain)而不是数据库
EFCore 2.x 支持哪些平台框架?
- .Net Core 2.x
- .Net 4.6.1 +
- UWP
- Xamarin
EFCore使用实例
创建EF Core Demo
Step1 创建解决方案EFCore,添加Netcore类库EFCore.DomainModels。
Step2 继续添加Netcore类库项目EFCore.Data。
Step3 在DomainModel中新建Model,一个是省份Province,另一个Model是城市City。
namespace EFCore.DomainModels
{
public class Province
{
public Province()
{
Cities = new List();
}
public int Id { get; set; }
public string Name { get; set; }
public int Population { get; set; }
public List Cities { get; set; }
}
}
namespace EFCore.DomainModels
{
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public string AreaCode { get; set; }
public int ProvinceId { get; set; }
public Province Province { get; set; }
}
}
注意:
这两者之间是一对多的关系。
City类有一个外键ProvinceId和导航属性Province。
Province构造函数里面已经初始化City,这样Province.Cities就不会为Null。
关于EF导航属性的讲解,可以参考默默淡然这篇博文《EF Code First 导航属性 与外键》
点击了解更多关于导航属性
EF Core导航属性分为三种:
集合导航属性:主表中对子表相关数据的引用
引用导航属性:子表中对主表数据的引用
反转导航属性:一个导航属性对应的另一端的导航属性
微软的示例:
Blog是主表,Post是子表
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
在以上实体类的定义中:
Blog.Posts是集合导航属性,包含子表中的关联数据。
Post.Blog是引用导航属性,包含主表中的关联数据。
Post.Blog是Blog.Posts的反转导航属性,反过来也一样。
通过子表查询主表数据:
`var post=db.Posts.Include("Blog").First();`
可以访问到Blog表的其它字段:
`Console.Write(post.Blog.Url)`
通过主表访问子表数据:
`var blog=db.Blogs.Include(b=>b.Posts).First();`
可以访问子表相关的所有数据:
foreach(var post in blog.Posts)
{
Console.Write(post.Title);
}
通过引用导航属性访问主表数据,不需要额外定义。
通过集合导航属性访问子表数据,需要使用Fluent API配置。重写数据上下文的OnModelCreating方法,加入以下代码:
builder.Entity
.HasOne(post => post.Blog)
.WithMany(bolg => blog.Posts);
如果不使用Fluent API进行配置,执行`var blog=db.Blogs.Include(b=>b.Posts).First();`时会报数据库语法错误。
</details>
### Step4 安装EFCore
在Data项目中,从Nuget安装Microsoft.EntityFrameworkCore和Microsoft.EntityFrameworkCore.SqlServer。
注意DomainModel项目中只放Model,不在这个里面进行安装EFCore。


### Step5 项目引用关系
Data项目需要引用DomainModel的项目。

### Step6 在Data项目中建立MyContext类。
MyContext继承于DbContext,Context中包括了所有的用于数据库交互的操作。
这里使用sqllocaldb,可以在本地cmd用sqllocaldb info进行查看。
```CS
namespace EFCore.Data
{
public class MyContext: DbContext
{
public DbSet<Province> Provinces { get; set; }
public DbSet<City> Cities { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB; Database=EFCoreDemo; Trusted_Connection=True;");
}
}
}
正常生产环境中的数据库连接字符创应该写在配置中,这里为了进行演示就写在MyContext中了。
程序第一次使用时就会触发Onconfiguring这个方法,迁移时也会使用这个方法。
EFCore 迁移
迁移过程的三个主要步骤:
- 创建/修改 Domain Model
- 创建迁移文件
- 应用迁移到数据库,或生产SQL脚本
想要在VS中使用迁移命令的话需要安装迁移用的库: Microsoft.EntityFrameworkCore.Tool
按照官方文档,执行之前首先需要安装两个库:
Microsoft.EntityFrameworkCore.Tools - PowerShell命令
Microsoft.EntityFrameworkCore.Design - 迁移引擎
实际上,只需添加Tools这个库即可,因为它下面包依赖了Design这个库。
应用EFCore迁移的两种方式
方式一:直接迁移
Update-Database -verbose (加上-verbose 可以显示更新数据库的详情)
本文的例子在执行过程后,会自动在User下面生成数据库文件。方式二:生成SQL脚本
script-migration可以生产迁移脚本。
生产环境一般是通过迁移脚本进行数据库更新的。
执行EFCore迁移命令
Step1 打开Package Management Tool
英文版VS Studio 可以直接在搜索框中搜索package Manager Tool;
中文版可以在视图–>NuGet包管理工器–>程序包管理控制台中打开。
注意选择正确的项目执行迁移命令,这里我们选择EFCore.Data项目。
Step2 查看都有哪些迁移命令:
通过get-help entityframeworkcore命令可以查看EFCore的迁移命令。
- Add-Migration命令创建了Model或者Model变了,生成一些SQL语句但并没有执行
- Update-Database命令读取之前创建的Migration,执行SQL语句并进行匹配
Step3 生成Migration:
执行add-migration Initial命令,这里的Initial是这次migration的名字。
执行后生成三个文件,分别是:
MyContextModelSnapshot快照文件,
带时间戳的迁移文件20190709125559_Initial.cs 和 20190709125559_Initial.Designer.cs
应用迁移更新数据库
Method1 命令直接更新:Update-Database -verbose (加上-verbose 可以显示更新数据库的详情)
此时会自动在User下面生成数据库文件。
通过视图里面的SQL Server对象资源浏览器 可以查看我们创建的数据,可以观察到迁移表中记录等。
Method 2 生成SQL脚本:script-migration