转自:http://blog.csdn.net/bitfan/article/details/12779517

Entity Framework走马观花 之

把握全局

=========================================

这是一个系列文章

上一篇《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技术导游系列开篇与热身的更多相关文章

  1. [转]Entity Framework技术导游系列开篇与热身

    学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/ ...

  2. Entity Framework技术导游系列开篇与热身

    在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来, ...

  3. Entity Framework技术导游系列 开篇 (转)

    在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来, ...

  4. Entity Framework技术系列之0:开篇

    小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/shareto ...

  5. 【转】Entity Framework技术系列之7:LINQ to Entities

    前言 LINQ(Language Integrated Query,语言集成查询)是一组用于C#和VB.NET语言的扩展,它允许编写C#或者VB.NET代码,以与查询数据库相同的方式操作内存数据. L ...

  6. Entity Framework 6 开发系列 目录

    2014 年开始接触 Entity Framework 6 也快两年,用它已经沉淀了一个成熟架构,也用来开发了不少大大小小的产品和项目,直到这段时间,才真正有时间来回顾,重新学习它,为让大家更加了解E ...

  7. Entity Framework 5.0系列之Code First数据库迁移

    我们知道无论是"Database First"还是"Model First"当模型发生改变了都可以通过Visual Studio设计视图进行更新,那么对于Cod ...

  8. 基于.NET的微软ORM框架视频教程(Entity Framework技术)

    基于.NET的微软ORM框架视频教程(Entity Framework技术) 第一讲  ORM映射 第二讲 初识EntifyFramework框架 第三讲 LINQ表达式查询 第四讲 LINQ方法查询 ...

  9. Entity Framework 技术参考:http://kb.cnblogs.com/zt/ef/

    Entity Framework 技术参考:http://kb.cnblogs.com/zt/ef/

随机推荐

  1. _x、__x、__x__含义与区别

    _x是一种弱表示,它用在类中的属性或方法,表示是private属性,希望外部使用者不要直接调用它.但它只是暗示,没有任何限制性措施. private属性主要推荐的还是这种方式,因为Python的设计理 ...

  2. java 基本类库包的作用

    tools.jar:工具类库,它跟我们程序中用到的基础类库没有关系. Jre库包含的jar文件(jdk1.6):resources.jar.rt.jar.jsse.jar.jce.jar.charse ...

  3. 【转发】du命令 实现Linux 某个文件夹下的文件按大小排序

    1. df -lh 2. du -s /usr/* | sort -rn这是按字节排序 3. du -sh /usr/* | sort -rn这是按兆(M)来排序 4.选出排在前面的10个du -s ...

  4. Ubuntu13.04 配置smb服务器-new

    1.安装smb服务器:apt-get install samba 2.安装smb支持的文件系统:apt-get install smbfs 或者cifs-utils(因为可能会提示smbfs以过期,已 ...

  5. sql学习资料

    http://blog.sina.com.cn/s/articlelist_1594135432_9_1.html

  6. MongoDB C#驱动中Query几个方法 (转)

    Query.All("name", "a", "b");//通过多个元素来匹配数组 Query.And(Query.EQ("nam ...

  7. poj1179

    //Accepted 244 KB 0 ms //区间dp //石子合并模型 #include <cstdio> #include <cstring> #include < ...

  8. 数据结构 《5》----二叉搜索树 ( Binary Search Tree )

    二叉树的一个重要应用就是查找. 二叉搜索树 满足如下的性质: 左子树的关键字 < 节点的关键字 < 右子树的关键字 1. Find(x) 有了上述的性质后,我们就可以像二分查找那样查找给定 ...

  9. 关于python3 OpenCV的安装和配置

    开发环境,win7 32bit, Anaconda3.之前尝试pip安装,但是总是不成功.后来,交流群里面废帝大神让我用conda安装,之后按照conda install --channel http ...

  10. Python变量与常量

    变量是计算机内存中的一块区域,变量可以存储规定范围内的值,而且值可以改变.基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中.常量是一块只读的内存区域,常量一旦被初始化就不能被 ...