Entity Framework 基础

什么是Entity Framework

在.NET 3.5之前,开发者通常使用ADO.NET获取或保存数据到底层数据库中。即通过ADO.NET连接到数据库,然后使用DateSet对象与数据库交互。这是一个麻烦且容易出错的处理过程,为了解决这个问题,微软提供了Entity Framework的开源框架,处理程序与数据库相关的操作。

EF是一个开源的ORM框架,开发者使用DO(Domain Object)来处理数据,而不需要关注数据库中表和列。使用EF相对于传统的应用程序,开发者可以用更少的代码,在更高的层次处理数据,以及创建和维护面向数据的应用。

官方定义

Entity Framework is an object-relational mapper (O/RM) that enables .NET developers to work with a database using .NET objects. It eliminates the need for most of the data-access code that developers usually need to write.

EF是一个ORM,让开发者使用.NET对象操作数据库。它解决了开发者需要编写大量数据访问代码的疼点。

下图说明了EF在应用中的位置:

EF在App中的位置

从上图可知,EF在业务层(Domain classes)和数据库之间,它保存DO对象属性中的数据,同时也能从数据库中检索数据,然后自动转换为DO对象。

EF特征

  • Cross-platform: EF Core 是跨平台的框架,可以运行在Windows,Linux和Mac上。
  • 实体模型:EF创建一个基于POCO(普通CLR对象)对象的EDM(Entity Data Model 实体数据模型),使用这个模型可以查询和保存实体数据到底层数据库。
  • 查询:EF可以使用LINQ去获取数据库中的数据,database provider将转换LINQ语句到特定的数据库语言。EF也可使用原生的SQL语句。
  • 变更跟踪 Change Tracking:EF会跟踪这个实体状态的更改。
  • 保存 当调用SaveChanges()方法,EF会根据实体的变更执行insert,update,delete等命令。EF也提供了一个异步保存方法SaveChangeAsync()。
  • 并发:EF默认使用Optimistic Concurrency来保护其他用户的变更覆盖。
  • 事务:当查询或保存数据时,EF自动执行事务管理。它也提供了自定义事务处理。
  • 缓存:EF提供了缓存机制,所以,重复的查询将返回缓存中的数据,而不是去访问数据库。
  • 配置:EF是可配置的,并包含了默认配置规则集合,用户可使用注释特征或Fluent API去配置EF模型。
  • 迁移:EF提供了迁移命令集合,可在NuGet包管理器控制台或CLI上,执行创建或管理数据库方案。

EF版本

微软在2008年伴随.NET Framework 3.5一起发布了EF。从哪以后,微软又发布了很多的EF版本。到目前为止,最新的EF版本有两个:EF 6 和 EF Core。下表列举了EF6和EF Core的不同之处:

EF6 EF Core
2008年和.NET Framework 3.5一起发布 2016年与.NET Core 1.0 一起发布
稳定,功能多 新,且是EF的进化版
支持Windows 跨平台
工作在.NET Framework 3.5+ .NET Framework 4.5+和.NET Core
开源 开源

EF 6 的历史版本

EF 版本 发布时间 .NET Framework
EF 6 2013 .NET 4.0&.NET 4.5,vs2012
EF 5 2012 .NET 4.0,vs2012
EF 4.3 2011 .NET 4.0,vs2012
EF 4 2010 .NET 4.0,vs2010
EF 1 2008 .NET 3.5 SP1,vs2008

EF Core 历史版本

EF Core 版本 发布时间 .NET Framework
EF Core 2.0 2017 8月 .NET Core 2.0,vs2017
EF Core 1.1 2016 11月 .NET Core 1.1
EF Core 1.0 2016 7月 .NET Core 1.0

EF 基本工作流程

下图说明了EF基本的CRUD工作流:

EF工作流程
  1. 首先,定义模型,包括领域类,context类(继承自DbContext),配置信息。EF是基于这些模型对象执行CRUD操作的。
  2. 插入数据时,添加领域对象到context中,调用SaveChanges()方法,EF API会构建并执行一个合适Insert语句。
  3. 读取数据时,执行Linq-to-Entities查询语句,EF API将其转化为对应数据库的SQL查询语句并执行它,结果将转化为DO对象。
  4. 当context中更新或删除对象时,调用SaveChanges()方法,EF API会构建并执行合适的Update或Delete语句。

EF工作原理

EF API(EF 6 & EF Core)具有这些能力,包括:

  • 将DO(领域模型对象)映射到数据库集合
  • 翻译并执行Linq语句
  • 跟踪实体在其生命周期变更,以及保存实体的变更

EF API的首要任务是构建Entity Data Model(EDM),EDM在内存中的结构可以用来表示:概念模型,存储模型以及者两者两者之间的映射。

Conceptual Model 概念模型由EF通过你定义的领域模型,context和配置生成的。

Storage Model EF从数据库集合中生成存储模型。在code-first方案中,存储模型是由概念模型推断出来的。在database-first方案中,它是从数据库中推断出来的。

Mappings 映射信息包含了概念模型和存储模型之间的映射。

EF通过EDM执行CRUD操作。它通过EDM从Linq语句中构建SQL,以及将执行结果转变为实体对象。

EF 总体架构

下图展示了EF的总体架构

ef框架架构

我们分别看看架构中的组件

EDM(Entity Data Model):EDM由概念模型,映射和存储模型三部分构成(见上文)。

Linq to Entities:L2E是一个针对对象模型编写的查询语言,它返回定义在概念模型中的实体。

Entity SQL:Entity SQL是以一种查询语言有点像L2E,但是只能运行在EF6中。相对于L2E,Entity SQL稍难,而且需要开发者单独学习。

Object Service:Object Service是访问数据库的主要入口点。它负责将下一层返回的数据转换为实体对象的结构。

Entity Client Data Provider:这一次主要负责将Linq语句转化为数据的SQL语句,它与ADO.Net进行通信,ADO.Net又向数据库发送或检索数据。

ADO.Net Data Provider:这一层使用标准的ADO.Net库与数据库通讯。

Context类

在EF中context非常重要,它类继承自DbContext,它表示一个与数据库的会话。下面的代码是context类的示例:

public class SchoolContext:DbConext{
    public SchoolContext(){

    }

    public DbSet<Student> Students {get;set;}
    public DbSet<StudentAddress> StudentAddresses {get;set;}
    public DbSet<Grade> Grades {get;set;}
}

上面的示例中,SchoolContext继承自DbContext,使得他成为了一个context类。它包含了Student,StudentAddress以及Grade的实体集合。

context类用于从数据库中查询或保存数据。也可以用来配置领域模型,数据库关系映射,变更追踪变更设置,缓存,事务等。

EF中的实体

EF中的实体是应用程序的领域类,他被包含在context类的DbSet<TEntity>类型属性中。EF API将TEntity映射到数据库中的表,TEntity的属性映射为表中的列。比如,在school应用程序中,Student,StudentAddress和Grade的领域模型如下:

public class Student{
    public int StudentID {get;set;}
    public string StudentName {get;set;}
    public DateTime? DateOfBirth {get;set;}
    public byte[] Photo {get;set;}
    public decimal Height {get; set}
    public float Weight {get; set}

    //引用导航属性
    public StudentAddress StudentAddress {get; set;}
    public Grade Grade {get; set;}
}

public partial class StudentAddress{
    public int StudentID { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }

    //引用导航属性
    public Student Student { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    //集合导航属性
    public ICollection<Student> Students { get; set; }
}

当他们包含在context类中的DbSet<TEntity>属性中时,它们就是实体。而Students, StudentAddresses和Grades被称为实体集。

实体包含两种类型的属性:标量属性(Scalar Properties)和导航属性(Navigation Properties)。基本类型的属性称为标量属性,标量属性存储实际的数据,标量属性映射到数据中表的列。导航属性表示该属性关联到另一个实体,这里又有两种类型的导航属性:引用导航和集合导航。

  • 引用导航属性表示,一个实体的属性的类型是另一个实体类型。
  • 集合导航属性表示,实体属性的类型是集合类型。

EF中实体的类型

EF中有两种实体类型:POCO实体和动态代理实体(Dynamic Proxy Entity)。

  • POCO实体:POCO就是一个基本类,不依赖任何框架,EF 6和EF Core都支持POCO实体。POCO实体通过EDM生成实体类型,支持CRUD操作。
  • 动态代理(POCO Proxy)是运行时的代理类,它包裹了POCO实体,动态代理实体可以lazy loading。动态代理类只支持EF6(EF Core 2.0 不支持该类型)。

实体的状态

EF API维护实体的状态。context类执行实体操作时,会影响实体的状态。实体的状态用枚举来表示,EF 6中是System.Data.Entity.EntityState枚举类型,而EF Core中是Microsoft.EntityFrameworkCore.EntityState枚举类型。他们有如下的枚举值:

  1. Added 执行insert命令
  2. Modified 执行update命令
  3. Deleted 执行delete命令
  4. Unchanged 无任何改变
  5. Detached 不追踪实体的状态

context不仅保存所有实体的引用(从数据库中获取),而且还能追踪和维护实体的状态(这个功能叫变更追踪)。context仅会自动处理实体从Unchanged状态到Modified状态。其他状态的改变必须使用明确的DbContext或DbSet方法。在调用context.SaveChanges()方法时,EF API是基于实体的state创建和执行增删改语句的。

开发模式

EF有三种不同的模式供开发者选择:

  1. Database-First
  2. Code-First
  3. Model-First

在数据库优先模式中,可以使用Visual Studio集成EDM向导或执行EF命令来存在的数据库生成context和实体。EF 6对该中模式支持的非常好,EF Core也提供了有限的支持。

当应用程序还没有数据库时,可以使用代码优先模式。在这种模式下,需要先写DO和context,然后使用迁移命令创建数据库。开发者如果遵循领域驱动设计(Domain-Driven Design DDD)原则,喜欢先编写领域模型然后在生成数据库。EF 6和EF Core都支持这种模式。

模型优先模式下,Visual Studio中集成了的Visual Designer工具,可以在上面创建实体,关系和继承层次结构,然后生成实体,context和数据库脚本。这种方式仅EF 6支持。

EF中的持久方式

当使用EF保存实体到数据库中时,有两种场景供选择:连接场景和非连接场景。

连接场景

在连接场景下,检索和保存实体都是使用的同一个context对象,这样,它可以在对象的生命周期中保持对其状态的追踪。这种场景常常用在Windows应用程序或数据库在本地的情况。

连接场景

上图可以看到,检索和保存都是使用的同一个context对象。这项做的好处是,执行效率高,context对象保持对实体状态的跟踪。缺点是需要对数据库保持长连接,比较耗资源。

非连接场景

检索和保存使用不同的context对象,使用完后context对象会被自动释放。

非连接场景

非连接场景要相对复杂些,因为context对象不能追踪实体的状态,所以开发者在调用SaveChanges()方法前,必须要设置实体的状态。在上图中,程序使用context1检索数据,使用context2执行CUD操作。这种模式常用在web应用程序或远程数据库的情况。优点,使用较少的资源,不需要长连接。缺点,在保存数据之前需要设置实体的状态,执行效率稍慢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容