[ASP.NET MVC 小牛之路]06 - 使用 Entity Framework
在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧。在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码中手工造的数据。本文将演示如何在ASP.NET MVC中使用Entity Framework从数据库中获取数据。虽然本文题目听上去比较简单,但如果你认真阅读,相信你一定会有所收获。
本文目录:
ORM 和 EF
当我们要开发一个应用程序,就要考虑怎样展示数据,怎样持久化数据。考虑这个问题时我们所要关心的东西,最重要的莫过于程序的性能、开发的简易性和代码的可维护、可扩展性。
持久化(Persistence),是指在应用程序中能永久地保存各个处理状态信息的机制。如果没有持久化这个机制,状态只能保存在内存中,机器关机后就会丢失。
在应用程序中,当要永久地保存数据时,我们会选择关系数据库(Relation DataBase);当要临时地保存数据时,我们则使用存储在内存中的对象。目前对于大多数开发人员来说,都是用关系数据库技术来作为持久化机制。虽然现在有些人正在尝试使用对象数据库(Object DataBase)技术,但关系数据库技术在许多年以内依然会是最主要的持久化机制。
为什么会出现对象数据库? SQL语言是一种非过程化的面向集合的解释型语言,而很多高级语言是过程化的面向对象的编译型语言,这使得两种语言之间存在着不匹配,导致效率不如人意。这种不匹配被称为“阻抗失配”,对象数据库的出现就是为了解决“阻抗失配”。
我们知道,在关系数据库技术中是用Table以行和列的结构来存放和组织数据的。在.NET 2.0以前,C#还没有泛型的时候,人们基本上用填充在DataSet中的DataTable来映射并存放从关系数据库中查询出来的数据,正如下面代码所示:
using (SqlConnection conn = new SqlConnection(connString)) {
using (SqlDataAdapter da = new SqlDataAdapter("Select * from order", conn)) {
DataTable dt = new DataTable();
da.Fill(dt);
...
}
}
这种方式虽然能让面向对象语言匹配关系数据库,但它有一些明显的缺点,如非类型安全、难操控、低性能等。从.NET 2.0开始,人们开始通过泛型技术用实体模型对象的集合来匹配关系数据库中的数据,这种方式解决了DataTable方式所面临的缺点,且它有强类型、在VS中自动完成、编译时检查等特点,厂受.Net开发人员的喜爱。
为了让开发人员不用手动去做这种“匹配”工作,人们研发了很多ORM工具(如Entity Framework、NHibernate等)。ORM(Object Relation Mapping)工具,顾名思义,它的角色就是为了解决“关系”和“面向对象”之间的“失配”,它可以使得开发人员不用过多关心持久层而可以花更多的时间专注于业务。
Entity Framework(EF)是微软以ADO.NET为基础所发展出来的ORM解决方案,以Entity Data Model(EDM) 为主。EF利用了抽象化数据结构的方式,将每个数据库对象都转换成应用程序中的类对象(Entity),而数据字段都转换为属性 (Property),关系则转换为结合属性 (Association),让数据库的 E/R 模型完全的转成对象模型,如此让开发人员就能用熟悉的面向对象编程语言来调用访问。EF 4.0 以后支持Database First、Model First、Code First三种生成模式,Code First模式用的人比较多。
概念和理论性的东西就不多讲了,我也是带着疑问去查找网络资源根据自己的理解半摘半归纳出来的,而且鄙人不才,很多东西想描述但都不知道怎么组织语言。
本文目的主要是让读者对EF有个感性的认识,然后了解EF在ASP.NET MVC中的应用,不会去研究原理性的东西,下次有空再单独介绍EF吧。
使用Entity Framework
接着上一篇博文[ASP.NET MVC 小牛之路]05 - 使用Ninject,我们现在要把代码中手工造的数据改成从数据库读取。为此,我们先准备下数据库。本示例使用的是MS SQL Server,使用其他数据库也是一样的。先创建一个名为BookShop的数据库,然后执行下面的脚本创建一个Books表:
CREATE TABLE Books
(
[ID] INT NOT NULL PRIMARY KEY IDENTITY,
[Title] NVARCHAR(100) NOT NULL,
[Isbn] VARCHAR(20) NOT NULL,
[Summary] NVARCHAR(1000) NOT NULL,
[Author] NVARCHAR(50) NOT NULL,
[Thumbnail] VARBINARY(MAX),
[Price] DECIMAL(16, 2) NOT NULL,
[Published] DATE NOT NULL,
)
然后随便在表中加几条用于测试的数据:

接下来我们就要让应用程序连接数据库了。由于上一篇博文是我在公司用休息的时间写的,公司的电脑装的是VS2010,家里的笔记本装的是VS2012,所以得重新把上篇博文的示例移到VS2012上,对于本示例,VS2010和VS2012都是一样的。上一篇示例项目的目录结构如下:

本文的示例将在上篇的这个示例基础上继续。
用NuGet在BookShop.Domain工程中安装Entity Framework包,方法请参考本系列的上一篇文章。
在BookShop.Domain工程的Concrete文件夹中添加一个名为EFDbContext的类,代码如下:
public class EFDbContext : DbContext {
public DbSet<Book> Books { get; set; }
}
使用EF Code First第一步就是创建一个继承自System.Data.Entity.DbContext的类,这个类将为数据库中的每个表定义一个属性,属性的名称代表数据库中的表名。DbSet作为返回类型,它是用于生成CRUD(Create、Read、Update和Delete)操作的装置,映射数据库表的行。
我们需要在BookShop.WebUI工程中的web.config配置文件中添加数据库的连接字符串,来告诉EF怎样连接数据库。根据自己机器上的数据库配置连接字符串如下:
<connectionStrings>
<add name="EFDbContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=BookShop;User ID=sa;Password=sa" providerName="System.Data.SqlClient" />
</connectionStrings>
接下来,我们把BookShop.Domain工程下Concrete文件中的BookRepository类文件改造一下,把代码中手工造的数据改成从数据库读取,以测试应用程序是否可以正常连接数据库。修改后的BookRepository类如下:
public class BookRepository : IBookRepository {
private EFDbContext context = new EFDbContext();
public IQueryable<Book> Books {
get { return context.Books; }
}
}
在我们的这个仓储类中,我们改使用EF,通过创建一个EFDbContext类的实例来获取数据库中的数据。如你所见,我们不需要自己写ADO.NET代码去连接和读取数据库,非常简洁明了,我们就是这样使用Entity Framework的。我们来看一下运行效果吧:

到这我们已经成功使用EF连接上了数据库,并从数据库中读取出来了数据。我们还可以通过Linq进行非常灵活的查询,就像写SQL一样。比如要查询价格在100元以下的前10条记录,并且按价格从低到高显示,那么我们可以在BookShop.WebUI工程下的BookController中的List方法中这样写:
public ViewResult List() {
return View(repository.Books
.OrderBy(b => b.Price)
.Where(b => b.Price < )
.Take());
}
或许你很快就会对EF获取数据库的方式产生这样的疑问:EF从数据库中读取整个Books表的数据到内存,然后返回给调用者(上面代码中的repository.Books)用Linq语句过滤用户想要的前10条数据,如果Books表中有几百万条数据,那内存岂不是完蛋了,EF不会这么傻吧?EF会不会根据Linq查询语句智能地生成SQL文本再到数据库中去查询数据呢?这里就要讲讲IQueryable和IEnumerable了。
IQueryable 和 IEnumerable
其实,对于上面的即有过虑又有排序的条件查询Linq语句,EF是读取数据库中整个Books表中的数据到内存,还是根据Linq查询语句智能的生成SQL再执行查询,完全编码者来决定的。我们打开BookShop.Domain工程的BookRepository类文件,请注意该类中Books属性的返回类型:
...
public IQueryable<Book> Books {
get { return context.Books; }
}
在上篇博文中,我们对使用IQueryable作为返回类型提了个疑问:为什么用IQueryable而不用IEnumerable作为返回类型?答案是:使用IQueryable,EF会根据调用者的Linq表达式先生成相应的SQL查询语句,然后到数据库中执行查询,查询出来的数据即是用户想要的数据;而使用IEnumerable,Linq表达式的过滤、排序等操作都是在内存中发生的,即EF会先从数据库中把整个表的数据查询出来放在内存中,然后由调用者使用Linq语句进行过滤、排序等操作。是不是这样呢?我们来监视一下两种情况EF生成的SQL语句就知道了。
我们先来看看使用IQueryable的情况。重新运行一下程序,然后使用SQL Server Management Studio的活动和监视器查看一下我们的BookShop应用程序所执行的SQL语句,结果如下:

结果证明使用IQueryable,EF是先根据Linq表达式生成相应的SQL语句再执行查询的。
我们再稍稍修改一下代码来看看用IEnumerable的情况。把BookRepository类修改如下:
public class BookRepository : IBookRepository {
private EFDbContext context = new EFDbContext();
public IEnumerable<Book> Books {
get { return context.Books; }
}
}
当然BookRepository类所实现的IBookRepository接口(在BookShop.Domain工程的Abstract文件夹中)也要改一下:
public interface IBookRepository {
IEnumerable<Book> Books { get; }
}
再重新运行一下应用程序,用活动和监视器查看最后执行的SQL语句如下图:

我们看到改用IEnumerable后,EF生成的SQL没有任何过滤、排序等的操作,它一次把表中的所有数据都Select出来,和上面写的Linq表达式一点都没关系。
IQueryable虽然可以很智能地根据Linq表达式生成相应的SQL语句,但毕竟有一个分析Linq表达式的过程,相对来说性能比IEnumerable要差。那么我们什么时候用IEnumerable,什么时候用IQueryable呢?我想,对于少量的数据(比如从数据库中读取应用程序相关的系统信息)和不需要对数据进行过滤操作的情况,用IEnumerable比较适合;对于数据量较大需要对数据进行过滤(比如分页查询)的情况,则用IQueryable比较合适。
参考: 《Pro ASP.NET MVC 4》
http://www.entityframeworktutorial.net/
[ASP.NET MVC 小牛之路]06 - 使用 Entity Framework的更多相关文章
- [ASP.Net] 转 > ASP.NET MVC 小牛之路
URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开 [ASP.NET MVC 小牛之路]18 - Web API [ASP. ...
- [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject
本人博客已转移至:http://www.exblr.com/liam 为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...
- [ASP.NET MVC 小牛之路]05 - 使用 Ninject
在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...
- [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)
继上一篇文章之后,本文将介绍 Controller 和 Action 的一些较高级特性,包括 Controller Factory.Action Invoker 和异步 Controller 等内容. ...
- [ASP.NET MVC 小牛之路]13 - Helper Method
我们平时编程写一些辅助类的时候习惯用“XxxHelper”来命名.同样,在 MVC 中用于生成 Html 元素的辅助类是 System.Web.Mvc 命名空间下的 HtmlHelper,习惯上我们把 ...
- [ASP.NET MVC 小牛之路]15 - Model Binding
Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...
- [ASP.NET MVC 小牛之路]16 - Model 验证
上一篇博文 [ASP.NET MVC 小牛之路]15 - Model Binding 中讲了MVC在Model Binding过程中如何根据用户提交HTTP请求数据创建Model对象.在实际的项目中, ...
- [ASP.NET MVC 小牛之路]17 - 捆绑(Bundle)
本文介绍 MVC 4 提供的一个新特性:捆绑(Bundle),一个在 View 和 Layout 中用于组织优化浏览器请求的 CSS 和 JavaScript 文件的技术. 本文目录 了解VS默认加 ...
- [ASP.NET MVC 小牛之路]18 - Web API
Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的 ...
随机推荐
- Addthis
WordPress外贸主题模版可以非常方便地整合国外流行的分享收藏社会化网络功能,比如Addthis是国外一个网络书签按钮聚合网站,是稳步提升网站流量和搜索引擎排名的WEB2.0工具!通过AddThi ...
- 常见css水平自适应布局
左右布局,左边固定,右边自适应布局 BFC方法解决 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ...
- 矩阵快速幂 HDU 4565 So Easy!(简单?才怪!)
题目链接 题意: 思路: 直接拿别人的图,自己写太麻烦了~ 然后就可以用矩阵快速幂套模板求递推式啦~ 另外: 这题想不到或者不会矩阵快速幂,根本没法做,还是2013年长沙邀请赛水题,也是2008年Go ...
- 2016-2-10 tomcat基础学习
安装tomcat服务器(1)配置 JAVA_HOME指向你的jdk主目录 也可以在startup.bat文件中设置JAVA_HOME环境变量. 首次使用JAVA_HOME前设置即可.set JAVA_ ...
- 2016-2-1 Servlet细节
Servlet的一些细节(韩顺平老师视频讲解)(1)由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序想要被外界访问,必须把servlet程序映射到一个URL地址上.这个工作在 ...
- startssl申请配置免费https证书
之前给业务配置都是在沃通上申请免费证书,而后通过反向代理层的Nginx进行https认证. 今天来了个新需求,要求域名直接解析至阿里云SLB.https配置需要通过阿里云的控制台部署这倒无所谓,只是在 ...
- Linux tricks
Environment Settings Path Globally set path is in /etc/profile; or the user's .bash_profile for part ...
- bzoj1051Tarjan裸题
tarjan缩点+判断出度为0的点 所以不需要新建边 #include <cstdio> ,time=,T=,sum=,ans=; ],to[],nex[],fir[],dfn[],low ...
- java的基础知识文件操作和标识符
1.文件夹的操作 dir :显示当前文件夹中的所有文件和文件夹. cd 路径: 进入到指定的路径. cd .. : 回到上一级目录 cd \ : 回到当前目录的跟目录 md 文件夹名 创建一个 ...
- mysql安装流程
一.配置MySQL数据库 1.解压绿色版mysql,如下图 二.安装服务 1.运行cmd(管理员版本,否则没有权限),如下图 2.运行命令mysqld –install安装服务,如下图: 如果不需要m ...