1、ExecuteQuery方法

看命名,我们很容易联想到ado.net里熟悉的Command的ExecuteNonQuery方法,但是VS的智能提示告诉我们这个方法返回的是一个泛型集合,应该“所思非所得”。下面通过一个简单方法,验证我们的猜想(数据库设计可以参考这一篇):

  1. /// <summary>
  2. /// 直接执行sql语句,获取总人数
  3. /// </summary>
  4. /// <returns></returns>
  5. public int GetTotalCount()
  6. {
  7. string strSql = "SELECT COUNT(0) FROM Person(NOLOCK)";
  8. var query = dataContext.ExecuteQuery<int>(strSql);
  9. int result = query.First<int>();
  10. Console.WriteLine();
  11. Console.WriteLine("total count:{0}", result);
  12. return result;
  13. }

调试的时候,通过IntelliTrace跟踪到:

毫无疑问,上面的图片说明最初的想法是不正确的,”ADO.NET:执行Reader…”云云,让我们更加坚信它实际执行的应该是ExecuteReader方法。当然最简单的方法是直接查看它的方法说明:

  1. //
  2. // 摘要:
  3. // 直接对数据库执行 SQL 查询并返回对象。
  4. //
  5. // 参数:
  6. // query:
  7. // 要执行的 SQL 查询。
  8. //
  9. // parameters:
  10. // 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果某参数为
  11. // null,则该参数会转换为 DBNull.Value。
  12. //
  13. // 类型参数:
  14. // TResult:
  15. // 返回的集合中的元素的类型。
  16. //
  17. // 返回结果:
  18. // 由查询返回的对象的集合。
  19. public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters);

ExecuteQuery方法还有一个非泛型方法:

  1. //
  2. // 摘要:
  3. // 直接对数据库执行 SQL 查询。
  4. //
  5. // 参数:
  6. // elementType:
  7. // 要返回的 System.Collections.Generic.IEnumerable<T> 的类型。使查询结果中的列与对象中的字段或属性相匹配的算法如下所示:如果字段或属性映射到特定列名称,则结果集中应包含该列名称。如果未映射字段或属性,则结果集中应包含其名称与该字段或属性相同的列。通过先查找区分大小写的匹配来执行比较。如果未找到匹配项,则会继续搜索不区分大小写的匹配项。如果同时满足下列所有条件,则该查询应当返回(除延迟加载的对象外的)对象的所有跟踪的字段和属性:T
  8. // 是由 System.Data.Linq.DataContext 显式跟踪的实体。System.Data.Linq.DataContext.ObjectTrackingEnabled
  9. // 为 true。实体具有主键。否则会引发异常。
  10. //
  11. // query:
  12. // 要执行的 SQL 查询。
  13. //
  14. // parameters:
  15. // 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果某参数为
  16. // null,则该参数会转换为 DBNull.Value。
  17. //
  18. // 返回结果:
  19. // 由查询返回的对象的 System.Collections.Generic.IEnumerable<T> 集合。
  20. public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters);

看它的参数需要多传递一个elementType,明显不如泛型方法简洁。

 

2、ExecuteCommand方法

同样道理,这个方法立刻让我们联想到(世界没有联想,生活将会怎样?),联想到,等等,不知联想到什么。然后我们看一下方法使用说明:

  1. //
  2. // 摘要:
  3. // 直接对数据库执行 SQL 命令。
  4. //
  5. // 参数:
  6. // command:
  7. // 要执行的 SQL 命令。
  8. //
  9. // parameters:
  10. // 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果任一参数为
  11. // null,则该参数会转换为 DBNull.Value。
  12. //
  13. // 返回结果:
  14. // 一个 int,表示所执行命令修改的行数。
  15. public int ExecuteCommand(string command, params object[] parameters);

到这里,看它的返回类型为int,表示执行命令修改的行数,这次很容易想到ExecuteNonQuery方法。对不对呢?通过下面的代码证明我们的设想:

  1. /// <summary>
  2. /// 直接执行sql语句 根据用户Id更新体重
  3. /// </summary>
  4. /// <param name="id">用户Id</param>
  5. /// <param name="destWeight">更新后的体重</param>
  6. /// <returns></returns>
  7. public int ModifyWeightById(int id, double destWeight)
  8. {
  9. string strSql = string.Format("UPDATE Person SET Weight={0} WHERE Id={1}", destWeight, id);
  10. int result = dataContext.ExecuteCommand(strSql);
  11. Console.WriteLine();
  12. Console.WriteLine("affect num:{0}", result);
  13. return result;
  14. }

调试过程中,通过IntelliTrace可以很清楚地捕获:

“ADO.NET:执行NonQuery…”基本可以断言我们的设想是正确的。

 

3、防止sql注入

1和2中,执行sql语句的两个方法都有一个params 类型的参数,我们又会想到ado.net非常重要的sql语句的参数化防止sql注入问题。下面通过一个方法,看看linq2sql可不可以防止sql注入。

(1)、直接执行拼接的sql语句(有风险)

  1. /// <summary>
  2. /// 直接执行sql语句 根据用户Id更新FirstName
  3. /// </summary>
  4. /// <param name="id">用户Id</param>
  5. /// <param name="destName">更新后的FirstName</param>
  6. /// <returns></returns>
  7. public int ModifyNameById(int id, string destName)
  8. {
  9. string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
  10. int result = dataContext.ExecuteCommand(strSql);
  11. Console.WriteLine();
  12. Console.WriteLine("affect num:{0}", result);
  13. return result;
  14. }

然后,在客户端这样调用这个方法:

  1. int result = ServiceFactory.CreatePersonService().ModifyNameById(10, "'Anders'");//更新id为10的人的FirstName

运行的时候,直接抛出异常,提示“Anders”附近有语法错误。毫无疑问,它执行的sql语句:

  1. UPDATE Person SET FirstName=''Anders'' WHERE Id=10

是不能通过的,同时证明了string.format函数不能有效防止sql注入。

(2)、直接通过linq方法的参数传递,结果如何呢?

改进(1)的传参方法:

  1. /// <summary>
  2. /// 直接执行sql语句 根据用户Id更新FirstName
  3. /// </summary>
  4. /// <param name="id">用户Id</param>
  5. /// <param name="destName">更新后的FirstName</param>
  6. /// <returns></returns>
  7. public int ModifyNameById(int id, string destName)
  8. {
  9. //string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
  10. //int result = dataContext.ExecuteCommand(strSql);
  11. string strSql = "UPDATE Person SET FirstName={0} WHERE Id={1}";
  12. int result = dataContext.ExecuteCommand(strSql, new object[] { destName, id });
  13. Console.WriteLine();
  14. Console.WriteLine("affect num:{0}", result);
  15. return result;
  16. }

再次通过如下的方式调用:

  1. int result = ServiceFactory.CreatePersonService().ModifyNameById(10, "'Anders'");//更新id为10的人的FirstName

这一次运行正常吗?经测试,真的真的是真的正常,终于可以长松一口气了。查看数据库,Id为10的那一条记录的FirstName结果改变成了“'Anders'”(带单引号),明白ado.net工作原理的都知道这个一点也不神奇,不是吗?

对比(1)和(2),我们发现还是用linq2sql的源生方法传参比较好,通过拼接sql应该尽量避免。

update:当我们把(2)中的sql语句第一个参数加上单引号,写成下面的形式:

  1. /// <summary>
  2. /// 直接执行sql语句 根据用户Id更新FirstName
  3. /// </summary>
  4. /// <param name="id">用户Id</param>
  5. /// <param name="destName">更新后的FirstName</param>
  6. /// <returns></returns>
  7. public int ModifyNameById(int id, string destName)
  8. {
  9. id = 1;
  10. //string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
  11. //int result = dataContext.ExecuteCommand(strSql);
  12. string strSql = "UPDATE Person SET FirstName='{0}' WHERE Id={1}";//如果第一个参数加上单引号,结果又不同了
  13. int result = dataContext.ExecuteCommand(strSql, new object[] { destName, id });
  14. Console.WriteLine();
  15. Console.WriteLine("affect num:{0}", result);
  16. return result;
  17. }

虽然运行也正常,但是数据库中Id为10的那一条记录的FirstName结果改变成了”@p0”,也就是说使用linq的方法,拼接sql传参的地方不需要加单引号。

到这里,可能你会认为上面1、2、3全是废话,知道怎么用不就行了嘛?!其实很长一部分时间我也是这么想这么做的。工作久了,早就习惯了不求甚解,现在看书,看园子里高手的博客,多多少少会有点反思。

 

4、思考

ado.net中比较常用的ExecuteScalar方法,在linq2sql应该怎么实现呢?您尝试使用过了吗?

 

demo下载:demo

 

作者:Jeff Wong

出处:http://jeffwongishandsome.cnblogs.com/

本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。

linq直接执行sql语句的更多相关文章

  1. Entity Framework Code First执行SQL语句、视图及存储过程

    1.Entity Framework Code First查询视图 Entity Framework Code First目前还没有特别针对View操作的方法,但对于可更新的视图,可以采用与Table ...

  2. EFCore执行Sql语句的方法:FromSql与ExecuteSqlCommand

    前言 在EFCore中执行Sql语句的方法为:FromSql与ExecuteSqlCommand:在EF6中的为SqlQuery与ExecuteSqlCommand,而FromSql和SqlQuery ...

  3. Entity Framework Core 执行SQL语句和存储过程

    无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程

    这一节,我们来看看EF如何执行SQL语句与读取存储过程的数据,可能有一部分人,还不知道EF如何执行存储过程与原生SQL语句! 我们什么时候要直接使用原生的SQL语句? 返回值过于复杂 过于复杂的联合查 ...

  5. c#实现用SQL池(多线程),定时批量执行SQL语句 【转】

    在实际项目开发中,业务逻辑层的处理速度往往很快,特别是在开发Socket通信服务的时候,网络传输很快,但是一旦加上数据库操作,性能一落千丈,数据库操作的效率往往成为一个系统整体性能的瓶颈.面对这问题, ...

  6. MVC EF 执行SQL语句(转载)

    MVC EF 执行SQL语句 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 闲着没事,看了一篇关于LI ...

  7. Asp.Net MVC EF之一:使用Database类在EF框架中执行Sql语句

    引言 从EF6开始,增加了DateBase类,他通过从 DbContext 对象获取此类的实例.可用于管理支持数据库上下文或连接的实际数据库.这包括创建.删除和检查数据库的存在. 在6以前,我们使用E ...

  8. c#实现用SQL池(多线程),定时批量执行SQL语句

    在实际项目开发中,业务逻辑层的处理速度往往很快,特别是在开发Socket通信服务的时候,网络传输很快,但是一旦加上数据库操作,性能一落千丈,数据库操作的效率往往成为一个系统整体性能的瓶颈.面对这问题, ...

  9. easyui datagrid 禁止选中行 EF的增删改查(转载) C# 获取用户IP地址(转载) MVC EF 执行SQL语句(转载) 在EF中执行SQL语句(转载) EF中使用SQL语句或存储过程 .net MVC使用Session验证用户登录 PowerDesigner 参照完整性约束(转载)

    easyui datagrid 禁止选中行   没有找到可以直接禁止的属性,但是找到两个间接禁止的方式. 方式一: //onClickRow: function (rowIndex, rowData) ...

随机推荐

  1. ZOJ-3319

    Islands Time Limit: 1 Second      Memory Limit: 32768 KB There are N islands and some directed paths ...

  2. asp.net的Server.MapPath方法

    Server.MapPath()的功能: 返回与 Web 服务器上的指定虚拟路径相对应的物理文件路径. 命名空间: System.Web 程序集: System.Web(在 System.Web.dl ...

  3. 微信小程序radio组件 - 如何改变默认样式大小?

    今天在写小程序的时候用到radio组件,但是很懊恼并未提供修改radio组件大小属性,第一感觉准备用css width , height 改变radio的大小,但是怎么搞也无法改变. 但是又不愿意搞个 ...

  4. 利用CSS3伪类做3D按钮

    这是通过css3伪类实现的3d按钮,html代码为: <div id="container_buttons"> <p><a class="a ...

  5. 使用kubeadm安装k8s集群故障处理三则

    最近在作安装k8s集群,测试了几种方法,最终觉得用kubeadm应该最规范. 限于公司特别的网络情况,其安装比网上不能访问google的情况还要艰难. 慢慢积累经验吧. 今天遇到的三则故障记下来作参考 ...

  6. grunt 自定义任务实现js文件的混淆及加密

    //自定义任务 module.exports = function (grunt) { // 项目配置 var http = require('http'); var qs = require('qu ...

  7. OpenStack 计算服务 Nova介绍和控制节点部署 (八)

    一)nova在keystone上服务注册 1.1创建nova务实体 [root@controller ~]# source admin-openrc [root@controller ~]# open ...

  8. PTA L2-023 图着色问题-前向星建图 团体程序设计天梯赛-练习集

    L2-023 图着色问题 (25 分)   图着色问题是一个著名的NP完全问题.给定无向图,,问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色? 但本题并不是要你解 ...

  9. 深入了解HTTP协议

    1.HTTP协议简介 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议.所有的WWW文件都必须遵守这个标准. 1.1背景 设计HT ...

  10. go chapter 4 - 不定长参数

    https://www.cnblogs.com/chase-wind/p/5644838.html 空接口可以指向任何数据对象,所以可以使用interface{}定义任意类型变量,同时interfac ...