Entity Framework相关介绍
在深入学习某项技术之前,应该努力形成对此技术的总体印象,并了解其基本原理,本文的目的就在于此。
一、理解EF数据模型
EF本质上是一个ORM框架,它需要把对象映射到底层数据库中的表,为此,它使用了三个模型来描述这种映射关系。
(1)概念模型(Conceptual Model):主要体现为一组可以被应用程序直接使用的类。这些类也是我们在程序中直接使用的类,通常称之为“实体(Entity)”
(2)存储模型(Storage Model):主要体现为一组与底层数据存储介质(比如数据库系统)直接对应的类。
(3)概念-存储模型映射(Conceptual- Storage Mapping),解决“概念模型”中的类如何与“存储模型”中的类相互对应的问题。
(2)和(3)中的类型由EF内部使用,在实际开发中通常触及不到。
所有这三种模型都集中放在名为edmx文件中,以XML方式表达。
VisualStudio提供了一个向导,完成从现有数据库到EF数据模型间的映射转换工作:
当此向导完成之后,EF成功地在数据库与程序中使用的对象之间建立了以下对应关系:
|
关系数据库的世界 |
数据库应用程序的世界 |
|
数据库 |
DbContext类 |
|
表 |
DbContext中的DbSet<实体类名> |
|
表间的关联 |
实体类之间的关联 |
|
表中的字段 |
实体类的公有属性 |
|
表中的单条记录 |
单个实体类的对象 |
|
视图 |
DbContext中的DbSet<视图名称> |
|
存储过程 |
DbContext中的公有方法 |
可以在Visual Studio 的模型浏览面板中看到EF数据模型的概念模型和存储模型,如果直接以xml格式打开.edmx文件,可以看到“原汁原味”的全部三大数据模型。
Visual Studio提供的EF向导不仅生成了上述三大数据模型,还使用T4代码模板(其文件扩展名为.tt)直接生成了相应实体类,还有一个派生自DbContext的子类,在其中包容了所有实体集合属性和导入的存储过程等数据库元素。这一DbContext子类是我们使用EF开发程序的核心类型。
在解决方案资源管理器中双击“edmx”文件将打开EF设计器,这是一个很强悍的工具,几乎所有的调整数据映射关系的工作都可以使用它来完成。
如果基于Database First或Model First方式开发,那么EF设计器是天天要打交道的东西。其操作方式很简单:右击设计器,从弹出菜单中选择相应命令。
EF设计器提供的各种命令和具体使用方法有很多资料介绍,在此就不废话了。
二、使用EF访问数据库的基本方式
EF中可以使用以下四种方式访问数据库:
Entity SQL是专门为EF设计的一种查询语言,非常类似于通用的关系型数据库查询语言SQL,但它返回的数据都是在EF“概念模型”中所定义的,而非数据库中数据的“真实模样”。
LINQ to Entities可以看成是LINQ to Objects的一个“变种”,通过LINQ来查询EF数据模型。它在底层使用“对象服务(Object services)”来完成其功能。对象服务是一组用于查询实体数据模型的类,它可以将这些查询结果转换为强类型的CLR对象。
不管是使用Entity SQL还是LINQ to Entities,最终都是依赖“Entity Client”来完成其工作的。
Entity Client包容一组类,比如EntityConnection、EntityDataReader等,与ADO.NET对象模型非常类似,其功能也类似。Entity Client会将对数据的CRUD请求转发给ADO.NET数据提供者(ADO.NET Provider)组件,由其将相关SQL命令直接地发送给数据库。
在实际开发中,大家都使用LINQ to Entities和针对IEnumerable<T>/IQueryable<T>的一组扩展方法完成数据查询工作,几乎不会有人直接使用Entity SQL和 Entity Client。
下图展示了数应用程序运行过程中EF查询的内部处理流程:
可以看到,使用EF的数据查询从发出到真正执行要经过两次“命令树(Command Tree)转换”,而从数据库中读取的数据,也要经历从DbDataReader-->EntityDataReader-->IEnmerable<T>的转换。虽然EF采用了缓存、预编译等手段提升性能,但与ADO.NET相比,由于中间处理环节更多,整个查询处理流程中参与的对象也更多,因此总体性能一般比不上ADO.NET,并且会占用更多的内存,这也我们是在享用EF带来的方便的同时,所需要付出的代价。
另外,由于所有查询最终还是要转换为SQL命令,因此,有些LINQ to Objects可用的扩展方法,比如Last(),在EF中将不能用,因为EF不知道如何把它们翻译成底层数据库支持的SQL命令。
三、三种开发模式的PK
EF支持三种开发模式:
Code First、Database First和Model First。
到底应该用那一种模式是令人纠结的问题。
方式一:Code First
对于初次接触的人,EF的Code First实在很有点魔幻色彩。下面就让我们来体会一下。
创建两个类:Book(书)和BookReview(书评)。一本书可以有多条书评,因此,它们是一对多的关系:
public class Book
{
public virtual int Id {get;set;}
public virtual string Name { get; set; }
public virtual List<BookReview> Reviews { get; set; }
}
public class BookReview
{
public int Id{get;set;}
public int BookId { get; set; }
public virtual string Content { get; set; }
public virtual Book AssoicationWithBook { get; set; }
}
好了,现在创建一个派生自DbContext的子类:
public class BookDb : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<BookReview> Reviews { get; set; }
}
现在可以在程序中随意写几行代码从数据库中提取数据:
static void Main(string[] args)
{
using (var context = new BookDb())
{
Console.WriteLine("数据库中有{0}本书",context.Books.Count());
}
}
运行一下,如果计算机上安装有SqlExpress,那么或者是在应用程序文件夹,或者是打开SQL Server Management Studio(SSME)查看本机SQLServer,你就会发现,数据库己经创建好,其中的表及表的关联也帮助你完成了:
貌似我什么也没干,一切就OK了,神奇啊!
现在修改Book类,给它添加一个Authors属性,代表书的作者:
public class Book
{
public virtual int Id {get;set;}
public virtual string Name { get; set; }
public virtual string Authors { get; set; }
public virtual List<BookReview> Reviews { get; set; }
}
有了前面良好的第一印象,你一定以为只要再次运行程序,底层数据库就会自动更新,然而,EF会给你当头一棒让你清醒:
很明显,因为你修改了实体类,数据库结构也需要修改,比较郁闷的是,你不能打开创建好的数据库直接修改,而需要使用一个名为“数据库迁移(Database Migration)”的功能,采用两种方式(自动迁移和手动迁移)之一完成。
以自动迁移为例:
首先从从Tools菜单中打开Package Manager Console,然后键入:
enable-migrations –EnableAutomaticMigrations
上述命令会在项目中添加一个Migrations文件夹,其中会有一个Configuration类,为了方便,你需要在其构造函数中添加“AutomaticMigrationDataLossAllowed = true;”一句,让其自动重建数据库时不理会可能的数据丢失:
internal sealed class Configuration :DbMigrationsConfiguration<EFCodeFirst.BookDb>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
……
}
好了,现在运行update-database命令更新数据库:
再次运行程序,现在将一切OK。
以后每次更改实体类,都必须手动运行update-database命令更新数据库。
手动迁移方式与自动迁移基本一致,不同之处在于它会记录每次更新的情况,从而允许回滚数据库到某个“较老”的版本。
自动方式比较适合于单个人写的应用。而手动方式可以控制数据库结构的更新,比较适合于团队开发。
Code First模式的优点。
从上面的介绍,可以看到Code First有着突出的优点。
(1)是代码清洁,添加自定义逻辑容易。
这是使用Code First最大的好处
特别是在诸如WPF这种可以保持长连接的桌面应用中,可以让实体类实现INotifyProperty接口,将它们放入ObservableCollection中作为UI界面的绑定数据源,便可以充分利用WPF的数据绑定和EF自动维持实体状态的特点,大幅度地削减代码。
(2)易于实现继承
Code First在实现继承上非常方便,如果数据实体中有大量的继承的情况,使用Code First很方便,但需要注意的是Code First在生成数据库表时,默认使用“TPH:Table Per Hierarchy”方式,把父类子类塞到同一张表中,并在表中添加一个Discriminator字段,表明此记录所属的具体类型。
以下是CodeFirst为拥有继承关系的两个Parent/Child类生成的数据库表,可以看到,Discriminator字段保存了具体的数据类型。
有些朋友问能否把Discriminator字段名给改掉,很遗憾,我没发现可以修改它的方法。
很明显,Code First采用的这种实现策略违反了关系数据库设计范式,对大项目来说,这不易于维护数据的一致性。
实现继承的另外一种方式是TPT(table pertype ),不管子类父类,每个类型一张表。
比如有三个类,Instruct和Student派生自Person,若采用TPT策略,EF将生成三个表,并创建以下的关联:
对于TPH,手写SQL代码很容易,性能高,但对于TPT,EF在生成SQL命令时会产生许多Inner Joint,性能低,另外,对于这种方式存储的数据,手写SQL代码比较麻烦。
Database First和Model First默认情况下都是采用TPH的。
我的建议:除非两实体间确实是IS_A关系,并且在中间层需要使用多态,在“数据存取层(DAL)”尽量少用继承,别自找麻烦。
(3)一些开发高手们还为EF提供了一个EntityFramework Power Tools,这一工具增强了Code First的不少功能,比如它可以从现有数据库直接逆向生成实体类代码,之后就可以修改这些代码,为Code First方式进行开发省去了不少编码工作。同时,它还能为编写的实体类代码生成只读的数据模型视图,以图形的方式展示出实体类间的关联。
Code First存在的问题
CodeFirst试图“用代码搞掂一切”,其问题在于以下几点:
(1)当Model改变时,往往需要编写代码实现数据库的更改,远不如直接使用数据库所提供的工具修改数据库安全和直观,至少丢失数据的可能性小了很多。
(2)当数据实体间有复杂的关联时,需要使用Fluent API手动编写不少代码定义类之间的关联,这实在麻烦。
(3)对数据库表和关联属性的一些微调(比如改改字段名字,修改字段长度限制等),使用数据库设计工具能轻易实现,但Code First只能通过代码来完成,而且必须使用EF的数据迁移特性,这实在麻烦,整个过程还容易出错。
简而言之,Code First应用的场景是:快速开发,迅速迭代。
方式二:Database First
这是EF从1.0开始就支持的特性,其思路是:先设计并建好数据库,然后使用Visual Studio的向导创建EF数据模型并生成实体类代码。
这是最成熟稳定的方式,其设计器相当地完善,基本上能满足实际开发中的各种需求。
我个人认为这是开发正式项目最合适的方式。
当然,DatabaseFirst也有一些问题,主要是需要定制时会有些麻烦。比如:
(1)要想给实体类或生成的DbContext子类添加一些自定义的逻辑,需应用分部类,因为每次更新数据模型,这些代码都会被设计器覆盖并重写。
(2)生成的实体类中不包括任何Data Annotation(所谓“Data Annotation”就是附在实体类代码上的诸如[Required]之类的东东),因此它需要被转换为另一个类,才能方便地在诸如ASP.NET MVC之类的项目中使用(比如ASP.NET MVC项目中的视图模型(ViewModel)类往往需要有Data Annotation,以配合jQuery Validation插件生成网页上的数据验证代码)。
(3)默认情况下实体类与edmx文件放在同一个项目中,想将实体类分离到独立的项目,需要完成一些额外的配置工作(主要是把T4模板文件移到另一个项目,这需要适当地修改T4模板文件中的代码以保证文件路径引用正确)。
方式三:Model First
这种模式是先在可视化设计器中创建实体和它们间的关联,然后设计器生成SQL命令并保存于一个SQL文件中,通过执行这一SQL文件完成数据库的创建和修改工作。
我个人感觉:
这种方式最适合于全新开发的项目,从系统分析开始,逐步分析建立和完善领域模型,之后可以立即创建数据库,如果模型有修改,重新生成一个SQL文件,再执行一次即可,非常适合于新项目OOAD阶段的需要。
当进入OOP阶段时,由于数据库己经存在,就可以很方便地转用Database First方式,整个过程流畅自然。
三种模式PK结果:
Code First:对于小的或用于试验的项目,特别是像我经常要讲课的,教学实例使用Code First开发就比较合适,当程序运行时数据库自动生成,比较省事。
DB First:最为成熟,是正规项目的首选方式,因为是由开发者自己(而不是通过一堆“不太可靠”的代码)直接操作数据库,整个过程高度可控,能很好地保证数据安全,贯彻了“数据比代码重要”的理念。
Model First:当开始一个全新的项目,既没有DB,也没有代码时则非常好,是OOAD的好工具,需要时甚至可以直接生成创建各种不同类型数据库(比如MySQL)的SQL代码!
==============================================================
本篇文章介绍了一些EF相关的通用话题,下一篇文章将讨论一下EF实现CRUD的内部原理。
Entity Framework相关介绍的更多相关文章
- Entity Framework Core介绍(1)
介绍 Entity Framework (EF) Core 是轻量化.可扩展和跨平台版的常用 Entity Framework 数据访问技术. EF Core 可用作对象关系映射程序 (O/RM),以 ...
- Entity Framework开发介绍
一.Entity Framework概要 Entity Framework是微软的Object Relational Mapper(对象关系映射),也就是我们平常说的ORM,它可以让应用程序开发者将关 ...
- Entity Framework API介绍 -- DbSet<>().Find()
过去我们常常使用Where或First(FirstOrDefault)方法来查找对应的实体,比如: var query = context.CertInfoMakeDetails.ToList().W ...
- Entity Framework技术导游系列 开篇 (转)
在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来, ...
- [转]Entity Framework技术导游系列开篇与热身
学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/ ...
- Entity Framework技术导游系列开篇与热身
在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来, ...
- entity framework 新手入门篇(2)-entity framework基本的增删改查
经过前两节的简单描述,终于可以进入entity framework的使用部分了.本节将对entity framework原生的增删改查进行讲解. 承接上面的部分,我们有一个叫做House的数据库,其中 ...
- ABP官方文档翻译 9.2 Entity Framework Core
Entity Framework Core 介绍 DbContext 配置 在Startup类中 在模块PreInitialize方法中 仓储 默认仓储 自定义仓储 应用程序特定基础仓储类 自定义仓储 ...
- Entity Framework 数据并发访问错误原因分析与系统架构优化
博客地址 http://blog.csdn.net/foxdave 本文主要记录近两天针对项目发生的数据访问问题的分析研究过程与系统架构优化,我喜欢说通俗的白话,高手轻拍 1. 发现问题 系统新模块上 ...
随机推荐
- test文件伪装
该文件属于伪装图片类型.图片另存后把后缀改为rar将看到txt文件. 利用了 cmd cp /b 命令 命令格式:copy /b 文件1+文件2+......文件N 合并后的文件名<br&g ...
- 四,ESP8266 TCP服务器
我要赶时间赶紧写完所有的内容....朋友的东西答应的还没做完呢!!!!!!!没想到又来了新的事情,,....... 配置模块作为TCP服务器然后呢咱们连接服务器发指令控制继电器吸合和断开 控制的指令呢 ...
- 201521123022 《Java程序设计》 第七周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 Q1.ArrayList代码分析 Q1.1 解释ArrayList的contains源代码 ArrayLis ...
- 201521123027 《JAVA程序设计》第二周学习总结
1.本周学习总结 1.学习使用码云管理程序代码: 2.认识类型(整数.字节.浮点数.字符与布尔)与变量的使用: 3.学习运算符的基本使用以及类型转换的基本规则: 4.学习String类的一些使用方法: ...
- PHP封装数据库连接
将数据库连接放在类里面,用的时候直接实例化类. 将数据库地址,用户名,密码做成成员变量 将数据库连接做成成员方法,通过传入的sql语句返回结果集对象 class DBDA{ public $host= ...
- PHP面向对象三大特性之一:封装
面向对象的三大特性:封装.继承.多态 1.封装 封装的目的:让类更加安全,做法是不让外界直接访问类的成员 具体做法:1.成员变为私有:访问修饰符(public.private.protected) ...
- 【化繁为简】非前端开发者的福音---CSS的预处理语言 Less&Sass
写在前面: 众所周知CSS 是一门非程序式语言,没有变量.函数.SCOPE(作用域),在前期的界面样式设计时,需要书写大量看似没有逻辑的代码,不方便维护及扩展,也不利于重复调用,尤其对于 ...
- Hyperledger Fabric 1.0 从零开始(七)——启动Fabric多节点集群
5:启动Fabric多节点集群 5.1.启动orderer节点服务 上述操作完成后,此时各节点的compose配置文件及证书验证目录都已经准备完成,可以开始尝试启动多机Fabric集群. 首先启动or ...
- ORACLE PROC开发(转载)
Proc也就是嵌入式C,与informix的ESQ/C有类似之处,本部分主要列出Proc与Esql的区别,相同部分请参见informix部分. 1.数组功能 Proc中支持使用宿主变量数组一次查询SE ...
- Servlet第三篇【request和response简介、response的常见应用】
response.request对象 Tomcat收到客户端的http请求,会针对每一次请求,分别创建一个代表请求的request对象.和代表响应的response对象 既然request对象代表ht ...