EntityFramework走马观花之CRUD(上)
对于任何一个ORM框架,CRUD都是其核心功能,可以这么说,CRUD功能实现得好坏,直接决定了此ORM框架的命运。
CRUD是英文Create、Read、Update、Delete四个单词的缩写,对应于汉语,就是“增、删、改、查”四个字。再细分一下,“增、删、改”可归为一类,其特点是要更新数据源,而“查”则归为另一类,它不修改原始的数据源。
我们的技术探索之旅,从“查”开始。
1 两种查询数据的方式
EF主要使用两种方式查询数据:LINQto Entities和一组针对IQueryable<T>的扩展方法。
采用LINQ方式的查询代码拥有很强的可读性:
- using (var context = new BookDb())
- {
- var books = from book incontext.Books
- select new { bookName = book.Name , reviewCount = book.Reviews.Count };
- foreach (var book in books)
- {
- Console.WriteLine("{0}有书评{1}条。",book.bookName, book.reviewCount);
- }
- }
无需加注释,只看代码就能很轻松地知道其功能提取保存在数据库中的所有书的书评数量信息。
上述功能也可以使用扩展方法实现,这时需要编写Lambda表达式:
- using (var context = new BookDb())
- {
- foreach (var book incontext.Books.Select( b => new { bookName = b.Name , reviewCount = b.Reviews.Count}))
- {
- Console.WriteLine("{0}有评论{1}条。",book.bookName, book.reviewCount);
- }
- }
这两种方式没有好坏之分,只不过使用第二种方式可能会让别人觉得你“比较牛B”。 :-)
2 装入相关联的数据
大多数数据实体间都有着各种各样的关联,最常见的是比如一本书关联多个书评,这是一对多的关联。而书与书的作者之间的关系,就是多对多关联:一个作者可以写多本书,一本书作者也可以不止一个。
EF在数据实体间建立导航属性实现关联。在实际项目中,经常是得到一个(或一堆)实体对象之后,需要查询相关联的“另一堆”对象所封装的信息,这就是“关联数据的装入”问题。
对于这个问题,EF支持三种装入策略。
(1)当导航属性是virtual时,EF默认使用“延迟装入(lazy loading)”。比如书与书评的关联定义如下:
- public class Book
- {
- ……
- public virtual List<BookReview> Reviews { get; set; }
- }
则仅当程序中需要访问某Book对象的Reviews属性时,EF才向数据库发出SQL命令装入相关的书评数据。
(2)如果确认与某实体对象相关联的数据是确实需要的,可以使用Include()方法通知EF这个情况,EF将告诉数据库:“把XXX和XXX打包,一次性地发给我”:
- var books = from book in context.Books.Include("Reviews")
- select book;
- foreach (var book in books)
- {
- Console.WriteLine ("{0}有评论{1}条。", book.Name , book.Reviews.Count);
- }
这种方式称为“预先装入(Eager Loading)”。避免了第2次访问数据库的需要。
(3)第三种方式称为“显式装入(Explicit Loading)”,使用Load()方法实现,例如,以下代码让EF装入50条书和书评信息到内存中:
- context.Books.Take(50).Include("Reviews").Load();
这些数据“就位”后,可以缓存起来备用。
我们也可以“有目的”地只提取需要的数据,请看以下代码:
- using (var context = new BookDb())
- {
- Book book =context.Books.First();
- context.Entry(book).Collection("Reviews").Load();
- Console.WriteLine("{0}有书评{1}条。",book.Name, book.Reviews.Count);
- }
上述代码将只提取第一本书的所有书评。
下面对这三种模式进行一个小结:
(1)就查询性能而言,是“预先装入(Eager Loading)”最好,因为数据己全部装入内存,后继的查询无需再访问数据库。其缺点是可能会占用大量的内存。
(2)如果能预知要访问的数据项,并且当前在内存的数据对象中,只有少数对象需要查询获取更进一步的数据,那么使用“显式装入(Explicit Loading)”策略能有效地减少数据传输量,获得较高的性能。
(3)当一个实体类拥有多个一对多的关联,而且难以预知要访问的数据项,则“延迟装入(Lazy Loading)”又比较合适了,它仅在“需要用到时”才提取数据,能有效地减少内存占用,但可能给数据库服务器带来过多的数据查询请求。
(4)当数据实体对象需要序列化时,一定要使用“预先装入(Eager Loading)”,其原因是序列化对象时需要使用反射查询数据实体对象的所有属性,如果是延迟装入(Lazy Loading)”,将会导致向数据库发出大量的查询请求,而且每个对象都要发出“一堆”这样的查询请求,绝对会让数据库痛苦!
3 Find方法与本地数据
DbSet有一个Local属性很有趣,它引用当前己装入内存的数据,注意它包括当前己从数据库中取出或新加的但还没有保存到数据库中的数据,我们可把它称为DbSet对象的“本地数据集合”。
本地数据集合的一个重要特性是:针对它发出的各种查询不会发往数据库。
请看以下场景:
假设我只想提取数据库中第1本和第11本书的书评信息,以下代码完成这一工作:
- using (var context = new BookDb())
- {
- Book book =context.Books.First();
- Console.WriteLine("{0}有书评{1}条。",book.Name, book.Reviews.Count);
- book =context.Books.OrderBy(b => b.BookId).Skip(10).First();
- Console.WriteLine("{0}有书评{1}条。",book.Name, book.Reviews.Count);
- }
打开SQL Server Profiler,你会发现EF先后为上述代码生成了4条SQL命令发给数据库。
现在修改一下代码,使用 “显式装入”+ Local查询 方式:
- using (var context =new BookDb())
- {
- //提前装入50条书和书评数据到内存中
- context.Books.Take(50).Include("Reviews").Load();
- //在本地集合中进行查询
- Book book =context.Books.Local.First();
- Console.WriteLine("{0}有书评{1}条。",book.Name, book.Reviews.Count);
- book =context.Books.Local.OrderBy(b=>b.BookId).Skip(10).First();
- Console.WriteLine("{0}有书评{1}条。",book.Name, book.Reviews.Count);
- }
查看一下SQL ServerProfiler,你会发现EF只为上述代码生成一条SQL命令(虽然这条命令比较复杂,但毕竟只有一条)发给数据库,后继的查询都是在内存中进行的,相当地快!
“本地数据集合”还有另一个值得关注的特性。
请注意DbSet.Local属性的类型是ObservableCollection,当向DbSet中添加和移除数据时,此“本地数据集合”对象会激发CollectionChanged事件。这对于WPF桌面应用来说实在是太妙了!
你可以在WPF桌面程序中这样干:
使用DbContext提取数据后,让WPF数据绑定控件(比如DataGrid)通过ViewSource组件绑定到相应DbSet的Local属性,之后就可以在窗体上进行CRUD操作,所有操作被DbContext缓存起来,当用户点击“保存”按钮(或者是关闭窗体时),调用DbContext.saveChanges()方法存入数据库。DbContext会自动跟踪实体对象的状态,整个过程完全是自动化的!
如果你基于.NET 4.5并使用EF 6,则可以在WPF应用中直接使用EF6新增的异步方法,比如DbContext.SaveChangesAsync(),EF会在独立的线程中完成数据的提取、保存等工作(线程的分派工作在底层由.NET的TPL负责,程序员可以不理会它),并且在这些工作结束时,异步方法的后继代码将在UI线程中执行,这样一来,多线程应用中原来比较讨厌的跨线程更新UI控件的问题就不存在了,用起来实在是方便,代码可以精简不少!
最后一个与“本地数据集合”相关联的话题是DbSet的Find方法,开发时记住以下这句话就行了:
使用Find方法查询数据时,它会先在内存中找,找不到之后再到数据库中提取。
有关数据查询的技巧还有不少,本篇文章只是介绍了我觉得比较重要的知识和比较有用的一些技巧,算是抛砖引玉吧。
下篇文章带领大家到EF“更新数据”这个领域“寻幽探胜”。
EntityFramework走马观花之CRUD(上)的更多相关文章
- [转]EntityFramework走马观花之CRUD(上)
学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/ ...
- [转]EntityFramework走马观花之CRUD(下)
学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/ ...
- [转]EntityFramework走马观花之CRUD(中)
学习Entity Framework技术期间查阅的优秀文章,出于以后方便查阅的缘故,转载至Blog,可查阅原文:http://blog.csdn.net/bitfan/article/details/ ...
- EntityFramework走马观花之CRUD(下)
我在Entity Framework系列文章的CRUD上篇中介绍了EF的数据查询,中篇谈到了EF的数据更新,下篇则聊聊EF实现CRUD的内部原理. 跟踪实体对象状态 在CRUD上篇和中篇谈到,为了实现 ...
- EntityFramework走马观花之CRUD(中)
如果是独立的实体对象,在底层数据库中它对应一张独立的表,那么,对它进行新建.删除和修改没有任何难度,实在不值浪费笔墨在它上头. 在现实项目中,完全独立的对象少之又少,绝大多数情况都是对象之间有着紧密的 ...
- EntityFramework 基础的crud
EntityFramework 基础的crud操作 根据上一张实体映射的demo学习基础的crud操作 1.增加 BlogDbContext dbContext = new BlogDbContext ...
- EntityFramework 学习 一 CRUD using Stored Procedure: 使用存储过程进行CRUD操作
我们先创建如下3个存储过程 1.Sp_InsertStudentInfo: CREATE PROCEDURE [dbo].[sp_InsertStudentInfo] -- Add the param ...
- (转)EntityFramework之领域驱动设计实践
EntityFramework之领域驱动设计实践 - 前言 EntityFramework之领域驱动设计实践 (一):从DataTable到EntityObject EntityFramework之领 ...
- EntityFramework之领域驱动设计实践
EntityFramework之领域驱动设计实践 - 前言 EntityFramework之领域驱动设计实践 (一):从DataTable到EntityObject EntityFramework之领 ...
随机推荐
- JavaScript中this详解
这里的主题是 this ,不扯远了.this 本身原本很简单,总是指向类的当前实例,this 不能赋值.这前提是说 this 不能脱离 类/对象 来说,也就是说 this 是面向对象语言里常见的一个关 ...
- 2014第五届蓝桥杯试题C/C++程序设计B组——切面条
题目描述:标题:切面条 一根高筋拉面,中间切一刀,可以得到2根面条. 如果先对折1次,中间切一刀,可以得到3根面条. 如果连续对折2次,中间切一刀,可以得到5根面条. 那么,连续对折10次,中间切一刀 ...
- GridView 鼠标经过时变色两种方法
第一种: 新建一个js文件 并引用 <script src="jquery.js" type="text/javascript"></scri ...
- Entity Framework 5问题集锦
ORM框架万万千,一直都使用NHibernate,没用过其他的.最近闲来学习下微软自家的Entity Framework,记录一些我学习过程中遇到的头疼问题.(不断更新中...) 教程:http:// ...
- LotusPhp起步:经典的HelloWorld
写了几篇LotusPhp,一直没有跑个程序,感觉好像步骤有点错,所以先上个经典的Demo,HelloWorld吧 先按推荐目录建好文件夹,如果懒的建,下面有下载的Demo包,解压就可以用,因为简单,也 ...
- 问题记录:android.os.NetworkOnMainThreadException异常
在MainActivity的主线程中访问网络将会发生异常 查阅资料后发现 Android4.0 以后不允许在主线程进行网络连接
- Python学习教程(learning Python)--2.3.5 Python返回多个值问题
本节主要学习Python的函数是如何同时返回多个值的问题. 在学习Python的时候惊奇的发现,Python的函数可以同时返回多个值,很有意思. #define function sum def su ...
- Ruby判断文件是否存在
flag = FileTest::exist?("LochNessMonster") flag = FileTest::exists?("UFO") # exi ...
- TeXworks代码补全功能
使用Tab键会自动补全(实在是太TM的方便了) 比如:
- MIFARE系列4《组成图》
MIFARE集成电路芯片内含EEPROM.RF接口和数字控制单元. 1. RF射频接口 在RF射频接口电路中,主要包括有波形转换模块.它可将卡片读写器上的13.56MHZ的无线电调制频率接收,一方面送 ...