dotnetcore / FreeSql

🦄 .NET aot orm, C# orm, VB.NET orm, Mysql orm, Postgresql orm, SqlServer orm, Oracle orm, Sqlite orm, Firebird orm, 达梦 orm, 人大金仓 orm, 神通 orm, 翰高 orm, 南大通用 orm, 虚谷 orm, 国产 orm, Clickhouse orm, DuckDB orm, TDengine orm, QuestDB orm, MsAccess orm.
https://freesql.net
MIT License
4.12k stars 857 forks source link

FromQuery T1,T2,T3 返回相同结构时,表关联异常的问题的问题 #1620

Closed ddabb closed 11 months ago

ddabb commented 1 year ago

数据库版本

sqlserver 2019

安装的Nuget包

package id="FreeSql" version="3.2.690" targetFramework="net4"

.net framework FromQuery T1,T2,T3识别的问题 已经在Freesql 的微信群里面反馈,仅做登记,跟进进展。

2881099 commented 11 months ago

可能当时比较忙没看到这个 issues

像这种复杂的问题,请提供一个能重现的 console app,我拿到后可以直接重现 debug

2881099 commented 11 months ago

您有空提供一下 demo,先关闭 issues

LeaFrock commented 4 months ago

@2881099 我也遇到了这个bug,希望重新打开该issue。

我可以给出一个相对小的复现代码,假设有一张UserInfo表:

    public class UserInfo
    {
        [Column(IsIdentity = true, IsPrimary = true)]
        public int Id { get; set; }

        public int Role { get; set; }

        public int Points { get; set; }
    }

复现代码如下:

        // int[] ids1 = [1, 3, 5], ids2 = [2, 4, 6];
        int id1 = 1, id2 = 2;
        var query1 = freeSql.Select<UserInfo>()
            //.Where(u => ids1.Contains(u.Id))
            .Where(u => u.Id == id1)
            .WithTempQuery(u => new
            {
                GroupNo = 6,
                Points = (int)SqlExt.Sum(u.Points).ToValue()
            });
        var query2 = freeSql.Select<UserInfo>()
            // .Where(u => ids2.Contains(u.Id))
            .Where(u => u.Id == id2)
            .WithTempQuery(u => new
            {
                GroupNo = 6,
                Points = (int)SqlExt.Sum(u.Points).ToValue()
            });
        var sql = freeSql.Select<UserInfo>()
            .Where(u => u.Role == 1)
            .WithTempQuery(u => new
            {
                GroupNo = 6,
                Count = (int)SqlExt.Count(u.Id).ToValue()
            })
            .FromQuery(query1, query2) // 这里query1、query2的结构相同
            .LeftJoin(p => p.t1.GroupNo == p.t2.GroupNo)
            .LeftJoin(p => p.t1.GroupNo == p.t3.GroupNo)
            .ToSql(p => new
            {
                p.t1.Count,
                Points1 = p.t2.Points,
                Points2 = p.t3.Points
            });

打印出的SQL如下:

SELECT a.[Count] as1, htb.[Points] as2, htc.[Points] as3 
FROM ( 
    SELECT 6 [GroupNo], count(a.[Id]) [Count] 
    FROM [UserInfo] a 
    WHERE (a.[Role] = 1) ) a 
LEFT JOIN ( 
    SELECT 6 [GroupNo], sum(a.[Points]) [Points] 
    FROM [UserInfo] a 
    WHERE (a.[Id] = 1) ) htb ON a.[GroupNo] = htb.[GroupNo] 
LEFT JOIN ( 
    SELECT 6 [GroupNo], sum(a.[Points]) [Points] 
    FROM [UserInfo] a 
    WHERE (a.[Id] = 1) ) htc ON a.[GroupNo] = htc.[GroupNo]

将代码中过滤条件换成id数组后,SQL如下:

SELECT a.[Count] as1, htb.[Points] as2, htc.[Points] as3 
FROM ( 
    SELECT 6 [GroupNo], count(a.[Id]) [Count] 
    FROM [UserInfo] a 
    WHERE (a.[Role] = 1) ) a 
LEFT JOIN ( 
    SELECT 6 [GroupNo], sum(a.[Points]) [Points] 
    FROM [UserInfo] a 
    WHERE (((a.[Id]) in (1,3,5))) ) htb ON a.[GroupNo] = htb.[GroupNo] 
LEFT JOIN ( 
    SELECT 6 [GroupNo], sum(a.[Points]) [Points] 
    FROM [UserInfo] a 
    WHERE (((a.[Id]) in (1,3,5))) ) htc ON a.[GroupNo] = htc.[GroupNo]
LeaFrock commented 4 months ago

可以看到FromQuery的T1和T2结构相同时,后者where条件被前者覆盖了,导致SQL查询错误。

2881099 commented 4 months ago

区分一下每个 WithTempQuery 的类型,避免使用相同的匿名类型,随便加个属性区分就可以了

LeaFrock commented 4 months ago

不修复吗?那文档里在FromQuery的用法上,可能要申明一下了。

2881099 commented 4 months ago

调用链太长,暂时不好解决,使用时区分一下解决吧

2881099 commented 4 months ago
        var query1 = freeSql.Select<UserInfo>()
            //.Where(u => ids1.Contains(u.Id))
            .Where(u => u.Id == id1)
            .WithTempQuery(u => new
            {
                GroupNo = 6,
                Points = (int)SqlExt.Sum(u.Points).ToValue(),
                Flag1 = 1
            });
        var query2 = freeSql.Select<UserInfo>()
            // .Where(u => ids2.Contains(u.Id))
            .Where(u => u.Id == id2)
            .WithTempQuery(u => new
            {
                GroupNo = 6,
                Points = (int)SqlExt.Sum(u.Points).ToValue(),
                Flag2 =1 //与上面的匿名类型区分
            });
LeaFrock commented 4 months ago

workaround的确比较简单,也可以将Points分别命名成Points1和Point2,不需要额外的字段。

主要是这个问题会发生在运行时,编译期不太容易察觉。

如果从源代码层面不太好处理的话,我在想能否通过编写一个分析器,在编译期检测出这种情况然后直接报错或者提示警告。