diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs index 45915826e4..e6f433cad6 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbstractKeyCrudAppService.cs @@ -6,7 +6,6 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; -using Volo.Abp.Linq; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; @@ -66,7 +65,6 @@ namespace Volo.Abp.Application.Services ICrudAppService where TEntity : class, IEntity { - public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } protected IRepository Repository { get; } @@ -83,7 +81,6 @@ namespace Volo.Abp.Application.Services protected AbstractKeyCrudAppService(IRepository repository) { Repository = repository; - AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance; } public virtual async Task GetAsync(TKey id) @@ -100,12 +97,12 @@ namespace Volo.Abp.Application.Services var query = CreateFilteredQuery(input); - var totalCount = await AsyncQueryableExecuter.CountAsync(query); + var totalCount = await AsyncExecuter.CountAsync(query); query = ApplySorting(query, input); query = ApplyPaging(query, input); - var entities = await AsyncQueryableExecuter.ToListAsync(query); + var entities = await AsyncExecuter.ToListAsync(query); return new PagedResultDto( totalCount, diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index df0aa25f0d..900343800f 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -9,9 +9,11 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Aspects; using Volo.Abp.Auditing; +using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; using Volo.Abp.Features; using Volo.Abp.Guids; +using Volo.Abp.Linq; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; @@ -59,6 +61,9 @@ namespace Volo.Abp.Application.Services protected IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); private IUnitOfWorkManager _unitOfWorkManager; + + protected IAsyncQueryableExecuter AsyncExecuter => LazyGetRequiredService(ref _asyncExecuter); + private IAsyncQueryableExecuter _asyncExecuter; protected Type ObjectMapperContext { get; set; } protected IObjectMapper ObjectMapper diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs index 36cade51be..9792cbd0e6 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Guids; +using Volo.Abp.Linq; using Volo.Abp.MultiTenancy; using Volo.Abp.Timing; @@ -38,6 +39,9 @@ namespace Volo.Abp.Domain.Services protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); private ICurrentTenant _currentTenant; + + protected IAsyncQueryableExecuter AsyncExecuter => LazyGetRequiredService(ref _asyncExecuter); + private IAsyncQueryableExecuter _asyncExecuter; protected ILogger Logger => _lazyLogger.Value; private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider.cs new file mode 100644 index 0000000000..f9ef8c86e0 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.Internal; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Linq; + +namespace Volo.Abp.EntityFrameworkCore +{ + public class EfCoreAsyncQueryableProvider : IAsyncQueryableProvider, ITransientDependency + { + public bool CanExecute(IQueryable queryable) + { + return queryable.Provider is EntityQueryProvider; + } + + public Task CountAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return queryable.CountAsync(cancellationToken); + } + + public Task> ToListAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return queryable.ToListAsync(cancellationToken); + } + + public Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return queryable.FirstOrDefaultAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index 956bc7853f..3ac6ab5239 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -15,8 +15,6 @@ using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.MongoDB; using Volo.Abp.MultiTenancy; -using Volo.Abp.Reflection; -using Volo.Abp.Threading; namespace Volo.Abp.Domain.Repositories.MongoDB { diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider.cs new file mode 100644 index 0000000000..8b0c56c812 --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Linq; +using MongoDB.Driver; +using MongoDB.Driver.Linq; + +namespace Volo.Abp.MongoDB +{ + public class MongoDbAsyncQueryableProvider : IAsyncQueryableProvider, ITransientDependency + { + public bool CanExecute(IQueryable queryable) + { + return queryable.Provider.GetType().Namespace?.StartsWith("MongoDB") ?? false; + } + + public Task CountAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return ((IMongoQueryable) queryable).CountAsync(cancellationToken); + } + + public Task> ToListAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return ((IMongoQueryable) queryable).ToListAsync(cancellationToken); + } + + public Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default) + { + return ((IMongoQueryable) queryable).FirstOrDefaultAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/AsyncQueryableExecuter.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/AsyncQueryableExecuter.cs new file mode 100644 index 0000000000..3548f81ad1 --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/AsyncQueryableExecuter.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Linq +{ + public class AsyncQueryableExecuter : IAsyncQueryableExecuter, ITransientDependency + { + protected IEnumerable Providers { get; } + + public AsyncQueryableExecuter(IEnumerable providers) + { + Providers = providers; + } + + public virtual Task CountAsync( + IQueryable queryable, + CancellationToken cancellationToken = default) + { + var provider = FindProvider(queryable); + return provider != null + ? provider.CountAsync(queryable, cancellationToken) + : Task.FromResult(queryable.Count()); + } + + public virtual Task> ToListAsync( + IQueryable queryable, + CancellationToken cancellationToken = default) + { + var provider = FindProvider(queryable); + return provider != null + ? provider.ToListAsync(queryable, cancellationToken) + : Task.FromResult(queryable.ToList()); + } + + public virtual Task FirstOrDefaultAsync( + IQueryable queryable, + CancellationToken cancellationToken = default) + { + var provider = FindProvider(queryable); + return provider != null + ? provider.FirstOrDefaultAsync(queryable, cancellationToken) + : Task.FromResult(queryable.FirstOrDefault()); + } + + protected virtual IAsyncQueryableProvider FindProvider(IQueryable queryable) + { + return Providers.FirstOrDefault(p => p.CanExecute(queryable)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs deleted file mode 100644 index 2a5da24a58..0000000000 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/DefaultAsyncQueryableExecuter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Volo.Abp.Linq -{ - public class DefaultAsyncQueryableExecuter : IAsyncQueryableExecuter - { - public static DefaultAsyncQueryableExecuter Instance { get; } = new DefaultAsyncQueryableExecuter(); - - private DefaultAsyncQueryableExecuter() - { - - } - - public Task CountAsync(IQueryable queryable) - { - return Task.FromResult(queryable.Count()); - } - - public Task> ToListAsync(IQueryable queryable) - { - return Task.FromResult(queryable.ToList()); - } - - public Task FirstOrDefaultAsync(IQueryable queryable) - { - return Task.FromResult(queryable.FirstOrDefault()); - } - - public Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken) - { - return Task.FromResult(queryable.FirstOrDefault()); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableExecuter.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableExecuter.cs index f2c34fd2ad..0a19318c40 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableExecuter.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableExecuter.cs @@ -5,17 +5,21 @@ using System.Threading.Tasks; namespace Volo.Abp.Linq { - /// - /// This interface is intended to be used by ABP. - /// public interface IAsyncQueryableExecuter { - Task CountAsync(IQueryable queryable); + Task CountAsync( + IQueryable queryable, + CancellationToken cancellationToken = default + ); - Task> ToListAsync(IQueryable queryable); + Task> ToListAsync( + IQueryable queryable, + CancellationToken cancellationToken = default + ); - Task FirstOrDefaultAsync(IQueryable queryable); - - Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken); + Task FirstOrDefaultAsync( + IQueryable queryable, + CancellationToken cancellationToken = default + ); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableProvider.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableProvider.cs new file mode 100644 index 0000000000..43d8b6d40e --- /dev/null +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Linq/IAsyncQueryableProvider.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.Linq +{ + public interface IAsyncQueryableProvider + { + bool CanExecute(IQueryable queryable); + + Task CountAsync(IQueryable queryable, CancellationToken cancellationToken = default); + + Task> ToListAsync(IQueryable queryable, CancellationToken cancellationToken = default); + + Task FirstOrDefaultAsync(IQueryable queryable, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs index 14bb90834e..bf72d3ce44 100644 --- a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs +++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Linq; using Volo.Abp.Modularity; namespace Volo.Abp.Threading @@ -8,7 +7,6 @@ namespace Volo.Abp.Threading { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddSingleton(DefaultAsyncQueryableExecuter.Instance); context.Services.AddSingleton(NullCancellationTokenProvider.Instance); context.Services.AddSingleton(typeof(IAmbientScopeProvider<>), typeof(AmbientDataContextAmbientScopeProvider<>)); } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs new file mode 100644 index 0000000000..9a6c0817f2 --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/EfCoreAsyncQueryableProvider_Tests.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.EntityFrameworkCore +{ + public class EfCoreAsyncQueryableProvider_Tests : EntityFrameworkCoreTestBase + { + private readonly IRepository _personRepository; + private readonly EfCoreAsyncQueryableProvider _efCoreAsyncQueryableProvider; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public EfCoreAsyncQueryableProvider_Tests() + { + _personRepository = GetRequiredService>(); + _efCoreAsyncQueryableProvider = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); + } + + [Fact] + public void Should_Accept_EfCore_Related_Queries() + { + var query = _personRepository.Where(p => p.Age > 0); + + _efCoreAsyncQueryableProvider.CanExecute(query).ShouldBeTrue(); + } + + [Fact] + public void Should_Not_Accept_Other_Providers() + { + var query = new[] {1, 2, 3}.AsQueryable().Where(x => x > 0); + + _efCoreAsyncQueryableProvider.CanExecute(query).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Execute_Queries() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var query = _personRepository.Where(p => p.Age > 0); + + (await _efCoreAsyncQueryableProvider.CountAsync(query) > 0).ShouldBeTrue(); + (await _efCoreAsyncQueryableProvider.FirstOrDefaultAsync(query)).ShouldNotBeNull(); + (await _efCoreAsyncQueryableProvider.ToListAsync(query)).Count.ShouldBeGreaterThan(0); + + await uow.CompleteAsync(); + } + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs new file mode 100644 index 0000000000..22013d9b63 --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbAsyncQueryableProvider_Tests.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.MongoDB +{ + [Collection(MongoTestCollection.Name)] + public class MongoDbAsyncQueryableProvider_Tests : MongoDbTestBase + { + private readonly IRepository _personRepository; + private readonly MongoDbAsyncQueryableProvider _mongoDbAsyncQueryableProvider; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public MongoDbAsyncQueryableProvider_Tests() + { + _personRepository = GetRequiredService>(); + _mongoDbAsyncQueryableProvider = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); + } + + [Fact] + public void Should_Accept_MongoDb_Related_Queries() + { + var query = _personRepository.Where(p => p.Age > 0); + + _mongoDbAsyncQueryableProvider.CanExecute(query).ShouldBeTrue(); + } + + [Fact] + public void Should_Not_Accept_Other_Providers() + { + var query = new[] {1, 2, 3}.AsQueryable().Where(x => x > 0); + + _mongoDbAsyncQueryableProvider.CanExecute(query).ShouldBeFalse(); + } + + [Fact] + public async Task Should_Execute_Queries() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var query = _personRepository.Where(p => p.Age > 0).OrderBy(p => p.Name); + + (await _mongoDbAsyncQueryableProvider.CountAsync(query) > 0).ShouldBeTrue(); + (await _mongoDbAsyncQueryableProvider.FirstOrDefaultAsync(query)).ShouldNotBeNull(); + (await _mongoDbAsyncQueryableProvider.ToListAsync(query)).Count.ShouldBeGreaterThan(0); + + await uow.CompleteAsync(); + } + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs index 81052d17cc..f7e7fde5e2 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/DistrictAppService.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.TestApp.Application protected override async Task GetEntityByIdAsync(DistrictKey id) { - return await AsyncQueryableExecuter.FirstOrDefaultAsync( + return await AsyncExecuter.FirstOrDefaultAsync( Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name) ); }