Browse Source

Implemented creating dbcontext in async way.

pull/6809/head
Halil İbrahim Kalkan 6 years ago
parent
commit
cfecb1f8d9
  1. 9
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs
  2. 20
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
  3. 113
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  4. 10
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreRepository.cs
  5. 8
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IDbContextProvider.cs
  6. 104
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  7. 6
      framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs
  8. 14
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs
  9. 161
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  10. 13
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDbContextProvider.cs
  11. 109
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs
  12. 6
      framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs

9
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IReadOnlyRepository.cs

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Linq;
@ -11,9 +12,17 @@ namespace Volo.Abp.Domain.Repositories
{
IAsyncQueryableExecuter AsyncExecuter { get; }
[Obsolete("Use WithDetailsAsync method.")]
IQueryable<TEntity> WithDetails();
[Obsolete("Use WithDetailsAsync method.")]
IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors);
Task<IQueryable<TEntity>> WithDetailsAsync(); //TODO: CancellationToken
Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors); //TODO: CancellationToken
Task<IQueryable<TEntity>> GetQueryableAsync(); //TODO: CancellationToken
}
public interface IReadOnlyRepository<TEntity, TKey> : IReadOnlyRepository<TEntity>, IReadOnlyBasicRepository<TEntity, TKey>

20
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs

@ -24,34 +24,54 @@ namespace Volo.Abp.Domain.Repositories
public IUnitOfWorkManager UnitOfWorkManager { get; set; }
[Obsolete("This method will be removed in future versions.")]
public virtual Type ElementType => GetQueryable().ElementType;
[Obsolete("This method will be removed in future versions.")]
public virtual Expression Expression => GetQueryable().Expression;
[Obsolete("This method will be removed in future versions.")]
public virtual IQueryProvider Provider => GetQueryable().Provider;
[Obsolete("Use WithDetailsAsync method.")]
public virtual IQueryable<TEntity> WithDetails()
{
return GetQueryable();
}
[Obsolete("Use WithDetailsAsync method.")]
public virtual IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
return GetQueryable();
}
public virtual Task<IQueryable<TEntity>> WithDetailsAsync()
{
return GetQueryableAsync();
}
public virtual Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
{
return GetQueryableAsync();
}
[Obsolete("This method will be removed in future versions.")]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
[Obsolete("This method will be removed in future versions.")]
public IEnumerator<TEntity> GetEnumerator()
{
return GetQueryable().GetEnumerator();
}
[Obsolete("Use GetQueryableAsync method.")]
protected abstract IQueryable<TEntity> GetQueryable();
public abstract Task<IQueryable<TEntity>> GetQueryableAsync();
public abstract Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,

113
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

@ -19,11 +19,34 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity
{
public virtual DbSet<TEntity> DbSet => DbContext.Set<TEntity>();
[Obsolete("Use GetDbContextAsync() method.")]
protected virtual TDbContext DbContext => _dbContextProvider.GetDbContext();
[Obsolete("Use GetDbContextAsync() method.")]
DbContext IEfCoreRepository<TEntity>.DbContext => DbContext.As<DbContext>();
protected virtual TDbContext DbContext => _dbContextProvider.GetDbContext();
async Task<DbContext> IEfCoreRepository<TEntity>.GetDbContextAsync()
{
return await GetDbContextAsync() as DbContext;
}
protected virtual Task<TDbContext> GetDbContextAsync()
{
return _dbContextProvider.GetDbContextAsync();
}
[Obsolete("Use GetDbSetAsync() method.")]
public virtual DbSet<TEntity> DbSet => DbContext.Set<TEntity>();
Task<DbSet<TEntity>> IEfCoreRepository<TEntity>.GetDbSetAsync()
{
return GetDbSetAsync();
}
protected async Task<DbSet<TEntity>> GetDbSetAsync()
{
return (await GetDbContextAsync()).Set<TEntity>();
}
protected virtual AbpEntityOptions<TEntity> AbpEntityOptions => _entityOptionsLazy.Value;
@ -45,64 +68,72 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
);
}
public async override Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
public override async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
CheckAndSetId(entity);
var savedEntity = DbSet.Add(entity).Entity;
var dbContext = await GetDbContextAsync();
var savedEntity = (await dbContext.Set<TEntity>().AddAsync(entity, GetCancellationToken(cancellationToken))).Entity;
if (autoSave)
{
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
return savedEntity;
}
public async override Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
public override async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
DbContext.Attach(entity);
var dbContext = await GetDbContextAsync();
var updatedEntity = DbContext.Update(entity).Entity;
dbContext.Attach(entity);
var updatedEntity = dbContext.Update(entity).Entity;
if (autoSave)
{
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
return updatedEntity;
}
public async override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
public override async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
DbSet.Remove(entity);
var dbContext = await GetDbContextAsync();
dbContext.Set<TEntity>().Remove(entity);
if (autoSave)
{
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
}
public async override Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
public override async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
{
return includeDetails
? await WithDetails().ToListAsync(GetCancellationToken(cancellationToken))
: await DbSet.ToListAsync(GetCancellationToken(cancellationToken));
? await (await WithDetailsAsync()).ToListAsync(GetCancellationToken(cancellationToken))
: await (await GetDbSetAsync()).ToListAsync(GetCancellationToken(cancellationToken));
}
public async override Task<long> GetCountAsync(CancellationToken cancellationToken = default)
public override async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await DbSet.LongCountAsync(GetCancellationToken(cancellationToken));
return await (await GetDbSetAsync()).LongCountAsync(GetCancellationToken(cancellationToken));
}
public async override Task<List<TEntity>> GetPagedListAsync(
public override async Task<List<TEntity>> GetPagedListAsync(
int skipCount,
int maxResultCount,
string sorting,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var queryable = includeDetails ? WithDetails() : DbSet;
var queryable = includeDetails
? await WithDetailsAsync()
: await GetDbSetAsync();
return await queryable
.OrderBy(sorting)
@ -110,39 +141,48 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
.ToListAsync(GetCancellationToken(cancellationToken));
}
[Obsolete("Use GetQueryableAsync method.")]
protected override IQueryable<TEntity> GetQueryable()
{
return DbSet.AsQueryable();
}
public async override Task<TEntity> FindAsync(
public override async Task<IQueryable<TEntity>> GetQueryableAsync()
{
return (await GetDbSetAsync()).AsQueryable();
}
public override async Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return includeDetails
? await WithDetails()
? await (await WithDetailsAsync())
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken))
: await DbSet
: await (await GetDbSetAsync())
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
}
public async override Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
public override async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
{
var entities = await GetQueryable()
var dbContext = await GetDbContextAsync();
var dbSet = dbContext.Set<TEntity>();
var entities = await dbSet
.Where(predicate)
.ToListAsync(GetCancellationToken(cancellationToken));
foreach (var entity in entities)
{
DbSet.Remove(entity);
dbSet.Remove(entity);
}
if (autoSave)
{
await DbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
}
}
@ -152,7 +192,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
CancellationToken cancellationToken = default)
where TProperty : class
{
await DbContext
await (await GetDbContextAsync())
.Entry(entity)
.Collection(propertyExpression)
.LoadAsync(GetCancellationToken(cancellationToken));
@ -164,12 +204,13 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
CancellationToken cancellationToken = default)
where TProperty : class
{
await DbContext
await (await GetDbContextAsync())
.Entry(entity)
.Reference(propertyExpression)
.LoadAsync(GetCancellationToken(cancellationToken));
}
[Obsolete("Use WithDetailsAsync")]
public override IQueryable<TEntity> WithDetails()
{
if (AbpEntityOptions.DefaultWithDetailsFunc == null)
@ -180,6 +221,17 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return AbpEntityOptions.DefaultWithDetailsFunc(GetQueryable());
}
public override async Task<IQueryable<TEntity>> WithDetailsAsync()
{
if (AbpEntityOptions.DefaultWithDetailsFunc == null)
{
return await base.WithDetailsAsync();
}
return AbpEntityOptions.DefaultWithDetailsFunc(await GetQueryableAsync());
}
[Obsolete("Use WithDetailsAsync method.")]
public override IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
{
var query = GetQueryable();
@ -195,6 +247,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return query;
}
[Obsolete("This method will be deleted in future versions.")]
public IAsyncEnumerator<TEntity> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return DbSet.AsAsyncEnumerable().GetAsyncEnumerator(cancellationToken);
@ -251,8 +304,8 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
public virtual async Task<TEntity> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
{
return includeDetails
? await WithDetails().FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken))
: await DbSet.FindAsync(new object[] {id}, GetCancellationToken(cancellationToken));
? await (await WithDetailsAsync()).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken))
: await (await GetDbSetAsync()).FindAsync(new object[] {id}, GetCancellationToken(cancellationToken));
}
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)

10
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreRepository.cs

@ -1,3 +1,5 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
@ -6,9 +8,15 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
public interface IEfCoreRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
[Obsolete("Use GetDbContextAsync() method.")]
DbContext DbContext { get; }
[Obsolete("Use GetDbSetAsync() method.")]
DbSet<TEntity> DbSet { get; }
Task<DbContext> GetDbContextAsync();
Task<DbSet<TEntity>> GetDbSetAsync();
}
public interface IEfCoreRepository<TEntity, TKey> : IEfCoreRepository<TEntity>, IRepository<TEntity, TKey>
@ -16,4 +24,4 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
}
}
}

8
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IDbContextProvider.cs

@ -1,8 +1,12 @@
using System.Threading.Tasks;
namespace Volo.Abp.EntityFrameworkCore
{
public interface IDbContextProvider<out TDbContext>
public interface IDbContextProvider<TDbContext>
where TDbContext : IEfCoreDbContext
{
TDbContext GetDbContext();
Task<TDbContext> GetDbContextAsync();
}
}
}

104
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs

@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
@ -47,6 +47,33 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
return ((EfCoreDatabaseApi<TDbContext>)databaseApi).DbContext;
}
public async Task<TDbContext> GetDbContextAsync()
{
var unitOfWork = _unitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException("A DbContext can only be created inside a unit of work!");
}
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
var connectionString = _connectionStringResolver.Resolve(connectionStringName);
var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}";
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
{
databaseApi = new EfCoreDatabaseApi<TDbContext>(
await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
);
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return ((EfCoreDatabaseApi<TDbContext>)databaseApi).DbContext;
}
private TDbContext CreateDbContext(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
{
var creationContext = new DbContextCreationContext(connectionStringName, connectionString);
@ -67,6 +94,26 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
}
}
private async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
{
var creationContext = new DbContextCreationContext(connectionStringName, connectionString);
using (DbContextCreationContext.Use(creationContext))
{
var dbContext = await CreateDbContextAsync(unitOfWork);
if (dbContext is IAbpEfCoreDbContext abpEfCoreDbContext)
{
abpEfCoreDbContext.Initialize(
new AbpEfCoreDbContextInitializationContext(
unitOfWork
)
);
}
return dbContext;
}
}
private TDbContext CreateDbContext(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
@ -74,7 +121,14 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
}
public TDbContext CreateDbContextWithTransaction(IUnitOfWork unitOfWork)
private async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
? await CreateDbContextWithTransactionAsync(unitOfWork)
: unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
}
private TDbContext CreateDbContextWithTransaction(IUnitOfWork unitOfWork)
{
var transactionApiKey = $"EntityFrameworkCore_{DbContextCreationContext.Current.ConnectionString}";
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as EfCoreTransactionApi;
@ -117,5 +171,49 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
return dbContext;
}
}
private async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
{
var transactionApiKey = $"EntityFrameworkCore_{DbContextCreationContext.Current.ConnectionString}";
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as EfCoreTransactionApi;
if (activeTransaction == null)
{
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
var dbtransaction = unitOfWork.Options.IsolationLevel.HasValue
? await dbContext.Database.BeginTransactionAsync(unitOfWork.Options.IsolationLevel.Value)
: await dbContext.Database.BeginTransactionAsync();
unitOfWork.AddTransactionApi(
transactionApiKey,
new EfCoreTransactionApi(
dbtransaction,
dbContext
)
);
return dbContext;
}
else
{
DbContextCreationContext.Current.ExistingConnection = activeTransaction.DbContextTransaction.GetDbTransaction().Connection;
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
if (dbContext.As<DbContext>().HasRelationalTransactionManager())
{
await dbContext.Database.UseTransactionAsync(activeTransaction.DbContextTransaction.GetDbTransaction());
}
else
{
await dbContext.Database.BeginTransactionAsync(); //TODO: Why not using the new created transaction?
}
activeTransaction.AttendedDbContexts.Add(dbContext);
return dbContext;
}
}
}
}
}

6
framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs

@ -46,11 +46,17 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
}
[Obsolete("This method will be removed in future versions.")]
protected override IQueryable<TEntity> GetQueryable()
{
return ApplyDataFilters(Collection.AsQueryable());
}
public override Task<IQueryable<TEntity>> GetQueryableAsync()
{
return Task.FromResult(ApplyDataFilters(Collection.AsQueryable()));
}
protected virtual async Task TriggerDomainEventsAsync(object entity)
{
var generatesDomainEventsEntity = entity as IGeneratesDomainEvents;

14
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs

@ -1,4 +1,7 @@
using MongoDB.Driver;
using System;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Entities;
@ -7,11 +10,20 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public interface IMongoDbRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
[Obsolete("Use GetDatabaseAsync method.")]
IMongoDatabase Database { get; }
Task<IMongoDatabase> GetDatabaseAsync(CancellationToken cancellationToken = default);
[Obsolete("Use GetCollectionAsync method.")]
IMongoCollection<TEntity> Collection { get; }
Task<IMongoCollection<TEntity>> GetCollectionAsync(CancellationToken cancellationToken = default);
[Obsolete("Use GetMongoQueryableAsync method.")]
IMongoQueryable<TEntity> GetMongoQueryable();
Task<IMongoQueryable<TEntity>> GetMongoQueryableAsync(CancellationToken cancellationToken = default);
}
public interface IMongoDbRepository<TEntity, TKey> : IMongoDbRepository<TEntity>, IRepository<TEntity, TKey>

161
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -25,13 +25,37 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
where TMongoDbContext : IAbpMongoDbContext
where TEntity : class, IEntity
{
[Obsolete("Use GetCollectionAsync method.")]
public virtual IMongoCollection<TEntity> Collection => DbContext.Collection<TEntity>();
public async Task<IMongoCollection<TEntity>> GetCollectionAsync(CancellationToken cancellationToken = default)
{
return (await GetDbContextAsync(GetCancellationToken(cancellationToken))).Collection<TEntity>();
}
[Obsolete("Use GetDatabaseAsync method.")]
public virtual IMongoDatabase Database => DbContext.Database;
public virtual IClientSessionHandle SessionHandle => DbContext.SessionHandle;
public async Task<IMongoDatabase> GetDatabaseAsync(CancellationToken cancellationToken = default)
{
return (await GetDbContextAsync(GetCancellationToken(cancellationToken))).Database;
}
[Obsolete("Use GetSessionHandleAsync method.")]
protected virtual IClientSessionHandle SessionHandle => DbContext.SessionHandle;
protected async Task<IClientSessionHandle> GetSessionHandleAsync(CancellationToken cancellationToken = default)
{
return (await GetDbContextAsync(GetCancellationToken(cancellationToken))).SessionHandle;
}
[Obsolete("Use GetDbContextAsync method.")]
protected virtual TMongoDbContext DbContext => DbContextProvider.GetDbContext();
public virtual TMongoDbContext DbContext => DbContextProvider.GetDbContext();
protected Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
{
return DbContextProvider.GetDbContextAsync(GetCancellationToken(cancellationToken));
}
protected IMongoDbContextProvider<TMongoDbContext> DbContextProvider { get; }
@ -55,24 +79,27 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
GuidGenerator = SimpleGuidGenerator.Instance;
}
public async override Task<TEntity> InsertAsync(
public override async Task<TEntity> InsertAsync(
TEntity entity,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
await ApplyAbpConceptsForAddedEntityAsync(entity);
if (SessionHandle != null)
var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
var collection = dbContext.Collection<TEntity>();
if (dbContext.SessionHandle != null)
{
await Collection.InsertOneAsync(
SessionHandle,
await collection.InsertOneAsync(
dbContext.SessionHandle,
entity,
cancellationToken: GetCancellationToken(cancellationToken)
);
}
else
{
await Collection.InsertOneAsync(
await collection.InsertOneAsync(
entity,
cancellationToken: GetCancellationToken(cancellationToken)
);
@ -81,7 +108,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
return entity;
}
public async override Task<TEntity> UpdateAsync(
public override async Task<TEntity> UpdateAsync(
TEntity entity,
bool autoSave = false,
CancellationToken cancellationToken = default)
@ -103,20 +130,21 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
ReplaceOneResult result;
if (SessionHandle != null)
var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
var collection = dbContext.Collection<TEntity>();
if (dbContext.SessionHandle != null)
{
result = await Collection.ReplaceOneAsync(
SessionHandle,
result = await collection.ReplaceOneAsync(
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
cancellationToken: GetCancellationToken(cancellationToken)
);
}
else
{
result = await Collection.ReplaceOneAsync(
result = await collection.ReplaceOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
cancellationToken: GetCancellationToken(cancellationToken)
@ -131,7 +159,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
return entity;
}
public async override Task DeleteAsync(
public override async Task DeleteAsync(
TEntity entity,
bool autoSave = false,
CancellationToken cancellationToken = default)
@ -139,15 +167,18 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
await ApplyAbpConceptsForDeletedEntityAsync(entity);
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
var collection = dbContext.Collection<TEntity>();
if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity))
{
softDeleteEntity.IsDeleted = true;
ReplaceOneResult result;
if (SessionHandle != null)
if (dbContext.SessionHandle != null)
{
result = await Collection.ReplaceOneAsync(
SessionHandle,
result = await collection.ReplaceOneAsync(
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
cancellationToken: GetCancellationToken(cancellationToken)
@ -155,7 +186,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
}
else
{
result = await Collection.ReplaceOneAsync(
result = await collection.ReplaceOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
cancellationToken: GetCancellationToken(cancellationToken)
@ -171,17 +202,17 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{
DeleteResult result;
if (SessionHandle != null)
if (dbContext.SessionHandle != null)
{
result = await Collection.DeleteOneAsync(
SessionHandle,
result = await collection.DeleteOneAsync(
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
cancellationToken: GetCancellationToken(cancellationToken)
);
}
else
{
result = await Collection.DeleteOneAsync(
result = await collection.DeleteOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
GetCancellationToken(cancellationToken)
);
@ -194,38 +225,44 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
}
}
public async override Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
public override async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
{
return await GetMongoQueryable().ToListAsync(GetCancellationToken(cancellationToken));
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken)).ToListAsync(cancellationToken);
}
public async override Task<long> GetCountAsync(CancellationToken cancellationToken = default)
public override async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
{
return await GetMongoQueryable().LongCountAsync(GetCancellationToken(cancellationToken));
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken)).LongCountAsync(cancellationToken);
}
public async override Task<List<TEntity>> GetPagedListAsync(
public override async Task<List<TEntity>> GetPagedListAsync(
int skipCount,
int maxResultCount,
string sorting,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
cancellationToken = GetCancellationToken(cancellationToken);
return await (await GetMongoQueryableAsync(cancellationToken))
.OrderBy(sorting)
.As<IMongoQueryable<TEntity>>()
.PageBy<TEntity, IMongoQueryable<TEntity>>(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
.ToListAsync(cancellationToken);
}
public async override Task DeleteAsync(
public override async Task DeleteAsync(
Expression<Func<TEntity, bool>> predicate,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
var entities = await GetMongoQueryable()
cancellationToken = GetCancellationToken(cancellationToken);
var entities = await (await GetMongoQueryableAsync(cancellationToken))
.Where(predicate)
.ToListAsync(GetCancellationToken(cancellationToken));
.ToListAsync(cancellationToken);
foreach (var entity in entities)
{
@ -233,25 +270,45 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
}
}
[Obsolete("Use GetQueryableAsync method.")]
protected override IQueryable<TEntity> GetQueryable()
{
return GetMongoQueryable();
}
public async override Task<TEntity> FindAsync(
public override async Task<IQueryable<TEntity>> GetQueryableAsync()
{
return await GetMongoQueryableAsync();
}
public override async Task<TEntity> FindAsync(
Expression<Func<TEntity, bool>> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
return await (await GetMongoQueryableAsync(cancellationToken))
.Where(predicate)
.SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
}
[Obsolete("Use GetMongoQueryableAsync method.")]
public virtual IMongoQueryable<TEntity> GetMongoQueryable()
{
return ApplyDataFilters(SessionHandle != null ? Collection.AsQueryable(SessionHandle) : Collection.AsQueryable());
}
public async Task<IMongoQueryable<TEntity>> GetMongoQueryableAsync(CancellationToken cancellationToken = default)
{
var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection<TEntity>();
return ApplyDataFilters(
dbContext.SessionHandle != null
? collection.AsQueryable(dbContext.SessionHandle)
: collection.AsQueryable()
);
}
protected virtual bool IsHardDeleted(TEntity entity)
{
var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet<IEntity>;
@ -393,30 +450,19 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
throw new AbpDbConcurrencyException("Database operation expected to affect 1 row but actually affected 0 row. Data may have been modified or deleted since entities were loaded. This exception has been thrown on optimistic concurrency check.");
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <returns></returns>
[Obsolete("This method will be removed in future versions.")]
public QueryableExecutionModel GetExecutionModel()
{
return GetMongoQueryable().GetExecutionModel();
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[Obsolete("This method will be removed in future versions.")]
public IAsyncCursor<TEntity> ToCursor(CancellationToken cancellationToken = new CancellationToken())
{
return GetMongoQueryable().ToCursor(cancellationToken);
}
/// <summary>
/// IMongoQueryable<TEntity>
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[Obsolete("This method will be removed in future versions.")]
public Task<IAsyncCursor<TEntity>> ToCursorAsync(CancellationToken cancellationToken = new CancellationToken())
{
return GetMongoQueryable().ToCursorAsync(cancellationToken);
@ -457,16 +503,21 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
if (SessionHandle != null)
cancellationToken = GetCancellationToken(cancellationToken);
var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection<TEntity>();
if (dbContext.SessionHandle != null)
{
return await Collection
.Find(SessionHandle, RepositoryFilterer.CreateEntityFilter(id, true))
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
return await collection
.Find(dbContext.SessionHandle, RepositoryFilterer.CreateEntityFilter(id, true))
.FirstOrDefaultAsync(cancellationToken);
}
return await Collection
return await collection
.Find(RepositoryFilterer.CreateEntityFilter(id, true))
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
.FirstOrDefaultAsync(cancellationToken);
}
public virtual Task DeleteAsync(

13
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDbContextProvider.cs

@ -1,8 +1,15 @@
namespace Volo.Abp.MongoDB
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Volo.Abp.MongoDB
{
public interface IMongoDbContextProvider<out TMongoDbContext>
public interface IMongoDbContextProvider<TMongoDbContext>
where TMongoDbContext : IAbpMongoDbContext
{
[Obsolete("Use CreateDbContextAsync")]
TMongoDbContext GetDbContext();
Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default);
}
}
}

109
framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs

@ -1,4 +1,6 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Bson;
using MongoDB.Driver;
@ -21,6 +23,7 @@ namespace Volo.Abp.Uow.MongoDB
_connectionStringResolver = connectionStringResolver;
}
[Obsolete("Use CreateDbContextAsync")]
public TMongoDbContext GetDbContext()
{
var unitOfWork = _unitOfWorkManager.Current;
@ -48,6 +51,46 @@ namespace Volo.Abp.Uow.MongoDB
return ((MongoDbDatabaseApi<TMongoDbContext>) databaseApi).DbContext;
}
public async Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
{
var unitOfWork = _unitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException(
$"A {nameof(IMongoDatabase)} instance can only be created inside a unit of work!");
}
var connectionString = _connectionStringResolver.Resolve<TMongoDbContext>();
var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}";
var mongoUrl = new MongoUrl(connectionString);
var databaseName = mongoUrl.DatabaseName;
if (databaseName.IsNullOrWhiteSpace())
{
databaseName = ConnectionStringNameAttribute.GetConnStringName<TMongoDbContext>();
}
//TODO: Create only single MongoDbClient per connection string in an application (extract MongoClientCache for example).
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
{
databaseApi = new MongoDbDatabaseApi<TMongoDbContext>(
await CreateDbContextAsync(
unitOfWork,
mongoUrl,
databaseName,
cancellationToken
)
);
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return ((MongoDbDatabaseApi<TMongoDbContext>) databaseApi).DbContext;
}
[Obsolete("Use CreateDbContextAsync")]
private TMongoDbContext CreateDbContext(IUnitOfWork unitOfWork, MongoUrl mongoUrl, string databaseName)
{
var client = new MongoClient(mongoUrl);
@ -64,7 +107,34 @@ namespace Volo.Abp.Uow.MongoDB
return dbContext;
}
public TMongoDbContext CreateDbContextWithTransaction(
private async Task<TMongoDbContext> CreateDbContextAsync(
IUnitOfWork unitOfWork,
MongoUrl mongoUrl,
string databaseName,
CancellationToken cancellationToken = default)
{
var client = new MongoClient(mongoUrl);
var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional)
{
return await CreateDbContextWithTransactionAsync(
unitOfWork,
mongoUrl,
client,
database,
cancellationToken
);
}
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TMongoDbContext>();
dbContext.ToAbpMongoDbContext().InitializeDatabase(database, client, null);
return dbContext;
}
[Obsolete("Use CreateDbContextWithTransactionAsync")]
private TMongoDbContext CreateDbContextWithTransaction(
IUnitOfWork unitOfWork,
MongoUrl url,
MongoClient client,
@ -99,5 +169,42 @@ namespace Volo.Abp.Uow.MongoDB
return dbContext;
}
private async Task<TMongoDbContext> CreateDbContextWithTransactionAsync(
IUnitOfWork unitOfWork,
MongoUrl url,
MongoClient client,
IMongoDatabase database,
CancellationToken cancellationToken = default)
{
var transactionApiKey = $"MongoDb_{url}";
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as MongoDbTransactionApi;
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TMongoDbContext>();
if (activeTransaction?.SessionHandle == null)
{
var session = await client.StartSessionAsync(cancellationToken: cancellationToken);
if (unitOfWork.Options.Timeout.HasValue)
{
session.AdvanceOperationTime(new BsonTimestamp(unitOfWork.Options.Timeout.Value));
}
session.StartTransaction();
unitOfWork.AddTransactionApi(
transactionApiKey,
new MongoDbTransactionApi(session)
);
dbContext.ToAbpMongoDbContext().InitializeDatabase(database, client, session);
}
else
{
dbContext.ToAbpMongoDbContext().InitializeDatabase(database, client, activeTransaction.SessionHandle);
}
return dbContext;
}
}
}

6
framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs

@ -241,11 +241,17 @@ namespace Volo.Abp.Domain.Repositories
where TEntity : class, IEntity
{
[Obsolete("Use GetQueryableAsync method.")]
protected override IQueryable<TEntity> GetQueryable()
{
throw new NotImplementedException();
}
public override Task<IQueryable<TEntity>> GetQueryableAsync()
{
throw new NotImplementedException();
}
public override Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();

Loading…
Cancel
Save