记一次ORM模糊查询偶发报错

发布时间:2024年01月09日

0x00 前言

前几天生产服务使用的接口报错,本地测试、测试服没问题,预生产和生产报错,重新部署又好了领导那边又说是版本dll没更新上(发布是127,实际已经用上129版本),问题就搁置了(解决了就行)。忙了好几天,今天抽空查一下bug。

0x01 偶发报错的几种可能

1、线程安全报错
2、缓存丢失报错
3、上下文丢失报错
4、环境不同报错(本地调试是Debug,发布是Release)
5、其他报错(狗头保命)

0x02 demo场景复现

/// <summary>
/// 数组like测试
/// </summary>
private static async Task ArrayLikeTest()
{
    SqlSugarScope db = DbHelper.GetNewDbScope();
    var arrNums = Enumerable.Range(1, 1).Select(x => $"{x}").ToArray();
    var pageIndex = 1;
    var pageSize = 50;
    RefAsync<int> rowCount = 1;
    for (int i = 0; i < 100000; i++)
    {
        var sugarWhereExp = Expressionable.Create<Student03>();
        var isRandTrue = DateTime.Now.Ticks % 2 == 1;
        if (isRandTrue)
        {
            sugarWhereExp.And(p => arrNums.Any(x => p.Name.Contains(x)) || arrNums.Any(x => p.Name2.Contains(x)));
        }
        else
        {
            sugarWhereExp.And(p => arrNums.Contains(p.Name) || arrNums.Contains(p.Name2));
        }
        var tempWhereExp = sugarWhereExp.ToExpression();

        var r1 = await db.Queryable<Student03>("p").Where(tempWhereExp).ToPageListAsync(pageIndex,pageSize,rowCount);
    }
}

因为是偶发性,且部署的第一时间是没有问题,过了几天才有问题,所以demo使用%2取模做随机(生产环境是有模糊和精确两种查询);

可以看到两种sql查询都没问题,在数据库执行也没问题;

0x03 追捕不能重现的BUG

1、复现sql报错

通过预生产的环境报错,能清晰的知道sql是因为int类型转换字符串报错了,而且一线人员也将参数发给我看了,模糊查询的字符串是纯数字(不然你以为我demo直接写数字干嘛呢)

SELECT 1  FROM [Student03] [p]  WHERE (([Name] IN ('1')) OR ([Name2] IN ('1')) )
SELECT 1 FROM [Student03] [p]  WHERE ((  ([p].[Name] like '%'+ '1' +'%')  ) OR (  ([p].[Name2] like '%'+ '1' +'%')  ) )

--报错sql
SELECT 1  FROM [Student03] [p]  WHERE (([Name] IN (1)) OR ([Name2] IN (1)) )

SELECT 1 FROM [Student03] [p]  WHERE ((  ([p].[Name] like '%'+ 1 +'%')  ) OR (  ([p].[Name2] like '%'+ 1 +'%')  ) )

因为报错信息里面存在% 所以我们直接锁定模糊查询sql报错;

2、万能的F11步入调试法

因为是表达式目录树生成sql,我们在where时进行单步步入;

因为表达式一般都是递归解析,所以我们就不一步步看了,我们能知道的是like拼接报错,那么就一定是在数组解析时出现问题,我们直接看数组解析部分;

D:\Project\SqlSugar\Src\Asp.NetCore2\SqlSugar\ExpressionsToSql\ResolveItems\MethodCallExpressionResolve.cs

D:\Project\SqlSugar\Src\Asp.NetCore2\SqlSugar\ExpressionsToSql\ResolveItems\MethodCallExpressionResolve_Helper.cs

在众多的case中步入ListAny分支;

实体列的属性信息,这里因为是string,也默认取了其属性列做缓存;

这里有3个类型判断,guid、整型、日期;因为我们的string类型第一个属性是chars,字符串类型 所以进入了else的ToSqlValue方法,我们看看tosql做了什么;

这里也没问题,最终通过正则替换掉参数关键字(原先还以为是正则替换错了值导致的),生成模糊查询的sql;

3、思考问题

因为是一步步进方法的,所以最终的sql没问题 也没有找到可疑点;那么就不是bug?

我们仔细看看,因为缓存了string的属性列,那么有没有可能缓存被覆盖?答案肯定是否定的,orm自身缓存是通过类型的hashcode拼接的key;

那么有没有可能是列的问题?我们上面能够知道string缓存了两个列chars和length;看看列是怎么来的,通过反射获取属性;

(IndexerName会指定索引器名称,否则默认是Item)

我们通过il源码能够知道chars是字符串类型,length是整型;还记得我们看数组拼接的时候有个if判断吗?没错,我们都知道int数据类型查询是可以用单引号拼接,也可以不用;如:
SELECT 1 ?FROM [Student03] [p] ?WHERE p.Id='1' OR p.Id=1,结果是等效的;

那么如果此处是int类型会如何拼接?直接tostring!!!

也就是说 如果是int类型就会报错!

但是这里有两个列 第一列是chars【字符串类型】已经把结果的标识字符串给替换了,那么他可能会是因为这个原因报错吗?

难道就无解了吗?

0x04 峰回路转,杀回string,不要默认不可能!

两个列?string int?如果他的顺序是错误的,那么不就报错了?

typeof(string).GetProperties()反射获取属性的顺序可能会变?

我们试试!

var taskList=Enumerable.Range(1,1000).Select(e => Task.Run(TaskRun)).ToArray();
Task.WaitAll(taskList);

void TaskRun(){
	foreach (var element in Enumerable.Range(1, 1_000_000_000).AsParallel())
	{
		var s = typeof(string).GetProperties();
		var ar = s.Select(x => x.Name).ToArray();
		var ars = string.Join(",", ar);
		if (ars.StartsWith("Length"))
		{
			("首个属性是length" + ars).Dump();
			break;
		}
	}
}

结果是。。。。。。。。。。。。。。

没有出现奇迹。

真的无解了!!

问问我广大的群友

!!!!

上F12

因为项目是6.0版本,所以可能是中奖了

0x05 后续BUG复现及问题修复

1、因为BUG是偶发且不易复现,先写demo,不行再找个官方文档看看,确定一下

2、如果可能是此bug引起的问题,优先联系orm,提issue

3、顺序获取属性:

//1、根据token排序
PropertyInfo[] properties = typeof(string).GetProperties().OrderBy(x=>x.MetadataToken)

//2、标记属性

 public class Student03
 {
     [你的自定义属性(orderIndex=-1)]
     public int Id { get; set; }
     [你的自定义属性(orderIndex=2)]
     public string Name { get; set; }
     [你的自定义属性(orderIndex=3)]
     public string Name2 { get; set; }
 }

4、升级框架至7.0及以上

2024/01/09 9:48 复现截图

参考链接

允许 GetProperties 按声明顺序对结果进行排序 ·期刊 #46272 ·dotnet/运行时 (github.com)

typeof(可查询)。GetMethods() 以不同的顺序返回 .NET 5 和 .NET 6 的结果 ·期刊 #71422 ·dotnet/运行时 (github.com)

bug 给我死!(不确定的先F12,优先相信自己的怀疑)

文章来源:https://blog.csdn.net/u014690570/article/details/135460650
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。