ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

时间:2022-08-29 22:22:33

ASP.NET MVC深入浅出(被替换)

 

一. 谈情怀-ASP.NET体系

  从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模式,ViewState功不可没,通过的控件的拖拽和绑定,很快就可以搭建出来一个Web项目,其开发速度远胜Java、PHP,当年Web项目并不很重视体验,没有今天响应式,没有各种前端js框架,所以在当年的WebForm,微软是以引以为豪的。

  该框架毕竟有时代局限性,随着前端的崛起,随着人们对项目体验的提高,随着项目的体量的增大,WebForm在当下强调松耦合、模块化、组件化的时代,略显落后,故微软于2009年推出第一个开源框架,即MVC框架,俗称ASP.Net MVC1,后面又陆续的推出 MVC2、3、4、5。现在最新的跨平台Web开发框架 Core MVC,它隶属于.Net Core框架,而非 .Net FrameWork下。

  下面用一张图表示一下在Core出现之前,.Net在Web开发即ASP.NET中包含的内容。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

   简单分析一下:WebPages(没用过哎)、WebForms、MVC均为ASP.NET下的Web开发框架,WebAPI负责构建HTTP常规服务,SignalR负责构建实时服务。

   MVC和WebAPI简单对比一下:MVC用来构建网站既关心页面又关心数据,WebAPI只关心数据;MVC通过可以通过Action的名字来接受CURD操作,而WebAPI通过HTTP的访问方式(GET、PUT、POST、DELETE)来表达不同的CURD操作。

  (WebService、WCF、WebAPI、SignalR在后面会有单独的章节详细介绍)

二. WebForm和MVC对比

1. WebForm

  做过WebForm开发的朋友们应该很清楚,WebForm开发形式主要分为三种:

  ①:服务器端控件,即拖拽控件(aspx)

  ②:一般处理程序(ashx) + html模板

  ③:html静态页 + ajax + 一般处理程序(ashx)

  请求模型:

  ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

  WebForm的几个臭名昭著的缺点:封装太强,导致开发不灵活;ViewState的无状态;声明周期模型复杂,对于初学者不易于理解。

2. MVC

  我们经常从各种开发工程师(JAVA 、.NET、PHP)口中听到MVC,但不同人说的MVC貌似类似,但又有区别,那么MVC到底是什么呢?(类似的还有MVP和MVVM)

  1. 从宏观上来说:MVC是框架分层的一种搭建思想,在最原始的项目中,没有什么框架分层之说,所有的项目代码都在一个层里,这样会导致代码冗杂,耦合性强,项目迭代升级困难,MVC是一种分层思想,将一个项目代码分为几类,分别放到不同的层里,Model层存储一些数据和业务逻辑,View层处理页面问题,Controller层用来接收人机交互指令。MVC分层思想和传统的三层(数据库访问层、业务逻辑层、表现层)还是有区别的。

  MVC的核心是分离了View和Model(即页面和数据),Controller负责接收和发送指令,交给Model层处理。

  2. 从.Net的角度来说,MVC是微软提供一个Web开发框架,全称ASP.NET MVC,它是传统三层中View层的一种功能细分,一种表现形式而已,与三层没有直接关系。ASP.NET MVC框架中的开发流程围绕:

  ① Model:存储实体类,实现系统中的业务逻辑。

  ② View:页面展示(Razor模板),展现给用户。

  ③ Controller:页面的逻辑处理,用来与用户的交互,处理接受的Http请求。然后从Model中获取相应的数据,转发给View进行页面展示。

 补充:通过Controller中的Action,可以充当APP的服务端接口的作用,和WebAPI达到的效果一致,但访问方式是有区别的。

  请求模型:

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

  下面列举MVC框架的几个典型的优点:

  ①:松耦合,M-V-C分工明确,有利于代码的维护。

  ②:便于Seo优化,能自定义url的生成规则。

  ③:ASP.NET MVC5是个出色的表现层框架。

  ④:对单元测试支持友好。

三. ASP.Net MVC 框架

1. 包含的技术

  ASP.NET MVC框架,这里以MVC5为例,涉及到的知识有:Model-View-Controller的使用、Area和Global的理解、路由配置、数据传递的方式、AOP思想的体现(4大过滤器),各种Result、Razor语法、HttpContext相关的五大对象、分布视图、特性的补充、Html扩展控件、Bundles压缩js和css的原理、Owin约束、管道模型及MVC的工作原理。

   以及最新的Core MVC框架的相关知识。

2. 常见的约定

  ①:控制器命名通常以Controller结尾。

  ②:一个控制器对应一个View视图文件夹,常用页面都存放在里面。

四. 系列章节

第一节:走进MVC的世界(未完成)

第二节:各种路由约束(动态路由、静态路由、组合路由、正则约束、命名空间约束、区域内路由)

第三节:Action向View传值的四种方式(ViewData、ViewBag、TempData、Model)

第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法

第五节:从源码的角度理解MVC中各种Result(ActionResult、JsonResult、JavaScriptResult等)---待补充

第六节:Razor引擎及相关语法(未完成)

第七节:HtmlHelper及其扩展(未完成)

第八节:layout和partialView、RenderPartial和Partial、 action和RenderAction(未完成)

第九节:从源码的角度分析MVC中的一些特性及其用法

第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)

第十一节:Bundles压缩合并js和css及原理分析

第十二节:MVC中的一些特殊优化

第十二节:管道模型(未完成)

第十三节:HttpHander扩展及应用(自定义扩展名、图片防盗链)

第十四节:再探MVC中路由的奥秘

第十五节:HttpContext五大核心对象的使用(Request、Response、Application、Server、Session)

第十六节:利用MVC的扩展完成“一个后台,多套前端页面”的解决方案(未完成)

第十七节:XXX

第十八节:XXX

第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式

 

一. 背景

  说起EF的增删改操作,相信很多人都会说,有两种方式:① 通过方法操作  和  ② 通过状态控制。

  相信你在使用EF进行删除或修改操作的时候,可能会遇到以下错误:“ The object cannot be deleted because it was not found in the ObjectStateManager”,通过百度查询,说是没有进行上下文的附加,需要attach一下,那么都哪些情况需要附加,哪些是不需要附加的呢?

  在本章节,将结合EF的两种方式,从EF本地缓存的角度来详细介绍EF的增删改情况。

二. EF核心结论

  经过无数次摸爬滚打,详细的代码测试,整理出下面关于EF增删改操作的结论。

1. 总纲

  SaveChangs的时候一次性保存本地属性状态的全部变化.(换言之:只有本地缓存属性的状态发生变化了,SaveChanges才会实际生效)

补充:这里的属性状态的变化是存在于服务器端,一定不要理解为存在于本地,这也是为什么EF上下文不能用单例创建了。

  EF的本地缓存属性的三种形式:

    ①.通过Attach附加.

    ②.通过EF的即时查询,查询出来的数据,自动就本地缓存了.

    ③.通过状态控制. eg:Added、Modified、Deleted. (db.Entry(sl).State = EntityState.Added;)

2. EF的增删改操作的操作有两种形式

(一). 通过方法来操控

  a. 增加1个实体.   Add() 不需要Attach()附加.(当然附加了也不受影响)

  b. 增加1个集合.   AddRange() 不需要Attach()附加.(当然附加了也不受影响)

  c. 删除. Remove().   分两种情况:

    特别注意:如果数据为空,会报错.所以在实际开发过程中,要采用相应的业务逻辑进行处理.

    ①:自己创建了一个实体(非查询出来的),必须先Attach,然后Remove.

    ②:访问数据库,即时查询出来的数据(已经放到EF本地缓存里了),可以省略Attach,直接Remove(当然附加了也不受影响)

  d. 修改(如果数据主键不存在,执行增加操作). AddOrUpdate(),可以省略Attach,直接AddOrUpdate.

    需要引用程序集:using System.Data.Entity.Migrations;

    ①: 如果是执行增加操作,不需要进行Attach附加,但附加了Attach不受影响

    ②:如果是执行修改操作,不能进行Attach的附加,附加了Attach将导致修改失效,saveChange为0(无论是自己创建的或即时查询出来的,都不能进行Attach的附加)

  e. 修改. 不需要调用任何方法.

    该种方式如果实体为空,SaveChanges时将报错.

    ①:自己创建对象→先Attach(根据主键来区分对象)→然后修改属性值→最后saveChange

    ②: EF即时查询对象(自动本地缓存)→然后修改属性值→最后saveChange

(二). 通过修改本地属性的状态来操控.

  (该种方式本身已经改变了本地缓存属性了,所以根本不需要Attach附加)

  a. 增加. db.Entry(sl).State = EntityState.Added;

  b. 删除. db.Entry(sl).State = EntityState.Deleted;

    特别注意:如果数据为空,会报错.所以在实际开发过程中,要采用相应的业务逻辑进行处理.

    ①.适用于自己创建对象(根据主键来确定对象),然后删除的情况.

    ②.适用于即时查询出来的对象,然后进行删除的情况.

  c. 修改. db.Entry(sl).State = EntityState.Modified;

      特别注意:如果数据为空,会报错.所以在实际开发过程中,要采用相应的业务逻辑进行处理.

      ①.适用于自己创建对象(根据主键来确定对象),然后修改的情况.

      ②.适用于即时查询出来的对象,然后修改的情况.

三. 实战操练

1. 增加方法(Add和AddRange)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 private static void ADD()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. Add()方法-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = Guid.NewGuid().ToString("N"),
11                     txt1 = "t1",
12                     txt2 = "t2"
13                 };
14                 // db.Set<TestInfor>().Attach(tInfor);    //特别注意Add方法前不需要进行Attach状态的附加,当然附加了也不会出错.
15                 db.Set<TestInfor>().Add(tInfor);
16                 int n = db.SaveChanges();
17                 Console.WriteLine("数据作用条数:" + n);
18             }
19             using (DbContext db = new CodeFirstModel())
20             {
21                 Console.WriteLine("---------------------------2. AddRange()方法-------------------------------------");
22                 //监控数据库SQL情况
23                 //db.Database.Log += c => Console.WriteLine(c);
24                 List<TestInfor> tList = new List<TestInfor>()
25                 {
26                     new TestInfor()
27                     {
28                         id = Guid.NewGuid().ToString("N"),
29                         txt1 = "t11",
30                         txt2 = "t22"
31                     },
32                     new TestInfor()
33                     {
34                         id = Guid.NewGuid().ToString("N"),
35                         txt1 = "t11",
36                         txt2 = "t22"
37                     },
38                     new TestInfor()
39                     {
40                         id = Guid.NewGuid().ToString("N"),
41                         txt1 = "t11",
42                         txt2 = "t22"
43                     }
44                 };
45                 //特别注意AddRange方法前不需要进行Attach状态的附加,当然附加也不错.
46                 foreach (var item in tList)
47                 {
48                     db.Set<TestInfor>().Attach(item);
49                 }
50                 db.Set<TestInfor>().AddRange(tList);
51                 int n = db.SaveChanges();
52                 Console.WriteLine("数据作用条数:" + n);
53             }
54         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

2. 删除方法(先Attach-后Remove)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Delete1()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. Remove()方法 (调用Attach状态附加)-------------------------------------");
 6                 //监控数据库SQL情况
 7                 //db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = "11",  //实际测试的时候要有这条id的数据才能去测试哦
11                 };
12                 /*
13                  * 特别注意1:Remove方法删除必须调用Attach进行状态的附加,如果不附加将报下面的错误。
14                  * The object cannot be deleted because it was not found in the ObjectStateManager.
15                  * 特别注意2:直接使用状态的方式进行删除,db.Entry(tInfor).State = EntityState.Deleted; 是不需要进行attach附加的
16                  * 该种方式在后面进行测试讲解
17                  * 特别注意3:无论是Remove凡是还是直接状态的方式,如果传入的删除的数据为空,会报错抛异常
18                  */
19
20                 db.Set<TestInfor>().Attach(tInfor);     //如果注释掉该句话,则报错
21                 db.Set<TestInfor>().Remove(tInfor);
22
23                 int n = db.SaveChanges();
24                 Console.WriteLine("数据作用条数:" + n);
25             }
26         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

3. 删除方法(先查询→后Remove删除)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1   private static void Delete2()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------3. Remove()方法 (调用Attach状态附加)-------------------------------------");
 6                 int n;
 7                 //监控数据库SQL情况
 8                 //db.Database.Log += c => Console.WriteLine(c);
 9                 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault();
10                 /*
11                  * 特别注意1:对于先查询(即时查询,查出来放到了EF的本地缓存里),后删除,这种情况可以省略Attach状态的附加。
12                  * 因为查出来的数据已经放在EF的本地缓存里了,相当于已经附加了,无须再次附加(当然附加也不报错)
13                  */
14                 if (tInfor == null)
15                 {
16                     n = 0;
17                 }
18                 else
19                 {
20                     //db.Set<TestInfor>().Attach(tInfor);     //对于先查询(即时查询,查出来放到了EF的本地缓存里),后删除,这种情况省略该句话,仍然有效
21                     db.Set<TestInfor>().Remove(tInfor);
22                     n = db.SaveChanges();
23                 }
24                 Console.WriteLine("数据作用条数:" + n);
25             }
26         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

4. 修改(AddOrUpdate)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Update1()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. AddOrUpdate()方法-------------------------------------");
 6                 Console.WriteLine("-------------------------测试增加和自己创建数据的修改情况-----------------------------");
 7                 //监控数据库SQL情况
 8                 // db.Database.Log += c => Console.WriteLine(c);
 9                 TestInfor tInfor = new TestInfor()
10                 {
11                     id = "123",
12                     txt1 = "马茹",
13                     txt2 = "马茹2"
14                 };
15                 /*
16                  特别注意AddOrUpdate方法前不需要进行Attach状态的附加
17                  * 如果是执行增加操作,不需要附加Attach,附加了Attach不受影响
18                  * 如果是执行修改操作,不能附加Attach,附加了Attach将导致修改失效,saveChange为0
19                  */
20                 //db.Set<TestInfor>().Attach(tInfor);
21                 db.Set<TestInfor>().AddOrUpdate(tInfor);
22                 int n = db.SaveChanges();
23                 Console.WriteLine("数据作用条数:" + n);
24             }
25             using (DbContext db = new CodeFirstModel())
26             {
27                 Console.WriteLine("-------------------------测试即时查询出来的数据的修改情况-----------------------------");
28                 //监控数据库SQL情况
29                 // db.Database.Log += c => Console.WriteLine(c);
30                 TestInfor tInfor =db.Set<TestInfor>().Where(u=>u.id=="123").FirstOrDefault();
31                 tInfor.txt1="ypf11";
32                 /*
33                  即时查询出来的数据,调用AddorUpdate方法执行修改操作
34
35                  * 如果是执行修改操作,不需要进行Attach的附加,附加了Attach将导致修改失效,saveChange为0
36                  */
37                 db.Set<TestInfor>().Attach(tInfor);
38                 db.Set<TestInfor>().AddOrUpdate(tInfor);
39                 int n = db.SaveChanges();
40                 Console.WriteLine("数据作用条数:" + n);
41             }
42         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

5. 修改(自己创建对象,然后attach附加→修改属性值→SaveChanges)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Update2()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. attach附加→修改属性值→SaveChanges-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = "123"
11                 };
12
13                 /*
14                  特别注意1:该方式为自己创建对象(对象中必须要有主键值),然后通过attach附加,然后修改属性值,最后保存SaveChange。可以实现修改操作.
15                  特别注意2:该种方式如果实体为空,SaveChanges时将报错.
16                  */
17                 db.Set<TestInfor>().Attach(tInfor);
18                 tInfor.txt1 = "ypf1";
19
20                 int n = db.SaveChanges();
21                 Console.WriteLine("数据作用条数:" + n);
22             }
23         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

6. 修改(即时查询→修改属性值→SaveChanges)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Update3()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. 即时查询→修改属性值→SaveChangess-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "123").FirstOrDefault();
 9
10                 /*
11                  特别注意1:EF即时查询出来一个对象(自动保存到本地缓存了),然后修改属性值,最后保存SaveChange。可以实现修改操作.
12                  特别注意2:该种方式如果实体为空,SaveChanges时将报错.
13                  */
14                 tInfor.txt1 = "ypf333";
15
16                 int n = db.SaveChanges();
17                 Console.WriteLine("数据作用条数:" + n);
18             }
19         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

7. 增加方法(EntityState.Added)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void ADD2()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. EntityState.Added-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = Guid.NewGuid().ToString("N"),
11                     txt1 = "t1",
12                     txt2 = "t2"
13                 };
14                 db.Entry(tInfor).State = EntityState.Added;
15                 int n = db.SaveChanges();
16                 Console.WriteLine("数据作用条数:" + n);
17             }
18         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

8. 删除方法(EntityState.Deleted-自己创建的对象)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Delete3()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------EntityState.Deleted-自己创建的对象-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = "122",
11                 };
12                 db.Entry(tInfor).State = EntityState.Deleted;
13                 int n = db.SaveChanges();
14                 Console.WriteLine("数据作用条数:" + n);
15             }
16
17         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

9. 删除方法(EntityState.Deleted-即时查询的对象)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Delete4()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------EntityState.Deleted-即时查询的对象-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "123").FirstOrDefault();
 9                 db.Entry(tInfor).State = EntityState.Deleted;
10                 int n = db.SaveChanges();
11                 Console.WriteLine("数据作用条数:" + n);
12             }
13
14         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

10. 修改(自己创建对象,然后Modified→SaveChanges)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void Update4()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. 自己创建对象,然后Modified→SaveChanges-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = new TestInfor()
 9                 {
10                     id = "1",
11                     txt1 = "ypf1",
12                     txt2="ypf1"
13                 };
14                 db.Entry(tInfor).State = EntityState.Modified;
15                 int n = db.SaveChanges();
16                 Console.WriteLine("数据作用条数:" + n);
17             }
18         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

11. 修改(即时查询→修改属性值→然后Modified→SaveChanges)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 private static void Update5()
 2         {
 3             using (DbContext db = new CodeFirstModel())
 4             {
 5                 Console.WriteLine("---------------------------1. 即时查询→修改属性值→SaveChanges-------------------------------------");
 6                 //监控数据库SQL情况
 7                 // db.Database.Log += c => Console.WriteLine(c);
 8                 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "2").FirstOrDefault();
 9
10                 tInfor.txt1 = "ypf2";
11                 tInfor.txt2 = "ypf2";
12
13                 db.Entry(tInfor).State = EntityState.Modified;
14                 int n = db.SaveChanges();
15                 Console.WriteLine("数据作用条数:" + n);
16             }
17         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )

 

一. 前言

  在前面的两个章节中,我们分别详细介绍了EF的增删改的两种方式(方法和状态)和EF查询的两种方式( Lambda和Linq ),进行到这里,可以说对于EF,已经入门了,本来应该继续往下进行EF的高级属性,但本章节要打断一下,俗话所得好,“做人不能忘本”,应用到开发领域,就是“编码也不能忘本”,我们原始的SQL语句,在本章节将结合EF的调用,进行复习一番。

  本章节我们要达到的目标是:

    ① 掌握基本的单表和多表SQL操作(单表操作复习多表操作复习)

    ② 加深SQL重点关键字的使用(去复习

    ③ SQL参数化的写法

    ④ EF如何调用各类SQL语句

二. 两大核心方法

1. 从程序集的角度分析

  EF调用SQL语句,主要是依赖于DbContext→DataBase类下SqlQuery和ExecuteSqlCommand两个方法,来处理查询的SQL语句、增删改或其它的SQL语句。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

  ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

2. 封装思想

  结合泛型方法和参数化查询,将这两个方法进行简单封装一下,方便后面代码的调用。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1        #region 01-封装增删改或其他的方法
 2         /// <summary>
 3         /// 封装增删改或其他的方法
 4         /// </summary>
 5         /// <param name="db">数据库连接上下文</param>
 6         /// <param name="sql">数据库sql语句</param>
 7         /// <param name="paras">参数化参数</param>
 8         /// <returns></returns>
 9         public static int ExecuteSqlCommand(DbContext db, string sql, params SqlParameter[] paras)
10         {
11             return db.Database.ExecuteSqlCommand(sql, paras);
12         }
13         #endregion
14
15         #region 02-执行查询操作(结果为集合)
16
17         public static List<T> SqlQueryList<T>(DbContext db, string sql, params SqlParameter[] paras)
18         {
19             return db.Database.SqlQuery<T>(sql, paras).ToList();
20         }
21         #endregion
22
23         #region 03-执行查询操作(结果为单一实体)
24
25         public static T SqlQuery<T>(DbContext db, string sql, params SqlParameter[] paras)
26         {
27             return db.Database.SqlQuery<T>(sql, paras).FirstOrDefault();
28         }
29         #endregion
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

补充参数化查询:

  目的:防止SQL注入。 那么什么是SQL注入呢?

  SQL 注入漏洞存在的原因,就是拼接 SQL 参数。也就是将用于输入的查询参数,直接拼接在 SQL 语句中,导致了SQL 注入漏洞。

     举例:String sql = "select * from user where id=" + id;   该局sql语句的目的是通过id来查询用户信息,id的值传入进入。

  SQL注入的写法:id值传为:      2 or 1=1    ,由于 1=1 始终为true,加上or的配合,可以将所有的user信息查出来。

  以上还是比较温柔的:下面来个狠的,比如: 2; truncate table user      这种破坏就有点可怕了。

  参数化查询的写法:

  String sql = "select * from user where id=@id" ;

  SqlParameter[] paras ={ 

              new SqlParameter("@id","2"),
                               }; 这种写法,就有效的阻止SQL注入的风险。

三. 代码实践

  下面结合代码,展示EF调用SQL语句进行查询、增加、修改、删除、删除表所有数据的相关操作。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1           using (DbContext db = new CodeFirstModel())
 2             {
 3                 //1. 查询TestInfor表中的所有数据
 4                 Console.WriteLine("-----------------------------1. 查询TestInfor表中的所有数据----------------------------------");
 5                 string sql1 = @"select * from TestInfor";
 6                 List<TestInfor> tList = EFSqlTest.SqlQueryList<TestInfor>(db, sql1);
 7                 tList.ForEach(t =>
 8                 {
 9                     Console.WriteLine("id值为{0},txt1值为{1},txt2值为{2}", t.id, t.txt1, t.txt2);
10                 });
11
12                 //2. 查询TestInfor表中ID值为2的数据txt1和txt2
13                 Console.WriteLine("-----------------------------2. 查询TestInfor表中ID值为2的数据----------------------------------");
14                 string sql2 = @"select * from TestInfor where id=@id ";
15                 SqlParameter[] paras ={
16                                           new SqlParameter("@id","2"),
17                                      };
18                 TestInfor T2 = EFSqlTest.SqlQuery<TestInfor>(db, sql2, paras);
19                 if (T2!=null)
20                 {
21                     Console.WriteLine("id值为{0},txt1值为{1},txt2值为{2}", T2.id, T2.txt1, T2.txt2);
22                 }
23                 else
24                 {
25                     Console.WriteLine("没有查出符合条件的数据");
26                 }
27
28
29                 //3. 增加一条数据
30                 Console.WriteLine("-----------------------------3. 增加一条数据----------------------------------");
31                 string sql3 = @"insert into TestInfor values(@id,@txt1,@txt2)";
32                 SqlParameter[] paras3 ={
33                                          new SqlParameter("@id",Guid.NewGuid().ToString("N")),
34                                          new SqlParameter("@txt1","txt1+"+Guid.NewGuid().ToString("N").Substring(1,2)),
35                                          new SqlParameter("@txt2","txt2+"+Guid.NewGuid().ToString("N").Substring(1,2))
36                                     };
37                 int result3 = EFSqlTest.ExecuteSqlCommand(db, sql3, paras3);
38                 if (result3 > 0)
39                 {
40                     Console.WriteLine("增加成功");
41                 }
42
43                 //4. 删除一条数据
44                 Console.WriteLine("-----------------------------4. 删除一条数据----------------------------------");
45                 string sql4 = @"delete from TestInfor where id=@id";
46                 SqlParameter[] paras4 ={
47                                            new SqlParameter("@id","1"),
48                                      };
49                 int result4 = EFSqlTest.ExecuteSqlCommand(db, sql4, paras4);
50                 if (result4 > 0)
51                 {
52                     Console.WriteLine("删除成功");
53                 }
54                 else
55                 {
56                     Console.WriteLine("没有查到相应的数据进行删除");
57                 }
58
59                 //5. 修改一条数据
60                 Console.WriteLine("-----------------------------5. 修改一条数据----------------------------------");
61                 string sql5 = @"update TestInfor set txt1=@txt1 where id=@id";
62                 SqlParameter[] paras5 ={
63                                            new SqlParameter("@id","2"),
64                                            new SqlParameter("@txt1","limaru")
65                                       };
66                 int result5 = EFSqlTest.ExecuteSqlCommand(db, sql5, paras5);
67                 if (result5 > 0)
68                 {
69                     Console.WriteLine("修改成功");
70                 }
71                 else
72                 {
73                     Console.WriteLine("没有查到相应的数据进行修改");
74                 }
75
76                 //6. 删除表中的所有数据
77                 Console.WriteLine("-----------------------------6. 删除表中的所有数据----------------------------------");
78                 string sql6 = @"truncate table LoginRecords";
79                 //执行成功的话 result6仍然为0
80                 int result6 = EFSqlTest.ExecuteSqlCommand(db, sql6);
81
82             }        
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

思考:EF执行多条SQL语句,如何控制事务一体呢?

  在后续EF事务章节将详细介绍该问题的解决方案,敬请期待。

第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法

 

一. 背景

  上一个章节,介绍了EF调用两类SQL语句,主要是借助 ExecuteSqlCommand  和 SqlQuery 两个方法来完成,在本章节主要是复习几类存储过程的写法和对应的EF调用这几类存储过程的写法,另外介绍EF的DBFirst模式的下EF存储过程的特有写法。

  本章要达到以下几个目标:

  ① 熟练掌握存储过程的相关概念和几类写法(去复习)

  ② EF各种模式调用各种存储过程的通用写法(也是借助  ExecuteSqlCommand  和 SqlQuery )

  ③ EF的DBFirst模式事先映射好存储过程,简洁调用的写法

二. EF调用存储过程

  EF调用存储过程通用的写法,分两类:  

  ① 对于查询相关的存储过程,调用 SqlQuery 方法

  ② 对于增删改或其他的存储过程,调用 ExecuteSqlCommand 方法

1. 不含任何参数(查询类的存储过程)

  直接调用SqlQuery方法进行操作。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
1 if (exists (select * from sys.objects where name = 'GetAll'))
2     drop proc GetAll
3 go
4     create proc GetAll
5 as
6     select * from TestOne;
7
8 -- 调用
9 exec GetAll;
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
1       private static void NewMethod(DbContext db)
2         {
3             Console.WriteLine("---------------------------------1. 测试查询所有数据(不含输入参数)----------------------------------------");
4             List<TestOne> tList = db.Database.SqlQuery<TestOne>("GetAll").ToList();
5             foreach (var item in tList)
6             {
7                 Console.WriteLine("id为:{0},t1为:{1},t2为:{2}", item.id, item.t1, item.t2);
8             }
9         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

2. 含多个输入参数(查询类的存储过程)

  调用SqlQuery方法进行操作,传入参数的使用要使用SqlParameter参数化的方式进行传入,特别注意:调用时,存储过程的名字后面的参数 必须按照SqlParameter中的先后顺序来写

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 if (exists (select * from sys.objects where name = 'GetALLBy'))
 2     drop proc GetALLBy
 3 go
 4     create proc GetALLBy(
 5         @id varchar(32),
 6         @t1 varchar(32)
 7     )
 8 as
 9     select * from TestOne where id=@id and t1=@t1;
10
11 exec GetALLBy @id='1',@t1='2';
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1      private static void NewMethod2(DbContext db)
 2         {
 3             Console.WriteLine("---------------------------------2. 测试根据指定条件查询数据(含输入参数)----------------------------------------");
 4             SqlParameter[] para ={
 5                                            new SqlParameter("@id","1"),
 6                                            new SqlParameter("@t1","txt1")
 7                                   };
 8             //调用的时,存储过程的名字后面的参数 必须按照SqlParameter中的先后顺序来写
 9             List<TestOne> tList = db.Database.SqlQuery<TestOne>("GetALLBy @id,@t1", para).ToList();
10             foreach (var item in tList)
11             {
12                 Console.WriteLine("id为:{0},t1为:{1},t2为:{2}", item.id, item.t1, item.t2);
13             }
14         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

3. 增删改的存储过程(含1个输入参数)

  调用 ExecuteSqlCommand  方法来执行,针对输入参数,要采用SqlParameter的方式来进行传参数

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 if (exists (select * from sys.objects where name = 'DoSome'))
 2     drop proc DoSome
 3 go
 4     create proc DoSome(
 5         @id varchar(32)
 6     )
 7 as
 8     begin transaction
 9  begin try
10         truncate table [dbo].[TestOne];
11         insert into TestOne values(@id,'1','2');
12         delete from TestOne where id='2'
13     commit transaction
14  end try
15  begin catch
16     rollback transaction
17  end catch
18
19  exec DoSome 1
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
  private static void NewMethod3(DbContext db)
        {
            Console.WriteLine("---------------------------------3. 测试根据指定条件查询数据(含输入参数)----------------------------------------");
            SqlParameter[] para ={
                                           new SqlParameter("@id",Guid.NewGuid().ToString("N")),
                                     };
            int n = db.Database.ExecuteSqlCommand("DoSome @id", para);
            if (n > 0)
            {
                Console.WriteLine("操作成功");
            }
            else
            {
                Console.WriteLine("没有更多数据进行处理");
            }

        }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

4. 带输出参数的存储过程的调用,待补充?

三. DBFirst模式快捷调用存储过程

  前面介绍的调用存储过程的方法是通用模式,无论EF的哪种模式都可以使用,这里将介绍DBFirst模式的快捷调用,原理即创建的时候将存储过程映射进来了,所以可以直接调用。如下图:

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

1. 不含任何参数(查询类存储过程)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 -- 1.无参存储过程(查询)
 2 if (exists (select * from sys.objects where name = 'GetAll'))
 3     drop proc GetAll
 4 go
 5     create proc GetAll
 6 as
 7     select * from DBTestOne;
 8
 9 -- 调用
10 exec GetAll;
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
1    private static void DBNewMethod(EFDB3Entities db)
2         {
3             Console.WriteLine("---------------------------------1. 测试查询所有数据(不含输入参数)----------------------------------------");
4             var tList = db.GetAll().ToList();
5             foreach (var item in tList)
6             {
7                 Console.WriteLine("id为:{0},t1为:{1},t2为:{2}", item.id, item.t1, item.t2);
8             }
9         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

2. 含多个输入参数(查询类存储过程)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 --2. 有参数的存储过程(查询)
 2 if (exists (select * from sys.objects where name = 'GetALLBy'))
 3     drop proc GetALLBy
 4 go
 5     create proc GetALLBy(
 6         @id varchar(32),
 7         @t1 varchar(32)
 8     )
 9 as
10     select * from DBTestOne where id=@id and t1=@t1;
11
12 exec GetALLBy @id='1',@t1='2';
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
1      private static void DBNewMethod2(EFDB3Entities db)
2         {
3             Console.WriteLine("---------------------------------2. 测试根据指定条件查询数据(含输入参数)----------------------------------------");
4             var tList = db.GetALLBy("11", "1").ToList();
5             foreach (var item in tList)
6             {
7                 Console.WriteLine("id为:{0},t1为:{1},t2为:{2}", item.id, item.t1, item.t2);
8             }
9         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

3. 增删改存储过程(含1个输入参数)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1 --3. 增删改的一组过程
 2 if (exists (select * from sys.objects where name = 'DoSome'))
 3     drop proc DoSome
 4 go
 5     create proc DoSome(
 6         @id varchar(32)
 7     )
 8 as
 9     begin transaction
10  begin try
11         truncate table [dbo].[DBTestOne];
12         insert into DBTestOne values(@id,'1','2');
13         delete from DBTestOne where id='2'
14     commit transaction
15  end try
16  begin catch
17     rollback transaction
18  end catch
19
20  go
21  exec DoSome 1
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  private static void DBNewMethod3(EFDB3Entities db)
 2         {
 3             Console.WriteLine("---------------------------------3. 测试根据指定条件查询数据(含输入参数)----------------------------------------");
 4             int n = db.DoSome("33");
 5             if (n > 0)
 6             {
 7                 Console.WriteLine("操作成功");
 8             }
 9             else
10             {
11                 Console.WriteLine("没有更多数据进行处理");
12             }
13
14         }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

4. 带有输出参数,待补充

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  --4. 含输出参数的存储过程
 2
 3  if (exists (select * from sys.objects where name = 'GetT1Value'))
 4     drop proc GetT1Value
 5 go
 6     create proc GetT1Value(
 7         @t1 varchar(32),
 8         @t2 varchar(32) output
 9     )
10 as
11     select @t2=t2 from DBTestOne where t1=@t1;
12
13 go
14 declare @myT2 varchar(32);
15 exec GetT1Value '1',@myT2 output;
16 select @myT2 as fk;
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)

 

一. 简介

  上一个章节中,也介绍了立即加载和延迟加载,但上一个章节是针对单表而言的,不含外键,立即也好,延迟也好,都是指单表中的数据。但本章节重点介绍的三种加载方式均是针对含(导航属性、外键)的情况下,查询主表,从表中的数据加载情况。

  下面准备两张表:Student和StudentAddress两张表,一对一 or 零 的关系,实体结构如下,通过CodeFirst来反向生成数据库。

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1     /// <summary>
 2     /// 学生表(一个学生只能有一个地址或没有地址)
 3     /// </summary>
 4     public class Student
 5     {
 6         public Student()
 7         {
 8
 9         }
10         public string studentId { get; set; }
11
12         public string studentName { get; set; }
13
14
15         public virtual StudentAddress StudentAddress { get; set; }
16     }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1     /// <summary>
 2     /// 学生地址表(一个地址只能对应一个学生)
 3     /// </summary>
 4     public class StudentAddress
 5     {
 6         public StudentAddress()
 7         {
 8
 9         }
10
11         [ForeignKey("stu")]
12         //特别注意这个地方,stu对应下面的 Student stu;
13         //另外特别注意:studentAddressId,符合默认的Id生成规则,自动映射成主键,否则需要加【key】特性
14         public string studentAddressId { get; set; }
15
16         public string addressName { get; set; }
17
18         public virtual Student stu { get; set; }
19     }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

二. Lazy Loading

1. 又名:延迟加载、懒加载

2. 需要满足的条件:

  ①:poco类是public且不能为sealed

  ②:导航属性需要标记为Virtual

  满足以上两个条件,EF6默认就为延迟加载的模式。(默认:db.Configuration.LazyLoadingEnabled = true; )

3. 含义:每次调用子实体(外键所在的实体)的时候,才去查询数据库. 主表数据加载的时候,不去查询外键所在的从表。

4. 关闭延迟加载的办法: db.Configuration.LazyLoadingEnabled = false;

  特别注意:关闭延迟加载后,查询主表数据时候,主表的中从表实体为null.

1. 延迟加载代码测试

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1                 using (dbContext1 db = new dbContext1())
 2                 {
 3                     Console.WriteLine("--------------------------- 01-延迟加载 -----------------------------");
 4                     db.Database.Log = Console.WriteLine;
 5
 6                     //EF默认就是延迟加载,默认下面的语句就是true,所以下面语句注释没有任何影响
 7                     //db.Configuration.LazyLoadingEnabled = true;
 8
 9                     var list = db.Student.ToList();  //此处加载的数据,根据监测得出结论,没有对从表进行任何查询操作
10                     foreach (var stu in list)
11                     {
12                         Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName);
13
14                         //下面调用导航属性(一对一的关系)  每次调用时,都要去查询数据库(查询从表)
15                         var stuAddress = stu.StudentAddress;
16                         Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName);
17                     }
18                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

2. 关闭延迟加载

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1                 using (dbContext1 db = new dbContext1())
 2                 {
 3                     try
 4                     {
 5                         Console.WriteLine("--------------------------- 02-关闭延迟加载 -----------------------------");
 6                         db.Database.Log = Console.WriteLine;
 7
 8                         //下面的语句为关闭延迟加载
 9                         db.Configuration.LazyLoadingEnabled = false;
10
11                         var list = db.Student.ToList();  //关闭延迟加载后,此处从表实体StudentAddress为null,后面不会再次查询了
12                         foreach (var stu in list)
13                         {
14                             Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName);
15
16                             //StudentAddress为null,不会再次查询数据库,所以此处报错
17                             var stuAddress = stu.StudentAddress;
18                             Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName);
19                         }
20                     }
21                     catch (Exception ex)
22                     {
23                         Console.WriteLine(ex.Message);
24                     }
25                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

三. Eager Loading

1. 又名:立即加载、贪婪加载、预加载

2. 使用步骤:

  ①:先关闭延迟加载:db.Configuration.LazyLoadingEnabled = false;

   ②:查询主表的同时通过Include把从表数据也查询出来:

3. 含义:由于查询主表的时候通过Include已经一次性将数据查询了出来,所以在调用从表数据的时候,均从缓存中读取,无须查询数据库

代码测试

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1           using (dbContext1 db = new dbContext1())
 2                 {
 3                     Console.WriteLine("--------------------------- 03-立即加载 -----------------------------");
 4                     db.Database.Log = Console.WriteLine;
 5
 6                     //1.关闭延迟加载
 7                     db.Configuration.LazyLoadingEnabled = false;
 8
 9                     //2. 获取主表数据的同时,通过Include将从表中的数据也全部加载出来
10                     var list = db.Student.Include("StudentAddress").ToList();
11                     foreach (var stu in list)
12                     {
13                         Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName);
14
15                         //这里获取从表中的数据,均是从缓存中获取,无需查询数据库
16                         var stuAddress = stu.StudentAddress;
17                         Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName);
18                     }
19
20                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

四. Explicit Loading

1. 又名:显示加载

2. 背景:关闭延迟加载后,单纯查询主表的数据,后面又想再次查询从表,这个时候就需要用到显示加载了.

3. 前提:

  ①:关闭了延迟加载:db.Configuration.LazyLoadingEnabled = false;

  ②:单纯查询了主表,没有使用Include函数关联查询从表.

4. 使用步骤:

  ①:单个实体用:Reference

  ②:集合用:Collection

  ③:最后需要Load一下

5. 含义:关闭了延迟加载,单纯查询了主表数据,这个时候需要重新查询从表数据,就要用到显式加载了

代码测试

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1          using (dbContext1 db = new dbContext1())
 2                 {
 3                     Console.WriteLine("--------------------------- 04-显式加载 -----------------------------");
 4                     db.Database.Log = Console.WriteLine;
 5
 6                     //1.关闭延迟加载
 7                     db.Configuration.LazyLoadingEnabled = false;
 8
 9                     //2.此处加载的数据,不含从表中的数据
10                     var list = db.Student.ToList();
11                     foreach (var stu in list)
12                     {
13                         Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName);
14
15                         //3.下面的这句话,可以开启重新查询一次数据库
16                         //3.1 单个属性的情况用Refercence
17                         db.Entry<Student>(stu).Reference(c => c.StudentAddress).Load();
18                         //3.2 集合的情况用Collection
19                         //db.Entry<Student>(stu).Collection(c => c.StudentAddress).Load();
20
21                         //下面调用导航属性(一对一的关系)  每次调用时,都要去查询数据库
22                         var stuAddress = stu.StudentAddress;
23                         Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName);
24                     }
25
26                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

第十节: EF的三种追踪实体状态变化方式(DBEntityEntry、ChangeTracker、Local)

 

一. 简介

  我们在前面章节介绍EF基本增删改的时候,曾说过EF的SaveChanges()方法,会一次性的将所有的实体的状态变化统一提交到数据库,那么你是否想过EF的实体会有哪些状态变化呢?什么原因会导致其变化呢?我们怎么来追踪EF的实体变化呢?本章节以追踪EF实体状态变化的三种方式为线索,一一介绍上面疑问。

  废不多说,还是先把整理的结论贴出来,然后配合代码来介绍。

1. EF的实体状态总共有5种:Added、Deleted、Modified、Detached、unChanged

   ①. unChanged:属性值与数据库中属性值相同,没有发生任何变化,首次查询出来的实体的状态值为unChanged

  ②. Modified:实体中的某些属性的值或所有属性值与数据库中的发生了变化

    A:从数据库中查询出来的实体,直接改实体属性的值, 可以将实体状态改为 Modified。

    B:自己创建的实体,必须先Attach一下,直接改实体属性的值,才可以将实体状态改为 Modified。

  ③. Added: 实体将由上下文跟踪,但是在数据库中还不存在,

    Add()和 AddRange方法可以将实体状态变为:Added

  ④. Deleted:实体将由上下文跟踪并存在于数据库中,但是已被标记为在下次调用 SaveChanges 时从数据库中删除。

     A:从数据库中查询出来的实体,通过Remove方法, 可以将实体状态改为 Deleted。

     B:自己创建的实体,必须先Attach一下,然后Remove方法,才可以将实体状态改为 Deleted。

  ⑤. Detached: 调用AsNoTracking方法,取消实体状态追踪

2. 追踪方法一:DbEntityEntry 追踪单个实体的状态

   ①. db.Entry(XXX).State 来获取实体的状态

   ②. db.Entry(XXX).OriginalValues["txt1"] 可以获取属性的原始值

    ③. db.Entry(XXX).CurrentValues["txt1"] 可以获取属性的现在值

3. 追踪方法二:ChangeTracker 追踪EF上下文中所有实体的状态

  ①. db.ChangeTracker.Entries() 获取所有的实体状态变化 获取的是一个 IEnumerable<DbEntityEntry> 集合,遍历可以获取每个实体的状态变化

4. 追踪方法三:Local 获取单个实体状态发生增加、修改的实体集合(不含删除)

①. db.XXX.Local 获取的是XXX实体的集合

5. 删除状态和附加状态: AsNoTracking 和 Attach

二. 代码测试

1. 追踪方法一:DbEntityEntry  追踪单个实体的状态

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1        {
 2                     Console.WriteLine("-------------------追踪方法一:DbEntityEntry  追踪单个实体的状态--------------------------");
 3                     //修改前
 4                     var data1 = db.TestInfor.FirstOrDefault();
 5                     DbEntityEntry stata1 = db.Entry(data1);
 6                     Console.WriteLine("实体状态为:" + stata1.State);
 7                     //修改后
 8                     data1.txt1 = "fk";
 9                     DbEntityEntry stata2 = db.Entry(data1);
10                     Console.WriteLine("实体状态为:" + stata2.State);
11                     Console.WriteLine("txt1的原始值为" + stata2.OriginalValues["txt1"]);
12                     Console.WriteLine("txt1的现在值为" + stata2.CurrentValues["txt1"]);
13          }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

 分析:修改后实体状态由Unchanged→Modified了。

2. 追踪方法二:ChangeTracker  追踪EF上下文中所有实体的状态

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1  {
 2                     Console.WriteLine("-------------------追踪方法二:ChangeTracker  追踪EF上下文中所有实体的状态--------------------------");
 3                     //1.增加操作
 4                     db.TestInfor.Add(new TestInfor()
 5                     {
 6                         id = Guid.NewGuid().ToString("N"),
 7                         txt1 = "1",
 8                         txt2 = "2"
 9                     });
10
11                     //2. 删除操作
12                     var data = db.TestInfor.FirstOrDefault();
13                     db.TestInfor.Remove(data);
14
15                     //3. 修改操作
16                     var data2 = db.TestInfor.Where(u => u.id == "123").FirstOrDefault();
17                     data2.txt2 = "mr123";
18
19                     //4. 另外一个实体的增加操作
20                     db.TestInfor2.Add(new TestInfor2()
21                     {
22                         txt11 = "1",
23                         txt22 = "2"
24                     });
25
26                     List<DbEntityEntry> entityList = db.ChangeTracker.Entries().ToList();
27                     foreach (var item in entityList)
28                     {
29                         Console.WriteLine("实体状态为:" + item.State);
30                     }
31
32 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

3. 追踪方法三:Local 获取单个实体状态发生增加、修改的实体集合(不含删除)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1                 {
 2                     Console.WriteLine("-------------------追踪方法三:Local 获取单个实体状态发生增加、修改的实体集合(不含删除)--------------------------");
 3                     //1.增加操作
 4                     db.TestInfor.Add(new TestInfor()
 5                     {
 6                         id = Guid.NewGuid().ToString("N"),
 7                         txt1 = "1",
 8                         txt2 = "2"
 9                     });
10
11                     //2. 删除操作
12                     var data = db.TestInfor.FirstOrDefault();
13                     db.TestInfor.Remove(data);
14
15                     //3. 修改操作
16                     var data2 = db.TestInfor.Where(u => u.id == "123").FirstOrDefault();
17                     data2.txt2 = "mr123";
18
19                     //4. 另外一个实体的增加操作
20                     db.TestInfor2.Add(new TestInfor2()
21                     {
22                         txt11 = "1",
23                         txt22 = "2"
24                     });
25
26                     var EntityList = db.TestInfor.Local;
27
28                     foreach (var item in EntityList)
29                     {
30                         Console.WriteLine("实体的值分别为:{0},{1},{2}", item.id, item.txt1, item.txt2);
31                     }
32
33                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

 分析:这里的Local获取的是单个DBSet,发生了增加或修改操作,最终的获取的是实体集合。

4. 删除状态追踪和附加状态追踪(AsNoTracking 和 Attach)

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪
 1                 {
 2                     Console.WriteLine("-------------------删除状态和附加状态(AsNoTracking 和 Attach) --------------------------");
 3
 4                     //以修改为例测试状态
 5                     {
 6                         //1. 带状态追踪
 7                         var item = db.TestInfor.FirstOrDefault();
 8                         item.txt2 = "mr333+" + Guid.NewGuid().ToString("N").Substring(7);
 9                         Console.WriteLine("实体的状态为:" + db.Entry(item).State);
10                         int n = db.SaveChanges();
11                         if (n > 0)
12                         {
13                             Console.WriteLine("修改成功");
14                         }
15                         else
16                         {
17                             Console.WriteLine("没有相应的实体需要修改");
18                         }
19                     }
20                     {
21                         //2. 取消状态追踪
22                         var item = db.TestInfor.AsNoTracking().FirstOrDefault();
23                         item.txt2 = "mr333+" + Guid.NewGuid().ToString("N").Substring(10);
24                         Console.WriteLine("实体的状态为:" + db.Entry(item).State);
25                         int n = db.SaveChanges();
26                         if (n > 0)
27                         {
28                             Console.WriteLine("修改成功");
29                         }
30                         else
31                         {
32                             Console.WriteLine("没有相应的实体需要修改");
33                         }
34                     }
35                     {
36                         //3. 自己创建实体进行修改
37                         TestInfor item = new TestInfor()
38                         {
39                             id = "123"
40                         };
41                         db.TestInfor.Attach(item);
42                         item.txt1 = "fk3456";
43                         Console.WriteLine("实体的状态为:" + db.Entry(item).State);
44                         int n = db.SaveChanges();
45                         if (n > 0)
46                         {
47                             Console.WriteLine("修改成功");
48                         }
49                         else
50                         {
51                             Console.WriteLine("没有相应的实体需要修改");
52                         }
53                     }
54                 }
ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

ASP.NET MVC深入浅出(被替换)  第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式  第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )  第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法  第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性)   第十节: EF的三种追踪

ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪的更多相关文章

  1. 第四节&colon; EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法

    一. 背景 上一个章节,介绍了EF调用两类SQL语句,主要是借助 ExecuteSqlCommand  和 SqlQuery 两个方法来完成,在本章节主要是复习几类存储过程的写法和对应的EF调用这几类 ...

  2. 第三节&colon; EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery &rpar;

    一. 前言 在前面的两个章节中,我们分别详细介绍了EF的增删改的两种方式(方法和状态)和EF查询的两种方式( Lambda和Linq ),进行到这里,可以说对于EF,已经入门了,本来应该继续往下进行E ...

  3. ASP&period;NET MVC深入浅出系列&lpar;持续更新&rpar; ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结&lpar;3&rpar;&lpar;C&num;6&period;0和C&num;7&period;0新语法&rpar; 第三节:深度剖析各类数据结构&lpar;Array、List、Queue、Stack&rpar;及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节&colon; 总结Quartz&period;Net几种部署模式&lpar;IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  4. 第五节&colon; EF高级属性&lpar;一&rpar; 之 本地缓存、立即加载、延迟加载&lpar;不含导航属性&rpar;

    一. 本地缓存 从这个章节开始,介绍一下EF的一些高级特性,这里介绍的首先介绍的EF的本地缓存,在前面的“EF增删改”章节中介绍过该特性(SaveChanges一次性会作用于本地缓存中所有的状态的变化 ...

  5. 第一节&colon; 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式

    一. 背景 说起EF的增删改操作,相信很多人都会说,有两种方式:① 通过方法操作  和  ② 通过状态控制. 相信你在使用EF进行删除或修改操作的时候,可能会遇到以下错误:“ The object c ...

  6. OracleHelper&lpar;对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装&rpar;

    公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...

  7. Mybatis——SQL语句构建器类

    SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句.这么来做通常是由于SQL语句需要动态来生成-否则可以将它们放到外部文件或者存储过程中.正如你已经看到的 ...

  8. Oracle的sql语句的两种判断形式

    Oracle的sql语句的两种判断形式 判断当前列同时改动当前列 判断一个情况改动其他值 一类情况详解:实现的是当num这一列的值为3时,就显示好 以此类推 1)case num when 3 the ...

  9. Java-MyBatis:MyBatis 3 &vert; SQL 语句构建器类

    ylbtech-Java-MyBatis:MyBatis 3 | SQL 语句构建器类 1.返回顶部 1. SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL ...

随机推荐

  1. Some practices to write better C&num;&sol;&period;NET code(译)

    C#(.NET)中有关编码的一些建议,原文地址:http://www.codeproject.com/Articles/539179/Some-practices-to-write-better-Cs ...

  2. eclipse不能打断点的问题

    今天突然eclipse不能打断点了,按ctrl+左键也不能进行方法导向了.查了很多资料还是不清楚怎么回事. 我把原来的文件再重新复制下,这个副本竟然是正常的. 结论:把原来的文件重新编译生成class ...

  3. 有关C,C&plus;&plus;&comma;C&num;&comma; Java的图形图像处理类库 整理(未完待续)

    1.Java相关 1.1 Jzy3D Jzy3D 是一个Java的类库,用来绘制各种各样的三维图形,如下图所示: 下载地址:jzy3d-api,官网 1.2 Proscene 是一个用于创建交互式3D ...

  4. 用MSBuild和Jenkins搭建持续集成环境 - 转

    http://www.infoq.com/cn/articles/MSBuild-1 http://www.infoq.com/cn/articles/MSBuild-2 MSBuild是在.NET ...

  5. 浅谈c&num;接口的问题,适合新手来了解

    这段时间的项目有用到接口,开始不是特别理解接口,只是单单知道接口定义非常简单,甚至觉得这个接口只是多此一举(个人开发的时候).现在开始团队开发,才发现接口原来是这么的重要和便捷! 接下来就来谈谈我这段 ...

  6. PHP set&lowbar;error&lowbar;handler&lpar;&rpar; 函数

    定义和用法 set_error_handler() 函数设置用户自定义的错误处理函数. 该函数用于创建运行时期间的用户自己的错误处理方法. 该函数会返回旧的错误处理程序,若失败,则返回 null. 语 ...

  7. iOS 使用NJKWebViewProgress做webview进度条

    NJKWebViewProgress地址:https://github.com/ninjinkun/NJKWebViewProgress 导入头文件 #import "NJKWebViewP ...

  8. 不小心跳进安装Django-redis的坑

    为了写缓存这一块,我用pip install django-redis安装Django-redis. 它也把我的Django更新到最新了,我回头看下面的截图才发现的,把我的Django从1.8.2更新 ...

  9. js4

    数组的解构赋值: 位置对象的解构赋值: 名称 // {}在解构赋值的时候,不能出现在一行的最前面,否则js解析器会把他当成一个代码块,加()({a, b} = obj),告诉他这个仅仅是一个解构语句 ...

  10. CreateWindowEx failed &lpar;当前程序已使用了 Window 管理器对象的系统允许的所有句柄。&rpar;

    我在QT图形场景视图中通过QGraphicsProxyWidget添加代理Widget(实现添加基本的QT Widget,如按钮.复选框.日期时间控件等),当数量超过3500左右的时候,QT应用程序直 ...