From c9e573e027a8445bc0b27f995b30a9bbff83e2a5 Mon Sep 17 00:00:00 2001 From: cKey <35512826+colinin@users.noreply.github.com> Date: Tue, 7 Mar 2023 11:39:57 +0800 Subject: [PATCH] feat(tasks): normalize queries using specifications --- .../BackgroundJobInfoAppService.cs | 5 +- .../BackgroundJobInfoExtensions.cs | 38 ++++++++++ .../BackgroundJobInfoSpecification.cs | 36 +++++++++ ...groundJobInfoWaitingPeriodSpecification.cs | 19 +++++ .../BackgroundJobInfoWaitingSpecification.cs | 21 +++++ .../IBackgroundJobInfoRepository.cs | 9 ++- .../Expressions/ExpressionFuncExtensions.cs | 32 ++++++++ .../EfCoreBackgroundJobInfoRepository.cs | 76 +++---------------- 8 files changed, 164 insertions(+), 72 deletions(-) create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoExtensions.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoSpecification.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingPeriodSpecification.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingSpecification.cs create mode 100644 aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/System/Linq/Expressions/ExpressionFuncExtensions.cs diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs index ed377a1fe..1682fb77f 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs @@ -142,9 +142,10 @@ public class BackgroundJobInfoAppService : DynamicQueryableAppService(totalCount, ObjectMapper.Map, List>(backgroundJobInfos)); diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoExtensions.cs new file mode 100644 index 000000000..2cb430dc6 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoExtensions.cs @@ -0,0 +1,38 @@ +using LINGYUN.Abp.BackgroundTasks; + +namespace LINGYUN.Abp.TaskManagement; +public static class BackgroundJobInfoEbackgroundJobInfotensions +{ + public static JobInfo ToJobInfo(this BackgroundJobInfo backgroundJobInfo) + { + return new JobInfo + { + Id = backgroundJobInfo.Id, + TenantId = backgroundJobInfo.TenantId, + Name = backgroundJobInfo.Name, + NextRunTime = backgroundJobInfo.NextRunTime, + Args = backgroundJobInfo.Args, + IsAbandoned = backgroundJobInfo.IsAbandoned, + BeginTime = backgroundJobInfo.BeginTime, + EndTime = backgroundJobInfo.EndTime, + CreationTime = backgroundJobInfo.CreationTime, + Cron = backgroundJobInfo.Cron, + MaxCount = backgroundJobInfo.MaxCount, + MaxTryCount = backgroundJobInfo.MaxTryCount, + Description = backgroundJobInfo.Description, + Group = backgroundJobInfo.Group, + Interval = backgroundJobInfo.Interval, + JobType = backgroundJobInfo.JobType, + Status = backgroundJobInfo.Status, + Priority = backgroundJobInfo.Priority, + Source = backgroundJobInfo.Source, + LastRunTime = backgroundJobInfo.LastRunTime, + LockTimeOut = backgroundJobInfo.LockTimeOut, + Result = backgroundJobInfo.Result, + TriggerCount = backgroundJobInfo.TriggerCount, + TryCount = backgroundJobInfo.TryCount, + Type = backgroundJobInfo.Type, + NodeName = backgroundJobInfo.NodeName, + }; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoSpecification.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoSpecification.cs new file mode 100644 index 000000000..e1028c5d0 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoSpecification.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq.Expressions; +using Volo.Abp.Specifications; + +namespace LINGYUN.Abp.TaskManagement; +public class BackgroundJobInfoSpecification : Specification +{ + protected BackgroundJobInfoFilter Filter { get; } + public BackgroundJobInfoSpecification(BackgroundJobInfoFilter filter) + { + Filter = filter; + } + + public override Expression> ToExpression() + { + Expression> expression = _ => true; + + return expression + .AndIf(!Filter.Type.IsNullOrWhiteSpace(), x => x.Type.Contains(Filter.Type)) + .AndIf(!Filter.Group.IsNullOrWhiteSpace(), x => x.Group.Equals(Filter.Group)) + .AndIf(!Filter.Name.IsNullOrWhiteSpace(), x => x.Name.Equals(Filter.Name)) + .AndIf(!Filter.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(Filter.Filter) || + x.Group.Contains(Filter.Filter) || x.Type.Contains(Filter.Filter) || x.Description.Contains(Filter.Filter)) + .AndIf(Filter.JobType.HasValue, x => x.JobType == Filter.JobType) + .AndIf(Filter.Status.HasValue, x => x.Status == Filter.Status.Value) + .AndIf(Filter.Priority.HasValue, x => x.Priority == Filter.Priority.Value) + .AndIf(Filter.Source.HasValue, x => x.Source == Filter.Source.Value) + .AndIf(Filter.IsAbandoned.HasValue, x => x.IsAbandoned == Filter.IsAbandoned.Value) + .AndIf(Filter.BeginLastRunTime.HasValue, x => x.LastRunTime >= Filter.BeginLastRunTime) + .AndIf(Filter.EndLastRunTime.HasValue, x => x.LastRunTime <= Filter.EndLastRunTime) + .AndIf(Filter.BeginTime.HasValue, x => x.BeginTime >= Filter.BeginTime) + .AndIf(Filter.EndTime.HasValue, x => x.EndTime <= Filter.EndTime) + .AndIf(Filter.BeginCreationTime.HasValue, x => x.CreationTime >= Filter.BeginCreationTime) + .AndIf(Filter.EndCreationTime.HasValue, x => x.CreationTime <= Filter.EndCreationTime); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingPeriodSpecification.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingPeriodSpecification.cs new file mode 100644 index 000000000..2a5ef8ca8 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingPeriodSpecification.cs @@ -0,0 +1,19 @@ +using LINGYUN.Abp.BackgroundTasks; +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace LINGYUN.Abp.TaskManagement; +public class BackgroundJobInfoWaitingPeriodSpecification : BackgroundJobInfoWaitingSpecification +{ + public override Expression> ToExpression() + { + var status = new JobStatus[] { JobStatus.Running, JobStatus.FailedRetry }; + + Expression> expression = _ => true; + return expression + .And(x => x.IsEnabled && !x.IsAbandoned) + .And(x => x.JobType == JobType.Period && status.Contains(x.Status)) + .And(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingSpecification.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingSpecification.cs new file mode 100644 index 000000000..e5c3695f3 --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfoWaitingSpecification.cs @@ -0,0 +1,21 @@ +using LINGYUN.Abp.BackgroundTasks; +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace LINGYUN.Abp.TaskManagement; + +public class BackgroundJobInfoWaitingSpecification : Volo.Abp.Specifications.Specification +{ + public override Expression> ToExpression() + { + var status = new JobStatus[] { JobStatus.Running, JobStatus.FailedRetry }; + + Expression> expression = _ => true; + + return expression + .And(x => x.IsEnabled && !x.IsAbandoned) + .And(x => x.JobType != JobType.Period && status.Contains(x.Status)) + .And(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)); + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs index 603d66c1f..40bd4bba9 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoRepository.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Specifications; namespace LINGYUN.Abp.TaskManagement; @@ -54,23 +55,23 @@ public interface IBackgroundJobInfoRepository : IRepository /// 获取过滤后的任务数量 /// - /// + /// 查询规约 /// /// Task GetCountAsync( - BackgroundJobInfoFilter filter, + ISpecification specification, CancellationToken cancellationToken = default); /// /// 获取过滤后的任务列表 /// - /// + /// 查询规约 /// /// /// /// /// Task> GetListAsync( - BackgroundJobInfoFilter filter, + ISpecification specification, string sorting = nameof(BackgroundJobInfo.Name), int maxResultCount = 10, int skipCount = 0, diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/System/Linq/Expressions/ExpressionFuncExtensions.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/System/Linq/Expressions/ExpressionFuncExtensions.cs new file mode 100644 index 000000000..74e3c828a --- /dev/null +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/System/Linq/Expressions/ExpressionFuncExtensions.cs @@ -0,0 +1,32 @@ +using Volo.Abp.Specifications; + +namespace System.Linq.Expressions; + +internal static class ExpressionFuncExtensions +{ + public static Expression> AndIf( + this Expression> first, + bool condition, + Expression> second) + { + if (condition) + { + return ExpressionFuncExtender.And(first, second); + } + + return first; + } + + public static Expression> OrIf( + this Expression> first, + bool condition, + Expression> second) + { + if (condition) + { + return ExpressionFuncExtender.Or(first, second); + } + + return first; + } +} diff --git a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs index 5dfa3cd23..04a60f349 100644 --- a/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs +++ b/aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Specifications; using Volo.Abp.Timing; namespace LINGYUN.Abp.TaskManagement.EntityFrameworkCore; @@ -43,35 +44,7 @@ public class EfCoreBackgroundJobInfoRepository : { return await (await GetDbSetAsync()) .Where(x => x.Id.Equals(id)) - .Select(x => new JobInfo - { - Id = x.Id, - TenantId = x.TenantId, - Name = x.Name, - NextRunTime = x.NextRunTime, - Args = x.Args, - IsAbandoned = x.IsAbandoned, - BeginTime = x.BeginTime, - EndTime = x.EndTime, - CreationTime = x.CreationTime, - Cron = x.Cron, - MaxCount = x.MaxCount, - MaxTryCount = x.MaxTryCount, - Description = x.Description, - Group = x.Group, - Interval = x.Interval, - JobType = x.JobType, - Status = x.Status, - Priority = x.Priority, - Source = x.Source, - LastRunTime = x.LastRunTime, - LockTimeOut = x.LockTimeOut, - Result = x.Result, - TriggerCount = x.TriggerCount, - TryCount = x.TryCount, - Type = x.Type, - NodeName = x.NodeName, - }) + .Select(x => x.ToJobInfo()) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } @@ -92,26 +65,24 @@ public class EfCoreBackgroundJobInfoRepository : public async virtual Task> GetAllPeriodTasksAsync( CancellationToken cancellationToken = default) { - var status = new JobStatus[] { JobStatus.Running, JobStatus.FailedRetry }; - return await (await GetDbSetAsync()) - .Where(x => x.IsEnabled && !x.IsAbandoned) - .Where(x => x.JobType == JobType.Period && status.Contains(x.Status)) - .Where(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)) + .Where(new BackgroundJobInfoWaitingPeriodSpecification().ToExpression()) .OrderByDescending(x => x.Priority) .AsNoTracking() .ToListAsync(GetCancellationToken(cancellationToken)); } - public async virtual Task GetCountAsync(BackgroundJobInfoFilter filter, CancellationToken cancellationToken = default) + public async virtual Task GetCountAsync(ISpecification specification, CancellationToken cancellationToken = default) { - return await ApplyFilter(await GetDbSetAsync(), filter) + return await (await GetDbSetAsync()) + .Where(specification.ToExpression()) .CountAsync(GetCancellationToken(cancellationToken)); } - public async virtual Task> GetListAsync(BackgroundJobInfoFilter filter, string sorting = "Name", int maxResultCount = 10, int skipCount = 0, CancellationToken cancellationToken = default) + public async virtual Task> GetListAsync(ISpecification specification, string sorting = "Name", int maxResultCount = 10, int skipCount = 0, CancellationToken cancellationToken = default) { - return await ApplyFilter(await GetDbSetAsync(), filter) + return await (await GetDbSetAsync()) + .Where(specification.ToExpression()) .OrderBy(sorting ?? $"{nameof(BackgroundJobInfo.CreationTime)} DESC") .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); @@ -121,12 +92,8 @@ public class EfCoreBackgroundJobInfoRepository : int maxResultCount, CancellationToken cancellationToken = default) { - var status = new JobStatus[] { JobStatus.Running, JobStatus.FailedRetry }; - return await (await GetDbSetAsync()) - .Where(x => x.IsEnabled && !x.IsAbandoned) - .Where(x => x.JobType != JobType.Period && status.Contains(x.Status)) - .Where(x => (x.MaxCount == 0 || x.TriggerCount < x.MaxCount) || (x.MaxTryCount == 0 || x.TryCount < x.MaxTryCount)) + .Where(new BackgroundJobInfoWaitingSpecification().ToExpression()) .OrderByDescending(x => x.Priority) .ThenBy(x => x.TryCount) .ThenBy(x => x.NextRunTime) @@ -134,27 +101,4 @@ public class EfCoreBackgroundJobInfoRepository : .AsNoTracking() .ToListAsync(GetCancellationToken(cancellationToken)); } - - protected virtual IQueryable ApplyFilter( - IQueryable queryable, - BackgroundJobInfoFilter filter) - { - return queryable - .WhereIf(!filter.Type.IsNullOrWhiteSpace(), x => x.Type.Contains(filter.Type)) - .WhereIf(!filter.Group.IsNullOrWhiteSpace(), x => x.Group.Equals(filter.Group)) - .WhereIf(!filter.Name.IsNullOrWhiteSpace(), x => x.Name.Equals(filter.Name)) - .WhereIf(!filter.Filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter.Filter) || - x.Group.Contains(filter.Filter) || x.Type.Contains(filter.Filter) || x.Description.Contains(filter.Filter)) - .WhereIf(filter.JobType.HasValue, x => x.JobType == filter.JobType) - .WhereIf(filter.Status.HasValue, x => x.Status == filter.Status.Value) - .WhereIf(filter.Priority.HasValue, x => x.Priority == filter.Priority.Value) - .WhereIf(filter.Source.HasValue, x => x.Source == filter.Source.Value) - .WhereIf(filter.IsAbandoned.HasValue, x => x.IsAbandoned == filter.IsAbandoned.Value) - .WhereIf(filter.BeginLastRunTime.HasValue, x => filter.BeginLastRunTime.Value.CompareTo(x.LastRunTime) <= 0) - .WhereIf(filter.EndLastRunTime.HasValue, x => filter.EndLastRunTime.Value.CompareTo(x.LastRunTime) >= 0) - .WhereIf(filter.BeginTime.HasValue, x => x.BeginTime.CompareTo(x.BeginTime) >= 0) - .WhereIf(filter.EndTime.HasValue, x => filter.EndTime.Value.CompareTo(x.EndTime) >= 0) - .WhereIf(filter.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.Value) >= 0) - .WhereIf(filter.EndCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.EndCreationTime.Value) <= 0); - } }