前言

本文测试环境:VS2022+.Net7+MySQL

因为我想要实现使用EFCore去执行sql文件,所以就用到了方法ExecuteSqlAsync,然后就产生了下面的问题,首先因为方法接收的参数是一个FormattableString,它又是一个抽象类,所以我就瞎测试使用下面方式构建

using var db = new OpenDbContext();
var mysqlSql2 = "INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');";
var result = await db.Database.ExecuteSqlAsync($"{mysqlSql2}");

编译没有报错,但是一个运行,结果居然报错了

Unhandled exception. MySqlConnector.MySqlException (0x80004005): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default' at line 1

看着这个错误我一直以为是哪个name列的值写的有问题,去数据库执行,没问题成功添加,代码中那个值换了好几次,就是不行,翻翻微软文档

using (var context = new BloggingContext())
{
var rowsModified = context.Database.ExecuteSql($"UPDATE [Blogs] SET [Url] = NULL");
}

这不是和官方示例写的一样?难道是EFCore的bug?

寻找问题

抱着肯定不是EFCore bug的想法,查看源码吧

public static Task<int> ExecuteSqlAsync(
this DatabaseFacade databaseFacade,
FormattableString sql,
CancellationToken cancellationToken = default (CancellationToken))
{
return databaseFacade.ExecuteSqlRawAsync(sql.Format, (IEnumerable<object>) sql.GetArguments(), cancellationToken);
}

然后我就发现它源码里面还是从这个入参的sql中获取到对应的sql以及GetArguments,那么我就像提前构建一个FormattableString看下取到的值是多少

FormattableString mysqlSql = $"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');";

mysqlSql.Format.Dump();
mysqlSql.GetArguments().Dump();

这里的dump方法可以查看:此处

这不是也没问题吗,然后突然发现下面代码可以正常运行

using var db = new OpenDbContext();
FormattableString mysqlSql = $"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000')"; var result = await db.Database.ExecuteSqlAsync(mysqlSql);

那看来问题就出在ExecuteSqlAsync方法的入参上了,然后我这么测试

FormattableString mysqlSql = $"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');";

mysqlSql.Format.Dump();
mysqlSql.GetArguments().Dump(); FormattableString sql2 = $"{mysqlSql}";
sql2.Format.Dump();
sql2.GetArguments().Dump();

解决问题

到这里看来原因就出来了,是因为$的问题哦,那么解决方案就成先定义一个FormattableString类型直接传进入,或者

using var db = new OpenDbContext();
var name = "李四";
var result = await db.Database.ExecuteSqlAsync(
$"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '{name}', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');");
result.Dump();

不过这里需要注意,ExecuteSqlAsync方法里面的sql在EFCore中并没有给你放到一个事务里面,所以如果有需要,那么就只好自己创建事务了

using var db = new OpenDbContext();
var name = "李四";
using var tran = db.Database.BeginTransaction();
var result = await db.Database.ExecuteSqlAsync(
$"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '{name}', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');");
tran.Commit();
result.Dump();

未完

虽然解决了那个报错的问题,但是还是没解决我想执行sql文件,那只好换个方法去写了,自己去获取连接然后操作ADO.NET去执行吧(这里暂且先不用Dapper),我麻溜写下下面示例代码,顺带考虑到那个要裹在一个事务里面的情况(未封装,仅供参考)

// 模拟sql文件
var mysqlSql = @"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');
INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', 'error情况', '2023-10-08 17:26:47.000000');"; using var db = new OpenDbContext();
using var connection = db.Database.GetDbConnection();
using var tran = db.Database.BeginTransaction();
var cmd = connection.CreateCommand();
cmd.CommandText = mysqlSql;
int i = await cmd.ExecuteNonQueryAsync();
await tran.CommitAsync();
i.Dump();

运行居然报错:The transaction associated with this command is not the connection's active transaction ,还好报错中给了一个文档网站,网站中说我应该这么操作,将我开启的事务传递给cmd变量,也就是

// error 不能将源类型 'Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction' 转换为目标类型 'System.Data.Common.DbTransaction
cmd.Transaction = tran;

一脸懵逼这俩都对不上咋给,然后在看tran.的时候手滑点了一下,出来一个

cmd.Transaction = tran.GetDbTransaction();

源码如下

public static DbTransaction GetDbTransaction(this IDbContextTransaction dbContextTransaction) => dbContextTransaction is IInfrastructure<DbTransaction> accessor ? accessor.GetInfrastructure<DbTransaction>() : throw new InvalidOperationException(RelationalStrings.RelationalNotInUse);

这不是巧了,修改上面的代码如下

var mysqlSql = @"INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '2023-10-08 17:26:45.000000', '2023-10-08 17:26:47.000000');
INSERT INTO test1008.menus (id, name, create_time, modify_time) VALUES (default, '张飒1', '20xxcfdsfs000', '2023-10-08 17:26:47.000000');"; using var db = new OpenDbContext();
using var connection = db.Database.GetDbConnection();
using var tran = db.Database.BeginTransaction();
var cmd = connection.CreateCommand();
cmd.CommandText = mysqlSql;
cmd.Transaction = tran.GetDbTransaction();
int i = await cmd.ExecuteNonQueryAsync();
await tran.CommitAsync();
i.Dump();

因为我的sql第二条是错误的,所以运行成功报错,数据库中也不存在数据,这就是想要的效果。

再次修改sql后执行成功,数据库存在两条数据,实现了我的需求,完成。

FormattableString 介绍

以下内容来自chatgpt

FormattableString 是 C# 中的一个类,用于支持可格式化字符串的操作。它是在 .NET Framework 4.6 版本中引入的。

FormattableString 类的目的是提供一种方便的方式来创建可格式化的字符串。它可以使用类似于字符串插值的语法,但不会立即进行字符串插值操作,而是保留可格式化字符串的原始形式和参数的值。这使得开发人员可以在稍后的时间点或其他上下文中决定如何格式化字符串,以便满足特定的需求。

在使用 FormattableString 时,可以通过使用 $ 符号前缀来创建一个可格式化字符串,例如:

FormattableString message = $"Hello, {name}. The current time is {DateTime.Now}.";

在EFCore中ExecuteSql方法使用该类型是用来防止SQL注入的

解决因对EFCore执行SQL方法不熟练而引起的问题的更多相关文章

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

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

  2. MySQL导入SQL文件过大或连接超时的解决办法/在navcat执行sql卡在0%

    set global max_allowed_packet=100 000 000;set global net_buffer_length=100000;SET GLOBAL  interactiv ...

  3. 解决乱码的方法是,在执行SQL语句之前,将MySQL以下三个系统参数设置为与服务器字符集character-set-server相同的字符集

    character-set-server/default-character-set:服务器字符集,默认情况下所采用的. character-set-database:数据库字符集. characte ...

  4. PLSQL执行sql语句输出的中文是???之解决方法和步骤

    方法/步骤 1 登陆plsql,执行sql语句,输出的中文标题显示成问号????:条件包含中文,则无数据输出 步骤阅读 2 输入sql语句select * from V$NLS_PARAMETERS查 ...

  5. C#执行Sql 时,出现“算术运算导致溢出”问题,如何解决?

    昨天在C#执行oracle的sql语句时,总是报错,原先在pl/sql 执行sql语句是可以的,在C#执行就报“算术运算导致溢出”问题 SQL语句 select A.SKU_ID 商品标识,A.COL ...

  6. MySQL 执行SQL脚本 报ERROR 1231 (42000)的解决办法【转】

    今天在source mysqldump 备份文件时,发现导入的过程中报如下的错误: ERROR 1231 (42000): Variable 'time_zone' can't be set to t ...

  7. ORACLE 查询不走索引的原因分析,解决办法通过强制索引或动态执行SQL语句提高查询速度

    (一)索引失效的原因分析: <>或者单独的>,<,(有时会用到,有时不会) 有时间范围查询:oracle 时间条件值范围越大就不走索引 like "%_" ...

  8. Jenkins工程中SQL语句执行的方法

    前言 网上很多jenkins工程中基于shell或批处理方式调用sql文件执行sql命令的方式,大部分都是需要基于sql文件来完成的,因此在sql语句发生变化时需要去jenkins服务端修改对应的sq ...

  9. MySQL命令执行sql文件的两种方法

    MySQL命令执行sql文件的两种方法 摘要:和其他数据库一样,MySQL也提供了命令执行sql脚本文件,方便地进行数据库.表以及数据等各种操作.下面笔者讲解MySQL执行sql文件命令的两种方法,希 ...

  10. 解决执行sql脚本报错:没有足够的内存继续执行程序。

    出现执行sql脚本报错:没有足够的内存继续执行程序.是因为sql脚本过大,大家可能分为多个文件多次执行,这种笨方法可行,不过比较麻烦,大家可以用下面的方式,利用sqlcmd一次就行了:   执行cmd ...

随机推荐

  1. 02-面试必会-SSM框架篇

    01-什么是 Spring IOC 和 DI ? IOC : 控制翻转 , 它把传统上由程序代码直接操控的对象的调用权交给容 器,通过容器来实现对象组件的装配和管理.所谓的"控制反转&quo ...

  2. 一篇随笔学会CSS

    CSS3 1.CSS介绍 HTML+CSS+JS 结构+表现+交互 2.CSS发展史 Cascading Style Sheet 层叠级联样式表 表现层:美化网页(字体.颜色.边距.宽高.网页定位.网 ...

  3. 将 -Xms 参数设置和-Xmx 参数的相等,对比 -Xms参数 设置为-Xmx 参数的一半,有哪些优势?

    将 -Xms 参数设置为与 -Xmx 参数相等,相比于将 -Xms 参数设置为 -Xmx 参数的一半,具有以下优势: 1. 程序启动时间更短 当将 -Xms 参数设置为与 -Xmx 参数相等时,JVM ...

  4. 1.1 Metasploit 工具简介

    Metasploit 简称(MSF)是一款流行的开源渗透测试框架,由Rapid7公司开发,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风 ...

  5. XTTS系列之三:中转空间的选择和优化

    通常选择XTTS做迁移的数据库都不会太小的,至少都是几T.几十T这样的规模,这种级别的数据量原有空间不够用,所以在迁移过程临时用作存放迁移数据库备份文件的空间也是需要提前考虑规划的问题. 最近就有客户 ...

  6. 浅析switch和if(开发中这两者的优缺点;分析出优缺点在使用就能更确定自己需要使用哪个函数了)

    分析 Switch 相较于 if 的优点 1.switch 执行效率  高于  if 的执行效率 分析: switch是在编译阶段将子函数的地址和判断条件绑定了,只要直接将a的直接映射到子函数地址去执 ...

  7. Java扩展Nginx之四:远程调试

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是<Java扩展Nginx> ...

  8. Hexo博客Next6.0版本主题配置(站内搜索、新建404界面、静态资源压缩、底部信息隐藏、各版块透明度修改、字数统计、推荐阅读、博文置顶、阅读进度、在线评论、运行时间)

    新建404界面 在站点根目录下,输入hexo new page 404,在默认Hexo站点下/source/404/index.md 打开新建的404界面,编辑属于自己的404界面,可以显示腾讯公益4 ...

  9. 使用react-test-renderer/shallow写测试

    我的项目是采用react + ts来写的,项目中要写单元测试,于是采用了Jest库,  主要用的package有 react-test-renderer react-test-renderer/sha ...

  10. CMU15-445 Project3 Query Execution心得

    Project3 Query Execution 心得 一.概述 首先要说:这个 project很有趣很硬核!从这个 project 开始才感觉自己在数据库方面真正成长了! 第一个 project : ...