diff --git a/src/Volo.Abp/Volo.Abp.csproj b/src/Volo.Abp/Volo.Abp.csproj index 68b4e781b4..d02cb6b1b3 100644 --- a/src/Volo.Abp/Volo.Abp.csproj +++ b/src/Volo.Abp/Volo.Abp.csproj @@ -16,8 +16,10 @@ + + diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs new file mode 100644 index 0000000000..7e66355119 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/AsyncCrudAppService.cs @@ -0,0 +1,168 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Application.Services.Dtos; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Linq; + +namespace Abp.Application.Services +{ + public abstract class AsyncCrudAppService + : AsyncCrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : AsyncCrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : AsyncCrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : AsyncCrudAppService + where TGetAllInput : IPagedAndSortedResultRequest + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TCreateInput : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : AsyncCrudAppService> + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : AsyncCrudAppService> + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + { + protected AsyncCrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class AsyncCrudAppService + : CrudAppServiceBase, + IAsyncCrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + where TDeleteInput : IEntityDto + { + public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } + + protected AsyncCrudAppService(IRepository repository) + :base(repository) + { + AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance; + } + + public virtual async Task Get(TGetInput input) + { + CheckGetPermission(); + + var entity = await GetEntityByIdAsync(input.Id); + return MapToEntityDto(entity); + } + + public virtual async Task> GetAll(TGetAllInput input) + { + CheckGetAllPermission(); + + var query = CreateFilteredQuery(input); + + var totalCount = await AsyncQueryableExecuter.CountAsync(query); + + query = ApplySorting(query, input); + query = ApplyPaging(query, input); + + var entities = await AsyncQueryableExecuter.ToListAsync(query); + + return new PagedResultDto( + totalCount, + entities.Select(MapToEntityDto).ToList() + ); + } + + public virtual async Task Create(TCreateInput input) + { + CheckCreatePermission(); + + var entity = MapToEntity(input); + + await Repository.InsertAsync(entity); + await CurrentUnitOfWork.SaveChangesAsync(); + + return MapToEntityDto(entity); + } + + public virtual async Task Update(TUpdateInput input) + { + CheckUpdatePermission(); + + var entity = await GetEntityByIdAsync(input.Id); + + MapToEntity(input, entity); + await CurrentUnitOfWork.SaveChangesAsync(); + + return MapToEntityDto(entity); + } + + public virtual Task Delete(TDeleteInput input) + { + CheckDeletePermission(); + + return Repository.DeleteAsync(input.Id); + } + + protected virtual Task GetEntityByIdAsync(TPrimaryKey id) + { + return Repository.GetAsync(id); + } + } +} diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppService.cs new file mode 100644 index 0000000000..e3120f2d22 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppService.cs @@ -0,0 +1,163 @@ +using System; +using System.Linq; +using Volo.Abp.Application.Services.Dtos; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace Abp.Application.Services +{ + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TCreateInput : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppService> + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppService> + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + } + + public abstract class CrudAppService + : CrudAppServiceBase, + ICrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + where TDeleteInput : IEntityDto + { + protected CrudAppService(IRepository repository) + : base(repository) + { + + } + + public virtual TEntityDto Get(TGetInput input) + { + CheckGetPermission(); + + var entity = GetEntityById(input.Id); + return MapToEntityDto(entity); + } + + public virtual PagedResultDto GetAll(TGetAllInput input) + { + CheckGetAllPermission(); + + var query = CreateFilteredQuery(input); + + var totalCount = query.Count(); + + query = ApplySorting(query, input); + query = ApplyPaging(query, input); + + var entities = query.ToList(); + + return new PagedResultDto( + totalCount, + entities.Select(MapToEntityDto).ToList() + ); + } + + public virtual TEntityDto Create(TCreateInput input) + { + CheckCreatePermission(); + + var entity = MapToEntity(input); + + Repository.Insert(entity); + CurrentUnitOfWork.SaveChanges(); + + return MapToEntityDto(entity); + } + + public virtual TEntityDto Update(TUpdateInput input) + { + CheckUpdatePermission(); + + var entity = GetEntityById(input.Id); + + MapToEntity(input, entity); + CurrentUnitOfWork.SaveChanges(); + + return MapToEntityDto(entity); + } + + public virtual void Delete(TDeleteInput input) + { + CheckDeletePermission(); + + Repository.Delete(input.Id); + } + + protected virtual TEntity GetEntityById(TPrimaryKey id) + { + return Repository.Get(id); + } + } +} diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppServiceBase.cs b/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppServiceBase.cs new file mode 100644 index 0000000000..02a66138f2 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/CrudAppServiceBase.cs @@ -0,0 +1,172 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Application.Services.Dtos; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Linq.Extensions; + +namespace Abp.Application.Services +{ + /// + /// This is a common base class for CrudAppService and AsyncCrudAppService classes. + /// Inherit either from CrudAppService or AsyncCrudAppService, not from this class. + /// + public abstract class CrudAppServiceBase : ApplicationService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + { + protected IRepository Repository { get; } + + protected virtual string GetPermissionName { get; set; } + + protected virtual string GetAllPermissionName { get; set; } + + protected virtual string CreatePermissionName { get; set; } + + protected virtual string UpdatePermissionName { get; set; } + + protected virtual string DeletePermissionName { get; set; } + + protected CrudAppServiceBase(IRepository repository) + { + Repository = repository; + } + + /// + /// Should apply sorting if needed. + /// + /// The query. + /// The input. + protected virtual IQueryable ApplySorting(IQueryable query, TGetAllInput input) + { + //Try to sort query if available + var sortInput = input as ISortedResultRequest; + if (sortInput != null) + { + if (!sortInput.Sorting.IsNullOrWhiteSpace()) + { + return query.OrderBy(sortInput.Sorting); + } + } + + //IQueryable.Task requires sorting, so we should sort if Take will be used. + if (input is ILimitedResultRequest) + { + return query.OrderByDescending(e => e.Id); + } + + //No sorting + return query; + } + + /// + /// Should apply paging if needed. + /// + /// The query. + /// The input. + protected virtual IQueryable ApplyPaging(IQueryable query, TGetAllInput input) + { + //Try to use paging if available + var pagedInput = input as IPagedResultRequest; + if (pagedInput != null) + { + return query.PageBy(pagedInput); + } + + //Try to limit query result if available + var limitedInput = input as ILimitedResultRequest; + if (limitedInput != null) + { + return query.Take(limitedInput.MaxResultCount); + } + + //No paging + return query; + } + + /// + /// This method should create based on given input. + /// It should filter query if needed, but should not do sorting or paging. + /// Sorting should be done in and paging should be done in + /// methods. + /// + /// The input. + protected virtual IQueryable CreateFilteredQuery(TGetAllInput input) + { + var queryableRepository = Repository as IQueryableRepository; + if (queryableRepository == null) + { + throw new AbpException("Repository should be IQueryableRepository in order to call CreateFilteredQuery, but it's not. It's type: " + Repository.GetType().AssemblyQualifiedName); + } + + return queryableRepository; + } + + /// + /// Maps to . + /// It uses by default. + /// It can be overrided for custom mapping. + /// + protected virtual TEntityDto MapToEntityDto(TEntity entity) + { + return ObjectMapper.Map(entity); + } + + /// + /// Maps to to create a new entity. + /// It uses by default. + /// It can be overrided for custom mapping. + /// + protected virtual TEntity MapToEntity(TCreateInput createInput) + { + return ObjectMapper.Map(createInput); + } + + /// + /// Maps to to update the entity. + /// It uses by default. + /// It can be overrided for custom mapping. + /// + protected virtual void MapToEntity(TUpdateInput updateInput, TEntity entity) + { + ObjectMapper.Map(updateInput, entity); + } + + protected virtual void CheckPermission(string permissionName) + { + if (!string.IsNullOrEmpty(permissionName)) + { + //TODO: PermissionChecker.Authorize(permissionName); //Will be implemented when PermissionChecker is available + } + } + + protected virtual void CheckGetPermission() + { + CheckPermission(GetPermissionName); + } + + protected virtual void CheckGetAllPermission() + { + CheckPermission(GetAllPermissionName); + } + + protected virtual void CheckCreatePermission() + { + CheckPermission(CreatePermissionName); + } + + protected virtual void CheckUpdatePermission() + { + CheckPermission(UpdatePermissionName); + } + + protected virtual void CheckDeletePermission() + { + CheckPermission(DeletePermissionName); + } + } +} diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasLongTotalCount.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasLongTotalCount.cs new file mode 100644 index 0000000000..2e578bf611 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasLongTotalCount.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to set "Total Count of Items" to a DTO for long type. + /// + public interface IHasLongTotalCount + { + /// + /// Total count of Items. + /// + long TotalCount { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasTotalCount.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasTotalCount.cs new file mode 100644 index 0000000000..1508cde4a7 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IHasTotalCount.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to set "Total Count of Items" to a DTO. + /// + public interface IHasTotalCount + { + /// + /// Total count of Items. + /// + int TotalCount { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IListResult.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IListResult.cs new file mode 100644 index 0000000000..831e71dec4 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IListResult.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to return a list of items to clients. + /// + /// Type of the items in the list + public interface IListResult + { + /// + /// List of items. + /// + IReadOnlyList Items { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedAndSortedResultRequest.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedAndSortedResultRequest.cs new file mode 100644 index 0000000000..ede0990f95 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedAndSortedResultRequest.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to request a paged and sorted result. + /// + public interface IPagedAndSortedResultRequest : IPagedResultRequest, ISortedResultRequest + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedResult.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedResult.cs new file mode 100644 index 0000000000..86034be354 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/IPagedResult.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to return a page of items to clients. + /// + /// Type of the items in the list + public interface IPagedResult : IListResult, IHasTotalCount + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/ISortedResultRequest.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/ISortedResultRequest.cs new file mode 100644 index 0000000000..61720c07c0 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/ISortedResultRequest.cs @@ -0,0 +1,21 @@ +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// This interface is defined to standardize to request a sorted result. + /// + public interface ISortedResultRequest + { + /// + /// Sorting information. + /// Should include sorting field and optionally a direction (ASC or DESC) + /// Can contain more than one field separated by comma (,). + /// + /// + /// Examples: + /// "Name" + /// "Name DESC" + /// "Name ASC, Age DESC" + /// + string Sorting { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/LimitedResultRequestDto.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/LimitedResultRequestDto.cs new file mode 100644 index 0000000000..e01ed6c4de --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/LimitedResultRequestDto.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// Simply implements . + /// + public class LimitedResultRequestDto : ILimitedResultRequest + { + [Range(1, int.MaxValue)] + public virtual int MaxResultCount { get; set; } = 10; + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedAndSortedResultRequestDto.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedAndSortedResultRequestDto.cs new file mode 100644 index 0000000000..4cfd95dec2 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedAndSortedResultRequestDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// Simply implements . + /// + [Serializable] + public class PagedAndSortedResultRequestDto : PagedResultRequestDto, IPagedAndSortedResultRequest + { + public virtual string Sorting { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultDto.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultDto.cs new file mode 100644 index 0000000000..b81debda37 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultDto.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// Implements . + /// + /// Type of the items in the list + [Serializable] + public class PagedResultDto : ListResultDto, IPagedResult + { + /// + /// Total count of Items. + /// + public int TotalCount { get; set; } + + /// + /// Creates a new object. + /// + public PagedResultDto() + { + + } + + /// + /// Creates a new object. + /// + /// Total count of Items + /// List of items in current page + public PagedResultDto(int totalCount, IReadOnlyList items) + : base(items) + { + TotalCount = totalCount; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultRequestDto.cs b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultRequestDto.cs new file mode 100644 index 0000000000..adeaac9df3 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/Dtos/PagedResultRequestDto.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Volo.Abp.Application.Services.Dtos +{ + /// + /// Simply implements . + /// + [Serializable] + public class PagedResultRequestDto : LimitedResultRequestDto, IPagedResultRequest + { + [Range(0, int.MaxValue)] + public virtual int SkipCount { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs new file mode 100644 index 0000000000..5d3f2905c1 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/IAsyncCrudAppService.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Abp.Application.Services.Dtos; + +namespace Abp.Application.Services +{ + public interface IAsyncCrudAppService + : IAsyncCrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IAsyncCrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IAsyncCrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IAsyncCrudAppService + where TEntityDto : IEntityDto + where TCreateInput : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IAsyncCrudAppService> + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IAsyncCrudAppService> + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + { + + } + + public interface IAsyncCrudAppService + : IApplicationService + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + where TDeleteInput : IEntityDto + { + Task Get(TGetInput input); + + Task> GetAll(TGetAllInput input); + + Task Create(TCreateInput input); + + Task Update(TUpdateInput input); + + Task Delete(TDeleteInput input); + } +} diff --git a/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs b/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs new file mode 100644 index 0000000000..268651dbe3 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Application/Services/ICrudAppService.cs @@ -0,0 +1,70 @@ +using System; +using Volo.Abp.Application.Services; +using Volo.Abp.Application.Services.Dtos; + +namespace Abp.Application.Services +{ + public interface ICrudAppService + : ICrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface ICrudAppService + : ICrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface ICrudAppService + : ICrudAppService + where TEntityDto : IEntityDto + { + + } + + public interface ICrudAppService + : ICrudAppService + where TEntityDto : IEntityDto + where TCreateInput : IEntityDto + { + + } + + public interface ICrudAppService + : ICrudAppService> + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + { + + } + + public interface ICrudAppService + : ICrudAppService> + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + { + + } + + public interface ICrudAppService + : IApplicationService + where TEntityDto : IEntityDto + where TUpdateInput : IEntityDto + where TGetInput : IEntityDto + where TDeleteInput : IEntityDto + { + TEntityDto Get(TGetInput input); + + PagedResultDto GetAll(TGetAllInput input); + + TEntityDto Create(TCreateInput input); + + TEntityDto Update(TUpdateInput input); + + void Delete(TDeleteInput input); + } +} diff --git a/src/Volo.Abp/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs b/src/Volo.Abp/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs index 45542d3bae..8018bf7e87 100644 --- a/src/Volo.Abp/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs +++ b/src/Volo.Abp/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs @@ -6,8 +6,10 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Linq { - public class DefaultAsyncQueryableExecuter : IAsyncQueryableExecuter, ITransientDependency + public class DefaultAsyncQueryableExecuter : IAsyncQueryableExecuter, ISingletonDependency { + public static DefaultAsyncQueryableExecuter Instance { get; } = new DefaultAsyncQueryableExecuter(); + public Task CountAsync(IQueryable queryable) { return Task.FromResult(queryable.Count());