linq直接执行sql语句
1、ExecuteQuery方法
看命名,我们很容易联想到ado.net里熟悉的Command的ExecuteNonQuery方法,但是VS的智能提示告诉我们这个方法返回的是一个泛型集合,应该“所思非所得”。下面通过一个简单方法,验证我们的猜想(数据库设计可以参考这一篇):
/// <summary>
/// 直接执行sql语句,获取总人数
/// </summary>
/// <returns></returns>
public int GetTotalCount()
{
string strSql = "SELECT COUNT(0) FROM Person(NOLOCK)";
var query = dataContext.ExecuteQuery<int>(strSql);
int result = query.First<int>();
Console.WriteLine();
Console.WriteLine("total count:{0}", result);
return result;
}
调试的时候,通过IntelliTrace跟踪到:
![]()
毫无疑问,上面的图片说明最初的想法是不正确的,”ADO.NET:执行Reader…”云云,让我们更加坚信它实际执行的应该是ExecuteReader方法。当然最简单的方法是直接查看它的方法说明:
//
// 摘要:
// 直接对数据库执行 SQL 查询并返回对象。
//
// 参数:
// query:
// 要执行的 SQL 查询。
//
// parameters:
// 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果某参数为
// null,则该参数会转换为 DBNull.Value。
//
// 类型参数:
// TResult:
// 返回的集合中的元素的类型。
//
// 返回结果:
// 由查询返回的对象的集合。
public IEnumerable<TResult> ExecuteQuery<TResult>(string query, params object[] parameters);
ExecuteQuery方法还有一个非泛型方法:
//
// 摘要:
// 直接对数据库执行 SQL 查询。
//
// 参数:
// elementType:
// 要返回的 System.Collections.Generic.IEnumerable<T> 的类型。使查询结果中的列与对象中的字段或属性相匹配的算法如下所示:如果字段或属性映射到特定列名称,则结果集中应包含该列名称。如果未映射字段或属性,则结果集中应包含其名称与该字段或属性相同的列。通过先查找区分大小写的匹配来执行比较。如果未找到匹配项,则会继续搜索不区分大小写的匹配项。如果同时满足下列所有条件,则该查询应当返回(除延迟加载的对象外的)对象的所有跟踪的字段和属性:T
// 是由 System.Data.Linq.DataContext 显式跟踪的实体。System.Data.Linq.DataContext.ObjectTrackingEnabled
// 为 true。实体具有主键。否则会引发异常。
//
// query:
// 要执行的 SQL 查询。
//
// parameters:
// 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果某参数为
// null,则该参数会转换为 DBNull.Value。
//
// 返回结果:
// 由查询返回的对象的 System.Collections.Generic.IEnumerable<T> 集合。
public IEnumerable ExecuteQuery(Type elementType, string query, params object[] parameters);
看它的参数需要多传递一个elementType,明显不如泛型方法简洁。
2、ExecuteCommand方法
同样道理,这个方法立刻让我们联想到(世界没有联想,生活将会怎样?),联想到,等等,不知联想到什么。然后我们看一下方法使用说明:
//
// 摘要:
// 直接对数据库执行 SQL 命令。
//
// 参数:
// command:
// 要执行的 SQL 命令。
//
// parameters:
// 要传递给命令的参数数组。注意下面的行为:如果数组中的对象的数目小于命令字符串中已标识的最大数,则会引发异常。如果数组包含未在命令字符串中引用的对象,则不会引发异常。如果任一参数为
// null,则该参数会转换为 DBNull.Value。
//
// 返回结果:
// 一个 int,表示所执行命令修改的行数。
public int ExecuteCommand(string command, params object[] parameters);
到这里,看它的返回类型为int,表示执行命令修改的行数,这次很容易想到ExecuteNonQuery方法。对不对呢?通过下面的代码证明我们的设想:
/// <summary>
/// 直接执行sql语句 根据用户Id更新体重
/// </summary>
/// <param name="id">用户Id</param>
/// <param name="destWeight">更新后的体重</param>
/// <returns></returns>
public int ModifyWeightById(int id, double destWeight)
{
string strSql = string.Format("UPDATE Person SET Weight={0} WHERE Id={1}", destWeight, id);
int result = dataContext.ExecuteCommand(strSql);
Console.WriteLine();
Console.WriteLine("affect num:{0}", result);
return result;
}
调试过程中,通过IntelliTrace可以很清楚地捕获:
![]()
“ADO.NET:执行NonQuery…”基本可以断言我们的设想是正确的。
3、防止sql注入
1和2中,执行sql语句的两个方法都有一个params 类型的参数,我们又会想到ado.net非常重要的sql语句的参数化防止sql注入问题。下面通过一个方法,看看linq2sql可不可以防止sql注入。
(1)、直接执行拼接的sql语句(有风险)
/// <summary>
/// 直接执行sql语句 根据用户Id更新FirstName
/// </summary>
/// <param name="id">用户Id</param>
/// <param name="destName">更新后的FirstName</param>
/// <returns></returns>
public int ModifyNameById(int id, string destName)
{
string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
int result = dataContext.ExecuteCommand(strSql);
Console.WriteLine();
Console.WriteLine("affect num:{0}", result);
return result;
}
然后,在客户端这样调用这个方法:
int result = ServiceFactory.CreatePersonService().ModifyNameById(10, "'Anders'");//更新id为10的人的FirstName
运行的时候,直接抛出异常,提示“Anders”附近有语法错误。毫无疑问,它执行的sql语句:
UPDATE Person SET FirstName=''Anders'' WHERE Id=10
是不能通过的,同时证明了string.format函数不能有效防止sql注入。
(2)、直接通过linq方法的参数传递,结果如何呢?
改进(1)的传参方法:
/// <summary>
/// 直接执行sql语句 根据用户Id更新FirstName
/// </summary>
/// <param name="id">用户Id</param>
/// <param name="destName">更新后的FirstName</param>
/// <returns></returns>
public int ModifyNameById(int id, string destName)
{
//string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
//int result = dataContext.ExecuteCommand(strSql);
string strSql = "UPDATE Person SET FirstName={0} WHERE Id={1}";
int result = dataContext.ExecuteCommand(strSql, new object[] { destName, id });
Console.WriteLine();
Console.WriteLine("affect num:{0}", result);
return result;
}
再次通过如下的方式调用:
int result = ServiceFactory.CreatePersonService().ModifyNameById(10, "'Anders'");//更新id为10的人的FirstName
这一次运行正常吗?经测试,真的真的是真的正常,终于可以长松一口气了。查看数据库,Id为10的那一条记录的FirstName结果改变成了“'Anders'”(带单引号),明白ado.net工作原理的都知道这个一点也不神奇,不是吗?
对比(1)和(2),我们发现还是用linq2sql的源生方法传参比较好,通过拼接sql应该尽量避免。
update:当我们把(2)中的sql语句第一个参数加上单引号,写成下面的形式:
/// <summary>
/// 直接执行sql语句 根据用户Id更新FirstName
/// </summary>
/// <param name="id">用户Id</param>
/// <param name="destName">更新后的FirstName</param>
/// <returns></returns>
public int ModifyNameById(int id, string destName)
{
id = 1;
//string strSql = string.Format("UPDATE Person SET FirstName='{0}' WHERE Id={1}", destName, id);//这么拼接有风险
//int result = dataContext.ExecuteCommand(strSql);
string strSql = "UPDATE Person SET FirstName='{0}' WHERE Id={1}";//如果第一个参数加上单引号,结果又不同了
int result = dataContext.ExecuteCommand(strSql, new object[] { destName, id });
Console.WriteLine();
Console.WriteLine("affect num:{0}", result);
return result;
}
虽然运行也正常,但是数据库中Id为10的那一条记录的FirstName结果改变成了”@p0”,也就是说使用linq的方法,拼接sql传参的地方不需要加单引号。
到这里,可能你会认为上面1、2、3全是废话,知道怎么用不就行了嘛?!其实很长一部分时间我也是这么想这么做的。工作久了,早就习惯了不求甚解,现在看书,看园子里高手的博客,多多少少会有点反思。
4、思考
ado.net中比较常用的ExecuteScalar方法,在linq2sql应该怎么实现呢?您尝试使用过了吗?
demo下载:demo
作者:Jeff Wong
出处:http://jeffwongishandsome.cnblogs.com/
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。
linq直接执行sql语句的更多相关文章
- Entity Framework Code First执行SQL语句、视图及存储过程
1.Entity Framework Code First查询视图 Entity Framework Code First目前还没有特别针对View操作的方法,但对于可更新的视图,可以采用与Table ...
- EFCore执行Sql语句的方法:FromSql与ExecuteSqlCommand
前言 在EFCore中执行Sql语句的方法为:FromSql与ExecuteSqlCommand:在EF6中的为SqlQuery与ExecuteSqlCommand,而FromSql和SqlQuery ...
- Entity Framework Core 执行SQL语句和存储过程
无论ORM有多么强大,总会出现一些特殊的情况,它无法满足我们的要求.在这篇文章中,我们介绍几种执行SQL的方法. 表结构 在具体内容开始之前,我们先简单说明一下要使用的表结构. public clas ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程
这一节,我们来看看EF如何执行SQL语句与读取存储过程的数据,可能有一部分人,还不知道EF如何执行存储过程与原生SQL语句! 我们什么时候要直接使用原生的SQL语句? 返回值过于复杂 过于复杂的联合查 ...
- c#实现用SQL池(多线程),定时批量执行SQL语句 【转】
在实际项目开发中,业务逻辑层的处理速度往往很快,特别是在开发Socket通信服务的时候,网络传输很快,但是一旦加上数据库操作,性能一落千丈,数据库操作的效率往往成为一个系统整体性能的瓶颈.面对这问题, ...
- MVC EF 执行SQL语句(转载)
MVC EF 执行SQL语句 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 闲着没事,看了一篇关于LI ...
- Asp.Net MVC EF之一:使用Database类在EF框架中执行Sql语句
引言 从EF6开始,增加了DateBase类,他通过从 DbContext 对象获取此类的实例.可用于管理支持数据库上下文或连接的实际数据库.这包括创建.删除和检查数据库的存在. 在6以前,我们使用E ...
- c#实现用SQL池(多线程),定时批量执行SQL语句
在实际项目开发中,业务逻辑层的处理速度往往很快,特别是在开发Socket通信服务的时候,网络传输很快,但是一旦加上数据库操作,性能一落千丈,数据库操作的效率往往成为一个系统整体性能的瓶颈.面对这问题, ...
- easyui datagrid 禁止选中行 EF的增删改查(转载) C# 获取用户IP地址(转载) MVC EF 执行SQL语句(转载) 在EF中执行SQL语句(转载) EF中使用SQL语句或存储过程 .net MVC使用Session验证用户登录 PowerDesigner 参照完整性约束(转载)
easyui datagrid 禁止选中行 没有找到可以直接禁止的属性,但是找到两个间接禁止的方式. 方式一: //onClickRow: function (rowIndex, rowData) ...
随机推荐
- 属性名、变量名与 内部关键字 重名 加&
procedure TForm4.btn3Click(Sender: TObject); var MyQj: TQJson; MyPrinter: TPrinter; begin MyQj := TQ ...
- redis之(十一)redis实现缓存的功能
[一]redis实现缓存的原理 --->利用键的失效时间设置实现缓存技术 --->由于redis的内存有限,可以在redis的配置文件里设置maxmemory的参数.来限制redis最大可 ...
- python读写hdf5及cdf格式文件
Python write and read hdf5 file http://stackoverflow.com/questions/20928136/input-and-output-numpy-a ...
- Jmeter-----随机生成手机号后8位并去重,来进行注册手机号的压测
要求:对注册接口进行100000次压测,手机号已126开头,后面的8位数不限 前言:在进行测试中,我们需要对注册接口进行压测100000次,那么就要求手机号码每次填写的不一致,否则手机号使用一次后会出 ...
- ASP.NET MVC 视图学习,纯干货
最近用MVC专门为自己做了一个网站,用来记录文章心情和日记.加上和同事的一些交流感觉颇深.所以想把13年买的MVC 4高级编程重新看一遍,记录一些东西,以后应该用的到.视图总是被控制器渲染,因为控制器 ...
- windows10 易升 下载失败 解决方法
在你剩余最大空间的硬盘里有一个名字大概是Windows10Updata的文件夹里找到一个名字14339开头的升级镜像,把这个文件的名字用记事本保存下来方便以后使用,同时在这个文件夹里还有一个叫prod ...
- 【面试题】2018年最全Java面试通关秘籍汇总集!
[面试题]2018年最全Java面试通关秘籍汇总集!(转载于互联网) 前几天在交流群里有些小伙伴问面试相关的试题,当时给出了一些问题,苦于打字太累就没写下去了,但觉得这是一个很不负责任的表现,于是 ...
- react native 问题点
问题点一:安装了react-native-vector-icons后,编译出错 版本: "react": "16.2.0", "react-nativ ...
- C语言数据类型64位和32机器的区别
C语言编程需要注意的64位和32机器的区别 .数据类型特别是int相关的类型在不同位数机器的平台下长度不同.C99标准并不规定具体数据类型的长度大小,只规定级别.作下比较: 32位平台 char:1字 ...
- 牛客练习赛 29 E 位运算?位运算!(线段树)
题目链接 牛客练习赛29E 对$20$位分别建立线段树.首先$1$和$2$可以合起来搞(左移右移其实是等效的) 用个lazy标记下.转移的时候加个中间变量. $3$和$4$其实就是区间$01$覆盖操 ...