ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和Sql...

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中包含的内容。

   简单分析一下: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)

  请求模型:

  

  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达到的效果一致,但访问方式是有区别的。

  请求模型:

  下面列举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)

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

4. 修改(AddOrUpdate)

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

7. 增加方法(EntityState.Added)

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

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

复制代码
 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         }
复制代码

 

 

第三节: 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语句。

  

 

2. 封装思想

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

复制代码
 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
复制代码

补充参数化查询:

  目的:防止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语句进行查询、增加、修改、删除、删除表所有数据的相关操作。

复制代码
 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             }        
复制代码

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

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

 

 

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

 

一. 背景

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

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

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

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

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

 

二. EF调用存储过程

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

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

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

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

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

复制代码
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;
复制代码
复制代码
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         }
复制代码

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

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

复制代码
 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';
复制代码
复制代码
 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         }
复制代码

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

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

复制代码
 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
复制代码
复制代码
  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("没有更多数据进行处理");
            }

        }
复制代码

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

 

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

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

 

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

复制代码
 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;
复制代码
复制代码
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         }
复制代码

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

复制代码
 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';
复制代码
复制代码
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         }
复制代码

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

 

复制代码
 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
复制代码
复制代码
 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         }
复制代码

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

复制代码
 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;
复制代码

 

 

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

 

一. 简介

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

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

复制代码
 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     }
复制代码
复制代码
 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     }
复制代码

二. Lazy Loading

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

 2. 需要满足的条件:

   ①:poco类是public且不能为sealed

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

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

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

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

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

1. 延迟加载代码测试

复制代码
 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                 }
复制代码

2. 关闭延迟加载

复制代码
 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                 }
复制代码

三. Eager Loading

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

 2. 使用步骤:

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

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

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

代码测试

复制代码
 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                 }
复制代码

四. Explicit Loading

 1. 又名:显示加载

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

 3. 前提:

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

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

 4. 使用步骤:

   ①:单个实体用:Reference

   ②:集合用:Collection

   ③:最后需要Load一下

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

代码测试

复制代码
 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                 }
复制代码

 

 

第十节: 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  追踪单个实体的状态

复制代码
 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          }
复制代码

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

2. 追踪方法二:ChangeTracker  追踪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 }
复制代码

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

复制代码
 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                 }
复制代码

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

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

复制代码
 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                 }
复制代码

 

转载于:https://www.cnblogs.com/cjm123/p/9278340.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值