Revised IAsyncQueryableExecuter and implemented chain of responsibility to support multiple providers in a single application.

pull/4344/head
Halil İbrahim Kalkan 5 years ago
parent 6304c7c7cf
commit eca7781dc1

@ -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<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity
{
public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; }
protected IRepository<TEntity> Repository { get; }
@ -83,7 +81,6 @@ namespace Volo.Abp.Application.Services
protected AbstractKeyCrudAppService(IRepository<TEntity> repository)
{
Repository = repository;
AsyncQueryableExecuter = DefaultAsyncQueryableExecuter.Instance;
}
public virtual async Task<TGetOutputDto> 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<TGetListOutputDto>(
totalCount,

@ -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

@ -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<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);

@ -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<T>(IQueryable<T> queryable)
{
return queryable.Provider is EntityQueryProvider;
}
public Task<int> CountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return queryable.CountAsync(cancellationToken);
}
public Task<List<T>> ToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return queryable.ToListAsync(cancellationToken);
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return queryable.FirstOrDefaultAsync(cancellationToken);
}
}
}

@ -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
{

@ -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<T>(IQueryable<T> queryable)
{
return queryable.Provider.GetType().Namespace?.StartsWith("MongoDB") ?? false;
}
public Task<int> CountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return ((IMongoQueryable<T>) queryable).CountAsync(cancellationToken);
}
public Task<List<T>> ToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return ((IMongoQueryable<T>) queryable).ToListAsync(cancellationToken);
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default)
{
return ((IMongoQueryable<T>) queryable).FirstOrDefaultAsync(cancellationToken);
}
}
}

@ -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<IAsyncQueryableProvider> Providers { get; }
public AsyncQueryableExecuter(IEnumerable<IAsyncQueryableProvider> providers)
{
Providers = providers;
}
public virtual Task<int> CountAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default)
{
var provider = FindProvider(queryable);
return provider != null
? provider.CountAsync(queryable, cancellationToken)
: Task.FromResult(queryable.Count());
}
public virtual Task<List<T>> ToListAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default)
{
var provider = FindProvider(queryable);
return provider != null
? provider.ToListAsync(queryable, cancellationToken)
: Task.FromResult(queryable.ToList());
}
public virtual Task<T> FirstOrDefaultAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default)
{
var provider = FindProvider(queryable);
return provider != null
? provider.FirstOrDefaultAsync(queryable, cancellationToken)
: Task.FromResult(queryable.FirstOrDefault());
}
protected virtual IAsyncQueryableProvider FindProvider<T>(IQueryable<T> queryable)
{
return Providers.FirstOrDefault(p => p.CanExecute(queryable));
}
}
}

@ -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<int> CountAsync<T>(IQueryable<T> queryable)
{
return Task.FromResult(queryable.Count());
}
public Task<List<T>> ToListAsync<T>(IQueryable<T> queryable)
{
return Task.FromResult(queryable.ToList());
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable)
{
return Task.FromResult(queryable.FirstOrDefault());
}
public Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken)
{
return Task.FromResult(queryable.FirstOrDefault());
}
}
}

@ -5,17 +5,21 @@ using System.Threading.Tasks;
namespace Volo.Abp.Linq
{
/// <summary>
/// This interface is intended to be used by ABP.
/// </summary>
public interface IAsyncQueryableExecuter
{
Task<int> CountAsync<T>(IQueryable<T> queryable);
Task<int> CountAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default
);
Task<List<T>> ToListAsync<T>(IQueryable<T> queryable);
Task<List<T>> ToListAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default
);
Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable);
Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken);
Task<T> FirstOrDefaultAsync<T>(
IQueryable<T> queryable,
CancellationToken cancellationToken = default
);
}
}

@ -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<T>(IQueryable<T> queryable);
Task<int> CountAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default);
Task<List<T>> ToListAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default);
Task<T> FirstOrDefaultAsync<T>(IQueryable<T> queryable, CancellationToken cancellationToken = default);
}
}

@ -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<IAsyncQueryableExecuter>(DefaultAsyncQueryableExecuter.Instance);
context.Services.AddSingleton<ICancellationTokenProvider>(NullCancellationTokenProvider.Instance);
context.Services.AddSingleton(typeof(IAmbientScopeProvider<>), typeof(AmbientDataContextAmbientScopeProvider<>));
}

@ -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<Person, Guid> _personRepository;
private readonly EfCoreAsyncQueryableProvider _efCoreAsyncQueryableProvider;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public EfCoreAsyncQueryableProvider_Tests()
{
_personRepository = GetRequiredService<IRepository<Person, Guid>>();
_efCoreAsyncQueryableProvider = GetRequiredService<EfCoreAsyncQueryableProvider>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
[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();
}
}
}
}

@ -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<Person, Guid> _personRepository;
private readonly MongoDbAsyncQueryableProvider _mongoDbAsyncQueryableProvider;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public MongoDbAsyncQueryableProvider_Tests()
{
_personRepository = GetRequiredService<IRepository<Person, Guid>>();
_mongoDbAsyncQueryableProvider = GetRequiredService<MongoDbAsyncQueryableProvider>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
[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();
}
}
}
}

@ -22,7 +22,7 @@ namespace Volo.Abp.TestApp.Application
protected override async Task<District> GetEntityByIdAsync(DistrictKey id)
{
return await AsyncQueryableExecuter.FirstOrDefaultAsync(
return await AsyncExecuter.FirstOrDefaultAsync(
Repository.Where(d => d.CityId == id.CityId && d.Name == id.Name)
);
}

Loading…
Cancel
Save