WithTempQuery().ToChunAsyncEnumerable()和WithTempQuery.ToListAsync()行为不一致。
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
Research direction
The payload shows no FreeSql source files, only a repro: the same WithTempQuery chain run through ToChunkAsyncEnumerable and through ToListAsync, with the generated SQL for the first path. Start by reproducing with a small model using a navigation property inside the temp-query selector, then compare the two SQL outputs. Done means both paths emit the same SQL for the same selector, with no extra join and no missing alias.
Written by the indexing model from the issue text.
Description
问题描述及重现代码:
WithTempQuery()中的select表达式中的实体类用的是一个和表映射的实体类对象。然后在TempQuery中做了过滤筛选,器中过滤条件包含了导航属性下的实体类字段。如果是直接ToListAsync查询返回数据,生成的sql没有任何问题,但是使用ToChunkAsyncEnumerable,生成的sql会多一个连表语句。但是由于已经用TempQuery字段做了别名,所以会抛出没有这个字段。但实际上连这个连表语句都不需要的。对应的实体类以及查询代码如下:
实体类:
[ Table(Name = "case_info", DisableSyncStructure = true)]
public partial class CaseInfo {
/// <summary>
/// 案件ID
/// </summary>
[ Column(Name = "case_id", StringLength = 50, IsPrimary = true, IsNullable = false)]
public string Id { get; set; } = Guid.NewGuid().ToString().ToUpper();
/// <summary>
/// 发明名称
/// </summary>
[ Column(Name = "case_name", StringLength = 500)]
public string CaseName { get; set; }
/// <summary>
/// 承办部门
/// </summary>
[ Column(Name = "undertake_dept_id", StringLength = 50)]
public string UndertakeDeptId { get; set; }
/// <summary>
/// 卷号
/// </summary>
[ Column(Name = "volume", StringLength = 50)]
public string Volume { get; set; }
/// <summary>
/// 案件扩展信息
/// </summary>
[Navigate(nameof(Id))]
public virtual CaseExtendInfo? ExtendInfo { get; set; }
}
[ Table(Name = "case_extend_info", DisableSyncStructure = true)]
public partial class CaseExtendInfo {
[ Column(Name = "case_id", StringLength = 50, IsPrimary = true, IsNullable = false)]
public string CaseId { get; set; }
/// <summary>
/// 预估风险费
/// </summary>
[Column(Name = "estimated_risk_fee")]
public decimal EstimatedRiskFee { get; set; }
}
[ Table(Name = "sys_user_info", DisableSyncStructure = true)]
public partial class SysUserInfo {
[ Column(Name = "user_id", StringLength = 50, IsPrimary = true, IsNullable = false)]
public string UserId { get; set; }
/// <summary>
/// 部门id
/// </summary>
[Column(Name = "dept_id", StringLength = 50)]
public string DeptId { get; set; } = null!;
}
/// <summary>
/// 案件任务信息
/// </summary>
[Table(Name = "case_proc_info", DisableSyncStructure = true)]
public class CaseProcInfo : IVersionEntity, ISystemLoggable<CaseProcInfo>
{
/// <summary>
/// 主键ID
/// </summary>
[Display(Name = "任务ID", Order = 0)]
[Hide]
[Column(Name = "proc_id", StringLength = 50, IsPrimary = true, IsNullable = false)]
public string ProcId { get; set; } = Guid.NewGuid().ToString().ToUpper();
/// <summary>
/// 事项名称ID
/// </summary>
[Column(Name = "ctrl_proc_id", StringLength = 200)]
public string CtrlProcId { get; set; }
/// <summary>
/// 事项状态ID
/// </summary>
[Column(Name = "proc_status_id", StringLength = 50)]
public string? ProcStatusId { get; set; }
/// <summary>
/// 承办人
/// </summary>
[Column(Name = "undertake_user_id", StringLength = 50)]
public string? UndertakeUserId { get; set; }
/// <summary>
/// 任务贡献比
/// </summary>
[Column(Name = "project_schedule")]
public decimal? ProjectSchedule { get; set; }
/// <summary>
/// 承办人信息
/// </summary>
[Navigate(nameof(UndertakeUserId))]
public virtual SysUserInfo? UndertakeUserInfo { get; set; }
}
/// <summary>
/// 任务进度
/// </summary>
[Table(Name = "case_proc_schedule", DisableSyncStructure = true)]
public partial class CaseProcSchedule
{
/// <summary>
/// 主键
/// </summary>
[Column(Name = "schedule_id", StringLength = 50, IsPrimary = true, IsNullable = false)]
public string ScheduleId { get; set; } = null!;
/// <summary>
/// 创建时间
/// </summary>
[Column(Name = "create_time")]
public DateTime CreateTime { get; set; }
/// <summary>
/// 创建用户id
/// </summary>
[Column(Name = "create_user_id", StringLength = 50, IsNullable = false)]
public string CreateUserId { get; set; } = null!;
/// <summary>
/// 年-月
/// </summary>
[Column(Name = "filling_time")]
public DateTime? FillingTime { get; set; }
/// <summary>
/// 任务id
/// </summary>
[Column(Name = "proc_id", StringLength = 50, IsNullable = false)]
public string ProcId { get; set; } = null!;
/// <summary>
/// 进度
/// </summary>
[Column(Name = "progress")]
public decimal? Progress { get; set; }
/// <summary>
/// 耗时天数
/// </summary>
[Column(Name = "cost_days", DbType = "decimal(5,1)")]
public decimal CostDays { get; set; }
/// <summary>
/// 进度详情
/// </summary>
[Column(Name = "schedule_detail", StringLength = 1000, IsNullable = false)]
public string ScheduleDetail { get; set; } = string.Empty;
/// <summary>
/// 更新时间
/// </summary>
[Column(Name = "update_time")]
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 更新用户id
/// </summary>
[Column(Name = "update_user_id", StringLength = 50)]
public string? UpdateUserId { get; set; }
/// <summary>
/// 关联的任务信息
/// </summary>
[Navigate(nameof(ProcId))]
public virtual CaseProcInfo? ProcInfo { get; set; }
}
查询条件构造
var query = freeSql.Select<CaseProcSchedule>().WithLock()
.InnerJoin(s => s.ProcId == s.ProcInfo!.ProcId)
.InnerJoin(s => s.ProcInfo!.CaseId == s.ProcInfo!.CaseInfo.Id)
.Where(s => s.ProcInfo!.CaseInfo.CaseTypeId == "X")
.Where(s => request.FillingTimes.Contains(s.FillingTime!.Value))
.Where(s => request.UndertakerIds.Contains(s.ProcInfo!.UndertakeUserId!))
.WithTempQuery(s => new CaseProcSchedule
{
ScheduleId = s.ScheduleId,
CreateTime = s.CreateTime,
FillingTime = s.FillingTime,
ProcId = s.ProcId,
Progress = s.Progress,
CostDays = s.CostDays,
ScheduleDetail = s.ScheduleDetail,
ProcInfo = new CaseProcInfo()
{
ProcId = s.ProcInfo!.ProcId,
CtrlProcId = s.ProcInfo!.CtrlProcId,
ProcStatusId = s.ProcInfo!.ProcStatusId,
ProjectSchedule = s.ProcInfo!.ProjectSchedule,
UndertakeUserId = s.ProcInfo!.UndertakeUserId,
UndertakeUserInfo = new SysUserInfo()
{
UserId = s.ProcInfo!.UndertakeUserInfo!.UserId,
DeptId = s.ProcInfo!.UndertakeUserInfo!.DeptId,
},
CaseInfo = new CaseInfo()
{
Id = s.ProcInfo!.CaseInfo.Id,
CaseName = s.ProcInfo!.CaseInfo.CaseName,
Volume = s.ProcInfo!.CaseInfo.Volume,
UndertakeDeptId = s.ProcInfo!.CaseInfo.UndertakeDeptId,
ExtendInfo = new CaseExtendInfo()
{
CaseId = s.ProcInfo!.CaseInfo.ExtendInfo!.CaseId,
EstimatedRiskFee = s.ProcInfo!.CaseInfo.ExtendInfo.EstimatedRiskFee
}
}
}
}).Where(s => request.UndertakeUserIds.Contains(s.ProcInfo!.UndertakeUserId))
ToChunkAsyncEnumerable
await foreach (var productivityInfos in query.ToChunkAsyncEnumerable(s => new ProductivityInfo
{
CreateTime = s.CreateTime,
ScheduleId = s.ScheduleId,
ProcId = s.ProcId,
CaseId = s.ProcInfo!.CaseId,
ProgressMonth = $"{s.FillingTime!.Value.Year}年{s.FillingTime.Value.Month}月",
ProgressValue = s.Progress ?? 0,
ScheduleDetail = s.ScheduleDetail,
ContributionRatio = s.ProcInfo!.ProjectSchedule,
Volume = s.ProcInfo!.CaseInfo.Volume,
CaseName = s.ProcInfo!.CaseInfo.CaseName,
UndertakeDepartment = s.ProcInfo!.CaseInfo.UndertakeDeptId,
ProcName = s.ProcInfo!.CtrlProcId,
ProcStatus = s.ProcInfo!.ProcStatusId ?? string.Empty,
Undertaker = s.ProcInfo!.UndertakeUserId ?? string.Empty,
ProgressCostDays = s.CostDays,
EstimatedRiskFee = s.ProcInfo!.CaseInfo.ExtendInfo!.EstimatedRiskFee
}, 10).WithCancellation(cancellationToken))
SELECT a.[CreateTime] as1, a.[ScheduleId] as2, a.[ProcId2] as3, a__ProcInfo.[case_id] as4, N''+isnull(cast(datepart(year, a.[FillingTime]) as varchar), '')+N'年'+isnull(cast(datepart(month, a.[FillingTime]) as varchar), '')+N'月' as5, isnull(a.[Progress], 0) as6, a.[ScheduleDetail] as7, a.[ProjectSchedule] as8, a.[Volume] as9, a.[CaseName] as10, a.[UndertakeDeptId] as11, a.[CtrlProcId] as12, isnull(a.[ProcStatusId], '') as13, isnull(a.[UndertakeUserId], '') as14, a.[CostDays] as15, a.[EstimatedRiskFee] as16
FROM (
SELECT a.[schedule_id] [ScheduleId], a.[create_time] [CreateTime], a.[filling_time] [FillingTime], a.[proc_id] [ProcId], a.[progress], a.[cost_days] [CostDays], a.[schedule_detail] [ScheduleDetail], a__ProcInfo.[proc_id] [ProcId2], a__ProcInfo.[ctrl_proc_id] [CtrlProcId], a__ProcInfo.[proc_status_id] [ProcStatusId], a__ProcInfo.[project_schedule] [ProjectSchedule], a__ProcInfo.[undertake_user_id] [UndertakeUserId], a__ProcInfo__UndertakeUserInfo.[user_id] [UserId], a__ProcInfo__UndertakeUserInfo.[dept_id] [DeptId], a__ProcInfo__CaseInfo.[case_id] [Id], a__ProcInfo__CaseInfo.[case_name] [CaseName], a__ProcInfo__CaseInfo.[volume], a__ProcInfo__CaseInfo.[undertake_dept_id] [UndertakeDeptId], a__ProcInfo__CaseInfo__ExtendInfo.[case_id] [CaseId], a__ProcInfo__CaseInfo__ExtendInfo.[estimated_risk_fee] [EstimatedRiskFee]
FROM [case_proc_schedule] a With(NoLock)
INNER JOIN [case_proc_info] a__ProcInfo With(NoLock) ON a.[proc_id] = a__ProcInfo.[proc_id]
INNER JOIN [case_info] a__ProcInfo__CaseInfo With(NoLock) ON a__ProcInfo.[case_id] = a__ProcInfo__CaseInfo.[case_id]
LEFT JOIN [sys_user_info] a__ProcInfo__UndertakeUserInfo With(NoLock) ON a__ProcInfo__UndertakeUserInfo.[user_id] = a__ProcInfo.[undertake_user_id]
LEFT JOIN [case_extend_info] a__ProcInfo__CaseInfo__ExtendInfo With(NoLock) ON a__ProcInfo__CaseInfo__ExtendInfo.[case_id] = a__ProcInfo__CaseInfo.[case_id]
WHERE (a__ProcInfo__CaseInfo.[case_type_id] = N'X') AND (((a.[filling_time]) in ('2026-10-01 00:00:00.000'))) AND (((a__ProcInfo.[undertake_user_id]) in (N'06e41cf0-88db-4c05-95a0-3c8b6afcb238'))) ) a
LEFT JOIN [case_proc_info] a__ProcInfo ON a__ProcInfo.[proc_id] = a.[proc_id]
WHERE (((a.[UndertakeUserId]) in (N'06e41cf0-88db-4c05-95a0-3c8b6afcb238')))
toListAync查询
数据库版本
var list = query.ToListAsync(s => new ProductivityInfo
{
CreateTime = s.CreateTime,
ScheduleId = s.ScheduleId,
ProcId = s.ProcId,
CaseId = s.ProcInfo!.CaseInfo.Id,
ProgressMonth = $"{s.FillingTime!.Value.Year}年{s.FillingTime.Value.Month}月",
ProgressValue = s.Progress ?? 0,
ScheduleDetail = s.ScheduleDetail,
ContributionRatio = s.ProcInfo!.ProjectSchedule,
Volume = s.ProcInfo!.CaseInfo.Volume,
CaseName = s.ProcInfo!.CaseInfo.CaseName,
UndertakeDepartment = s.ProcInfo!.CaseInfo.UndertakeDeptId,
ProcName = s.ProcInfo!.CtrlProcId,
ProcStatus = s.ProcInfo!.ProcStatusId ?? string.Empty,
Undertaker = s.ProcInfo!.UndertakeUserId ?? string.Empty,
// UndertakerDepartment = s.ProcInfo!.UndertakeUserInfo!.DeptId,
ProgressCostDays = s.CostDays,
EstimatedRiskFee = s.ProcInfo!.CaseInfo.ExtendInfo!.EstimatedRiskFee
}, cancellationToken);
sql语句
SELECT a.[CreateTime] as1, a.[ScheduleId] as2, a.[ProcId] as3, a.[Id] as4, N''+isnull(cast(datepart(year, a.[FillingTime]) as varchar), '')+N'年'+isnull(cast(datepart(month, a.[FillingTime]) as varchar), '')+N'月' as5, isnull(a.[Progress], 0) as6, a.[ScheduleDetail] as7, a.[ProjectSchedule] as8, a.[Volume] as9, a.[CaseName] as10, a.[UndertakeDeptId] as11, a.[CtrlProcId] as12, isnull(a.[ProcStatusId], '') as13, isnull(a.[UndertakeUserId], '') as14, a.[CostDays] as15, a.[EstimatedRiskFee] as16
FROM (
SELECT a.[schedule_id] [ScheduleId], a.[create_time] [CreateTime], a.[filling_time] [FillingTime], a.[proc_id] [ProcId], a.[progress], a.[cost_days] [CostDays], a.[schedule_detail] [ScheduleDetail], a__ProcInfo.[proc_id] [ProcId2], a__ProcInfo.[ctrl_proc_id] [CtrlProcId], a__ProcInfo.[proc_status_id] [ProcStatusId], a__ProcInfo.[project_schedule] [ProjectSchedule], a__ProcInfo.[undertake_user_id] [UndertakeUserId], a__ProcInfo__UndertakeUserInfo.[user_id] [UserId], a__ProcInfo__UndertakeUserInfo.[dept_id] [DeptId], a__ProcInfo__CaseInfo.[case_id] [Id], a__ProcInfo__CaseInfo.[case_name] [CaseName], a__ProcInfo__CaseInfo.[volume], a__ProcInfo__CaseInfo.[undertake_dept_id] [UndertakeDeptId], a__ProcInfo__CaseInfo__ExtendInfo.[case_id] [CaseId], a__ProcInfo__CaseInfo__ExtendInfo.[estimated_risk_fee] [EstimatedRiskFee]
FROM [case_proc_schedule] a With(NoLock)
INNER JOIN [case_proc_info] a__ProcInfo With(NoLock) ON a.[proc_id] = a__ProcInfo.[proc_id]
INNER JOIN [case_info] a__ProcInfo__CaseInfo With(NoLock) ON a__ProcInfo.[case_id] = a__ProcInfo__CaseInfo.[case_id]
LEFT JOIN [sys_user_info] a__ProcInfo__UndertakeUserInfo With(NoLock) ON a__ProcInfo__UndertakeUserInfo.[user_id] = a__ProcInfo.[undertake_user_id]
LEFT JOIN [case_extend_info] a__ProcInfo__CaseInfo__ExtendInfo With(NoLock) ON a__ProcInfo__CaseInfo__ExtendInfo.[case_id] = a__ProcInfo__CaseInfo.[case_id]
WHERE (a__ProcInfo__CaseInfo.[case_type_id] = N'X') AND (((a.[filling_time]) in ('2026-10-01 00:00:00.000'))) AND (((a__ProcInfo__UndertakeUserInfo.[dept_id]) in (N'7e3d4e4f-1308-4650-a07b-237348c1de78',N'19acbfb0-8cbf-43f2-b936-07a93add2c55'))) AND (((a__ProcInfo.[undertake_user_id]) in (N'06e41cf0-88db-4c05-95a0-3c8b6afcb238'))) ) a
WHERE (((a.[UndertakeUserId]) in (N'ad0209f1-a52e-4d30-b5dc-3f81241ec9fb')))
ToChunkAsyncEnumerable多了个 LEFT JOIN [case_proc_info] a__ProcInfo ON a__ProcInfo.[proc_id] = a.[proc_id]
感觉是因为最外层多了一个导航属性过滤条件的关系。但是ToListAsync可以适配,ToChunkAsyncEnumerable没有。
安装的Nuget包
.net framework/. net core? 及具体版本
.net 10
- Dominant language
- C#
- Stars
- 4.4k
- Forks
- 911
- Avg merge
- 12h 47m
- Merged PRs (30d)
- 2
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnetcore/FreeSql
-
byte类型在条件中的解析BUGOpen
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
dotnetcore/FreeSql#2290 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
dotnetcore/FreeSql#2269 · 6 comments ·
-
Difficulty 1/5 Under an hour Newbie friendliness 68/100
dotnetcore/FreeSql#2078 ·
-
NuGet 包是以 Debug 配置发布Open
Difficulty 3/5 1-2 days Newbie friendliness 55/100
dotnetcore/FreeSql#2287 · 2 comments ·
-
Difficulty 3/5 1-2 days Newbie friendliness 55/100
dotnetcore/FreeSql#2281 ·
All issues in dotnetcore/FreeSql
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
Maintainers usually reply within 1 day
-
Bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
icsharpcode/ILSpy#4277 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
k2-fsa/sherpa-onnx#4098 ·
Maintainers usually reply within 2 days
-
bug effort:S P3
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
nightscout/nocturne#2633 ·
Maintainers usually reply within 1 day
-
enhancement good first issue hacktoberfest
Difficulty 2/5 1-3 hours Newbie friendliness 84/100