在家闲着也是闲着,继续写我的[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的更多相关文章

  1. [ASP.Net] 转 > ASP.NET MVC 小牛之路

    URL: http://www.cnblogs.com/willick/ 看到了不错的学习笔记,MVC.Net学习之路展开   [ASP.NET MVC 小牛之路]18 - Web API [ASP. ...

  2. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  3. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  4. [ASP.NET MVC 小牛之路]10 - Controller 和 Action (2)

    继上一篇文章之后,本文将介绍 Controller 和 Action 的一些较高级特性,包括 Controller Factory.Action Invoker 和异步 Controller 等内容. ...

  5. [ASP.NET MVC 小牛之路]13 - Helper Method

    我们平时编程写一些辅助类的时候习惯用“XxxHelper”来命名.同样,在 MVC 中用于生成 Html 元素的辅助类是 System.Web.Mvc 命名空间下的 HtmlHelper,习惯上我们把 ...

  6. [ASP.NET MVC 小牛之路]15 - Model Binding

    Model Binding(模型绑定)是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程.我们之前所有示例中传递给 Action 方法参数的对象都是在 Model Binding ...

  7. [ASP.NET MVC 小牛之路]16 - Model 验证

    上一篇博文 [ASP.NET MVC 小牛之路]15 - Model Binding 中讲了MVC在Model Binding过程中如何根据用户提交HTTP请求数据创建Model对象.在实际的项目中, ...

  8. [ASP.NET MVC 小牛之路]17 - 捆绑(Bundle)

    本文介绍 MVC 4 提供的一个新特性:捆绑(Bundle),一个在  View 和 Layout 中用于组织优化浏览器请求的 CSS 和 JavaScript 文件的技术. 本文目录 了解VS默认加 ...

  9. [ASP.NET MVC 小牛之路]18 - Web API

    Web API 是ASP.NET平台新加的一个特性,它可以简单快速地创建Web服务为HTTP客户端提供API.Web API 使用的基础库是和一般的MVC框架一样的,但Web API并不是MVC框架的 ...

随机推荐

  1. 2016ACM/ICPC亚洲区大连站-重现赛(感谢大连海事大学)(7/10)

    1001题意:n个人,给m对敌对关系,X个好人,Y个坏人.现在问你是否每个人都是要么是好人,要么是坏人. 先看看与X,Y个人有联通的人是否有矛盾,没有矛盾的话咋就继续遍历那些不确定的人关系,随便取一个 ...

  2. Sublime Text 3 快捷键

    选择类Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本.Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数名等 ...

  3. NPM 使用淘宝镜像

    --registry https://registry.npm.taobao.org

  4. pwnable.kr的passcode

    前段时间找到一个练习pwn的网站,pwnable.kr 这里记录其中的passcode的做题过程,给自己加深印象. 废话不多说了,看一下题目, 看到题目,就ssh连接进去,就看到三个文件如下 看了一下 ...

  5. React News Site 新闻站

    Learn React & Webpack by building the Hacker News front page from https://github.com/theJian/bui ...

  6. C#调用C++动态库方法及动态库封装总结

    我只是粗浅的学习过一些C++语法, 变量类型等基础内容, 如有不对的地方还望指出. 如果你跟我一样, 对指针操作不了解, 对封装C++动态库头疼的话, 下面内容还是有帮助的. 转载请注明出处: htt ...

  7. Shader实例:扭曲,漩涡

    效果: 案例:新仙剑,王者之剑. 在切换场景的时候,就会有这样的全屏扭曲效果. 思路: 1.用GrabPass抓屏到一张纹理中. 2.进行扭曲,绘制到UGUI的Image上. 准备: 去官网下载Uni ...

  8. Python3.5 Day2作业:购物车程序

    需求: 1. 启动程序后,用户通过账号密码登录,然后打印商品列表. 2. 允许用户根据商品编号购买商品. 3. 用户选择商品后,检测余额是否足够,够就直接扣款,不够就提醒充值. 4. 可随时退出,退出 ...

  9. 使用复合设计模式扩展持久化的CURD,Select能力

    大家可能会经常遇到接口需要经常增加新的方法和实现,可是我们原则上是不建议平凡的增加修改删除接口方法,熟不知这样使用接口是不是正确的接口用法,比如我见到很多的项目分层都是IDAL,DAL,IBLL,BL ...

  10. Qt for Mac 安装(包括PyQt)

    下载Qt: http://qt-project.org/downloads Qt5.2 for Mac:http://download.qt-project.org/official_releases ...