Entity Framework学习笔记(三)----CRUD(2)
请注明转载地址:http://www.cnblogs.com/arhat
昨天晚上老魏配的机器终于到了,可是拿回来之后什么都组装好了,唯独差一个非常重要的组件”电源线”,老魏那个汗啊。于是从朋友那里借来一根电源线终于折腾到凌晨两点多才把所有的软件和系统装好,今天早上顺便装了一个MAC(嘻嘻)。最让老魏兴奋的是换了VS2013,在使用VS2013的时候感觉非常的不错,对EF的支持真的是大大的增强了,不用再跑到网上找这个插件那个插件的,直接通过T4文件来生成Context是实体类了。那么今天老魏就用VS2013和SQL Server2008来继续学习EF把,至于创建的过程老魏在第一章中已经介绍过了,基本过程是一样的。前两章老魏用的是MySQL,那么本章用的是SQL Server,本质上是一样的,老魏就是把MySQl中的数据导入到SQL Server中。
说到这里了,老魏不得不提一下在SQL Server表设计器中是无法对表建立多对多关系的,需要到数据库关系图中建立,这点很让老魏不爽!更让了老魏不爽的是使用HeidiSQL竟然无法对SQL Server建立表关系,我那个擦啊!弄得老魏都崩溃了,如果各位朋友那个有好一点的SQL Server的客户端工具可以推荐一下。
好了,开始本章的内容!在上一章中我们只是对Clazz,Student两个表进行一个单独的插入,删除和更新以及简单的使用了Where扩展方法来得到数据。那么本章将会继续对这两个表的操作来进一步的说明,因为在本章主要是利用EF的懒加载来对数据操作。好了,看看本章的第一个需求。
如果我们想在插入班级的时候就把改班级的学生也插入到数据库中,这样可以不可以呢?答案是可以的,其实在平时我们的项目中就已经有这样的需求,那么下面我们来看看用EF怎么实现呢!
更改Program.cs代码如下:
static void Main(string[] args)
{
//EF 上下文
DAL.SchoolContext context = new DAL.SchoolContext();
//定义一个Student实体对象
Model.Student student = new Model.Student() { SName="大话济公",SAge=56,SMail="single_jie@163.com"};
//定义一个Clazz实体对象,这实体对象是要插入到数据库中的
Model.Clazz clazz = new Model.Clazz() { CName="泡妞课程"};
//通过Clazz的导航属性把student对象加入到Clazz的Students集合中
//这点EF的导航属性做的非常不错,传统的ADO.NET实现起来比较麻烦
clazz.Students.Add(student);
//更改数据库
context.SaveChanges();
}
当我们执行的时候没有发现问题产生,难道真得能够插入到数据中吗?我们打开数据库来看看结果,验证一下我们的猜想。

发现在数据库中Student表和Clazz表都已经正确的插入了,那么说明没有问题,但是我们在高兴的同时要多思考一下。如果是在插入学生的时候能不能直接选择一个班级呢?这个问题是在项目中经常操作的,比如发表一篇文章的时候要选择分类。
好,我们既然发现了问题,那么我们在更改一下代码:
static void Main(string[] args)
{
//EF 上下文
DAL.SchoolContext context = new DAL.SchoolContext();
//定义一个Student实体对象
Model.Student student = new Model.Student() { SName="广亮和尚",SAge=34,SMail="guangliang@163.com"};
//定义一个Clazz实体对象,这实体对象是要插入到数据库中的
Model.Clazz clazz = new Model.Clazz() { CName="泡妞课程"};
//通过导航属性把clazz对象附加到student实体对象上
student.Clazz = clazz;
//把clazz对象加入到Clazz
context.Student.Add(student);
//更改数据库
context.SaveChanges();
}
同样的运行没有任何的问题,但是当我们查看数据的时候却发现问题来了。

各位发现了什么?学生能够正常的插入,而班级则重复的插入了,按照我们正常的思维者这条记录是不应该插入到数据库中,其实这不该怪EF的,这是我们的业务逻辑有问题了,那么下面我们更改一下代码:
static void Main(string[] args)
{
//EF 上下文
DAL.SchoolContext context = new DAL.SchoolContext();
//定义一个Student实体对象
Model.Student student = new Model.Student() { SName="飞龙僧",SAge=56,SMail="feilong@163.com"};
//得到一个Clazz实体对象
Model.Clazz clazz = context.Clazz.Where<Model.Clazz>(c => c.CId == 1).Take<Model.Clazz>(1).FirstOrDefault<Model.Clazz>();
//通过导航属性把clazz对象附加到student实体对象上
student.Clazz = clazz;
//把clazz对象加入到Clazz
context.Student.Add(student);
//更改数据库
context.SaveChanges();
}
运行一下,发现没有问题,当我们再次查看数据的时候,发现数据时正常的,在Clazz表中并没有插入新的纪录(废话,因为我们是查出来的嘛!)。

但是此时我们有生出了一个疑问,在想Student表中插入数据的时候,我们只需要一个CId,但这里我们却是给student实体对象一个clazz对象,这样不是浪费了内存了吗?其实不然,微软比我们聪明肯定想到了。其实在赋值的时候,只是把CId属性的值赋值给了Student的CId了。下面是生成的SQL语句
INSERT [dbo].[Student]([SName], [SAge], [SMail], [CId]) VALUES (@0, @1, @2, @3) SELECT [SId] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [SId] = scope_identity()',N'@0 varchar(50),@1 int,@2 varchar(50),@3 int',@0='飞龙僧',@1=56,@2='feilong@163.com',@3=1
本来嘛,使用EF只是为了能够更简单的操作数据库,那么肯定是要牺牲一点效率的,这点就不用考虑了,现在的服务器的配置都那么高了,根本不在乎这点内存的浪费。
现在我们如果要更新一个学生的班级该如何做呢?看看代码:
static void Main(string[] args)
{
//EF 上下文
DAL.SchoolContext context = new DAL.SchoolContext();
//得到一个Student实体对象
Model.Student student = context.Student.Where<Model.Student>(s=>s.SId==3).Take<Model.Student>(1).FirstOrDefault<Model.Student>();
//定义一个Clazz实体对象,这实体对象是要插入到数据库中的
Model.Clazz clazz = context.Clazz.Where<Model.Clazz>(c => c.CId == 10).Take<Model.Clazz>(1).FirstOrDefault<Model.Clazz>();
//通过导航属性把clazz对象附加到student实体对象上
student.Clazz = clazz;
//更改数据库
context.SaveChanges();
}
运行一下,正常运行,在数据中同样也到了争取的结果了。但是我们现在又有问题诞生了,在更新的时候,我们是先查询出来让后再更新到数据库,这样的话就对数据库操作了两次,显然不是我们想要的效果,那么该怎么来解决这问题呢,也就是不用先查询然后在更新呢?答案是可以的!
在说这个问题的时候,老魏在这里得给大家说一下抱歉了,由于前两章的内容是MySql EF5的版本的,所以和本节EF6的内容稍微的优点不同,所以这里呢,老魏使用EF6的方法把。
比如现在我们有如下的代码:
static void Main(string[] args)
{
//EF 上下文
DAL.SchoolContext context = new DAL.SchoolContext();
Model.Student student = new Model.Student();
//在使用这中方法的时候,必须保证主键在数据库中必须存在
student.SId = 4;
student.SName = "飞龙僧";
student.SAge = 34;
student.CId = 1;
student.SMail = "flydragon@ww.com";
//通过DbEntityEntry来跟踪student的状态,通过修改状态值,来更新数据
context.Entry(student).State = EntityState.Modified;
//更改数据库
context.SaveChanges();
}
运行一下,在数据总的确放生改变了,那么在这里出现的Entry又是个什么东西呢?根据微软的解释是:“DbEntityEntry此类的实例提供对有关由 DbContext 跟踪的实体的信息和控制的访问权。 使用上下文的 Entity 或 Entities 方法来获取此类型的对象。“什么意思呢?其实Entity Framworke再把实体更新到数据库中(增删改查),是根据实体的状态来确定的,那么实体的状态又是由DbEntityEntry这个类的对象来保存和设置,但同时还起到一个非常重要的作用,那就是把Entity(这里是student)加入到context中,如果Entity不在context中,那么context是无法更新数据的,所以context.Entry(Entity)就是把实体加入到context中进行管理,同时更改一下他的状态,让context知道如何去处理这个Entity.当调用SaveChanges方法时候,context就根据state来处理数据。
EntityState,实体对象的状态。标志我们开发人员对实体的相应的操作,如下表格是实体的相关状态以及说明。
| 成员名称 | 说明 |
| Detached | 对象存在,但没有被跟踪。 在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态。 An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded by using a NoTrackingMergeOption. 没有 ObjectStateEntry 实例与状态为 Detached 的对象关联。 |
| Unchanged | 自对象附加到上下文中后,或自上次调用 SaveChanges 方法后,此对象尚未经过修改。 |
| Added | 对象为新对象,并且已添加到对象上下文,但尚未调用 SaveChanges 方法。 在保存更改后,对象状态将更改为 Unchanged。 状态为 Added 的对象在 ObjectStateEntry 中没有原始值。 |
| Deleted | 对象已从对象上下文中删除。 在保存更改后,对象状态将更改为 Detached。 |
| Modified | 对象上的一个标量属性已更改,但尚未调用 SaveChanges 方法。 在不带更改跟踪代理的 POCO 实体中,调用 DetectChanges 方法时,已修改属性的状态将更改为 Modified。 在保存更改后,对象状态将更改为 Unchanged。 |
对象上下文必须知道对象状态才能将更改保存回数据源。虽然这样我们可以少操作一次数据库,但是由于必须保证的是主键在数据库中必须存在,所以这点我们在写程序的时候是很难保证的,所以老魏建议还是使用原来的方法,当然了这个得根据不同情况选用不同的方法,有的人推荐使用EntityState,有的人推荐使用查询更新,具体情况及具体分析吧,但是在这里老魏的说一下,如果要更改实体的某些属性而不是全部属性的时候,我们这个时候得使用EntityState了。
好了,到此,关于Entity Framework的CRUD的操作说明老魏就说到这里了,只能起到一个抛砖引玉的作用,希望大家能够从中得到点什么东西吧!
Entity Framework学习笔记(三)----CRUD(2)的更多相关文章
- Entity Framework学习笔记(二)----CRUD(1)
请注明转载地址:http://www.cnblogs.com/arhat 这篇文章老魏和大家分享一下Entity Framework的CRUD操作,在这之前呢,老魏先说一下老魏对EF的一个整体的认识, ...
- Entity Framework 学习笔记(2)
上期回顾:Entity Framework 学习笔记(1) Entity Framework最主要的东西,就是自己创建的.继承于DbContext的类: /// <summary> /// ...
- Entity Framework学习笔记
原文地址:http://www.cnblogs.com/frankofgdc/p/3600090.html Entity Framework学习笔记——错误汇总 之前的小项目做完了,到了总结经验和 ...
- ADO.NET Entity Framework学习笔记(3)ObjectContext
ADO.NET Entity Framework学习笔记(3)ObjectContext对象[转] 说明 ObjectContext提供了管理数据的功能 Context操作数据 AddObject ...
- Entity Framework 学习笔记
1.自定义数据库链接字符串上下文 public class PetDbContext : DbContext { public PetDbContext() : base("name=Dem ...
- Entity Framework 学习笔记(1)
开始从头系统地学习Entity Framework,当前的稳定版本为6.1.3,Nuget主页 http://www.nuget.org/packages/EntityFramework/ 微软喜欢把 ...
- Entity Framework学习笔记——错误汇总
之前的小项目做完了,到了总结经验和更新学习笔记的时间了.开始正题之前先啰嗦一下,对之前的学习目标进行一个调整:“根据代码生成表”与“生成数据库脚本和变更脚本”合并为“Code First模式日常使用篇 ...
- Entity Framework学习笔记——记一个错误解决方式及思路
继续之前设定的学习目标前,先来一篇小小的外篇.按照第一篇里的配置方式配置好的工程前两天还能正常工作,昨天却突然无法通过Add-Migration命令进行数据库的升级.错误信息如下: System.Da ...
- Entity Framework学习笔记——配置EF
初次使用Entity Framework(以下简称EF),为了避免很快忘记,决定开日志记录学习过程和遇到的问题.因为项目比较小,只会用到EF的一些基本功能,因此先在此处制定一个学习目标:1. 配置EF ...
随机推荐
- 手机NFC模拟门禁卡
楼主所在的某电子科技类大学,从宿舍楼到实验楼到图书馆办公楼,全部都有门禁,前两天突然在某安软件市场看到一个可以模拟门禁卡的软件,然而可能是我的手机系统太6了,竟然模拟不了,无奈自己动手,从根本上解决问 ...
- ASP.NET的SEO: 服务器控件背后——SEO友好的Html和JavaScript
本系列目录 假设你需要从一个页面转向其他页面,下面有很多种方式,你是如何选择的呢?你能清晰的说明理由么? <%--链接的表现形式--%> <asp:Hy ...
- java 命令行操作
打包命令 例如:将testproject下的文件打包成test.war jar cvf test.war ./testproject
- MVC_学习笔记_2_Authorize
MVC5_学习笔记_2_Authorize/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) *//* Author: Nico ...
- Linux 64位编译\链接32位程序
测试机器:Ubuntu14.04 64位 gcc编译32位程序,添加参数-m32: $ gcc -c -fno-builtin -m32 TinyHelloWorld.c ld链接32位代码,添加参数 ...
- 502 bad gateway 错误
在安装好使用过程中出现502问题,一般是因为默认php-cgi进程是5个,可能因为phpcgi进程不够用而造成502,需要修改/usr/local/php/etc/php-fpm.conf 将其中的m ...
- NSDateFormatter中时间格式串的含义
a: AM/PM (上午/下午) A: 0~86399999 (一天的第A微秒) c/cc: 1~7 (一周的第一天, 周天为1) ccc: Sun/Mon/Tue/Wed/Thu/Fri/Sat ( ...
- JS焦点图 上下翻动 支持IE6
<div class="slider"> <ul class="num" id="homePushName"> &l ...
- 常见的HTTP错误总结
一般来说HTTP2XX,代表请求正常完成,HTTP3XX代表网站重定向,HTTP4XX,代表客户端出现错误,HTTP5XX,代服务器端出现了错误 HTTP301:请求的数据具有新的位置 HTTP302 ...
- Cassandra 计数器counter类型和它的限制
文档基础 Cassandra 2.* CQL3.1 翻译多数来自这个文档 更新于2015年9月7日,最后有参考资料 作为Cassandra的一种类型之一,Counter类型算是限制最多的一个.Coun ...