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视图文件夹,常用页面都存放在里面。
四. 系列章节
第二节:各种路由约束(动态路由、静态路由、组合路由、正则约束、命名空间约束、区域内路由)
第三节:Action向View传值的四种方式(ViewData、ViewBag、TempData、Model)
第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法
第五节:从源码的角度理解MVC中各种Result(ActionResult、JsonResult、JavaScriptResult等)---待补充
第八节:layout和partialView、RenderPartial和Partial、 action和RenderAction(未完成)
第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)
第十三节:HttpHander扩展及应用(自定义扩展名、图片防盗链)
第十五节:HttpContext五大核心对象的使用(Request、Response、Application、Server、Session)
第十六节:利用MVC的扩展完成“一个后台,多套前端页面”的解决方案(未完成)
第一节: 结合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 }
ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪的更多相关文章
-
第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法
一. 背景 上一个章节,介绍了EF调用两类SQL语句,主要是借助 ExecuteSqlCommand 和 SqlQuery 两个方法来完成,在本章节主要是复习几类存储过程的写法和对应的EF调用这几类 ...
-
第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )
一. 前言 在前面的两个章节中,我们分别详细介绍了EF的增删改的两种方式(方法和状态)和EF查询的两种方式( Lambda和Linq ),进行到这里,可以说对于EF,已经入门了,本来应该继续往下进行E ...
-
ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借
ASP.NET MVC深入浅出系列(持续更新) 一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...
-
第五节: EF高级属性(一) 之 本地缓存、立即加载、延迟加载(不含导航属性)
一. 本地缓存 从这个章节开始,介绍一下EF的一些高级特性,这里介绍的首先介绍的EF的本地缓存,在前面的“EF增删改”章节中介绍过该特性(SaveChanges一次性会作用于本地缓存中所有的状态的变化 ...
-
第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式
一. 背景 说起EF的增删改操作,相信很多人都会说,有两种方式:① 通过方法操作 和 ② 通过状态控制. 相信你在使用EF进行删除或修改操作的时候,可能会遇到以下错误:“ The object c ...
-
OracleHelper(对增删改查分页查询操作进行了面向对象的封装,对批量增删改操作的事务封装)
公司的一个新项目使用ASP.NET MVC开发,经理让我写个OracleHelper,我从网上找了一个比较全的OracleHelper类,缺点是查询的时候返回DataSet,数据增删改要写很多代码(当 ...
-
Mybatis——SQL语句构建器类
SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句.这么来做通常是由于SQL语句需要动态来生成-否则可以将它们放到外部文件或者存储过程中.正如你已经看到的 ...
-
Oracle的sql语句的两种判断形式
Oracle的sql语句的两种判断形式 判断当前列同时改动当前列 判断一个情况改动其他值 一类情况详解:实现的是当num这一列的值为3时,就显示好 以此类推 1)case num when 3 the ...
-
Java-MyBatis:MyBatis 3 | SQL 语句构建器类
ylbtech-Java-MyBatis:MyBatis 3 | SQL 语句构建器类 1.返回顶部 1. SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL ...
随机推荐
-
Some practices to write better C#/.NET code(译)
C#(.NET)中有关编码的一些建议,原文地址:http://www.codeproject.com/Articles/539179/Some-practices-to-write-better-Cs ...
-
eclipse不能打断点的问题
今天突然eclipse不能打断点了,按ctrl+左键也不能进行方法导向了.查了很多资料还是不清楚怎么回事. 我把原来的文件再重新复制下,这个副本竟然是正常的. 结论:把原来的文件重新编译生成class ...
-
有关C,C++,C#, Java的图形图像处理类库 整理(未完待续)
1.Java相关 1.1 Jzy3D Jzy3D 是一个Java的类库,用来绘制各种各样的三维图形,如下图所示: 下载地址:jzy3d-api,官网 1.2 Proscene 是一个用于创建交互式3D ...
-
用MSBuild和Jenkins搭建持续集成环境 - 转
http://www.infoq.com/cn/articles/MSBuild-1 http://www.infoq.com/cn/articles/MSBuild-2 MSBuild是在.NET ...
-
浅谈c#接口的问题,适合新手来了解
这段时间的项目有用到接口,开始不是特别理解接口,只是单单知道接口定义非常简单,甚至觉得这个接口只是多此一举(个人开发的时候).现在开始团队开发,才发现接口原来是这么的重要和便捷! 接下来就来谈谈我这段 ...
-
PHP set_error_handler() 函数
定义和用法 set_error_handler() 函数设置用户自定义的错误处理函数. 该函数用于创建运行时期间的用户自己的错误处理方法. 该函数会返回旧的错误处理程序,若失败,则返回 null. 语 ...
-
iOS 使用NJKWebViewProgress做webview进度条
NJKWebViewProgress地址:https://github.com/ninjinkun/NJKWebViewProgress 导入头文件 #import "NJKWebViewP ...
-
不小心跳进安装Django-redis的坑
为了写缓存这一块,我用pip install django-redis安装Django-redis. 它也把我的Django更新到最新了,我回头看下面的截图才发现的,把我的Django从1.8.2更新 ...
-
js4
数组的解构赋值: 位置对象的解构赋值: 名称 // {}在解构赋值的时候,不能出现在一行的最前面,否则js解析器会把他当成一个代码块,加()({a, b} = obj),告诉他这个仅仅是一个解构语句 ...
-
CreateWindowEx failed (当前程序已使用了 Window 管理器对象的系统允许的所有句柄。)
我在QT图形场景视图中通过QGraphicsProxyWidget添加代理Widget(实现添加基本的QT Widget,如按钮.复选框.日期时间控件等),当数量超过3500左右的时候,QT应用程序直 ...