using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace Bussiness.MongoDB
{
///
/// 自定义MongoDB上下文提供程序,禁用事务支持
/// 用于解决Standalone MongoDB服务器不支持事务的问题
///
public class NonTransactionalMongoDbContextProvider : IMongoDbContextProvider
where TMongoDbContext : class, IAbpMongoDbContext
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly ICurrentTenant _currentTenant;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger> _logger;
public NonTransactionalMongoDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant,
IServiceProvider serviceProvider,
ILogger> logger)
{
_unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
_cancellationTokenProvider = cancellationTokenProvider;
_currentTenant = currentTenant;
_serviceProvider = serviceProvider;
_logger = logger;
}
public async Task GetDbContextAsync(CancellationToken cancellationToken = default)
{
var unitOfWork = _unitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException("A DbContext can only be created inside a unit of work!");
}
var targetTenantId = _currentTenant.Id;
var connectionString = await ResolveConnectionStringAsync(targetTenantId);
var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}";
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
{
// 创建不带事务的数据库API
databaseApi = new MongoDbDatabaseApi(await CreateDbContextAsync(unitOfWork, connectionString, cancellationToken));
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return ((MongoDbDatabaseApi)databaseApi).DbContext.As();
}
[Obsolete("Use GetDbContextAsync method.")]
public TMongoDbContext GetDbContext()
{
return AsyncHelper.RunSync(() => GetDbContextAsync());
}
private Task CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionString, CancellationToken cancellationToken)
{
var dbContext = unitOfWork.ServiceProvider.GetRequiredService();
var mongoUrl = new MongoUrl(connectionString);
var databaseName = mongoUrl.DatabaseName;
if (databaseName.IsNullOrWhiteSpace())
{
databaseName = ConnectionStringNameAttribute.GetConnStringName();
}
// 创建MongoDB客户端(不使用事务)
var client = new MongoClient(mongoUrl);
var database = client.GetDatabase(databaseName);
// 初始化DbContext,不传入session(即不使用事务)
dbContext.ToAbpMongoDbContext().InitializeDatabase(database, client, null);
_logger.LogDebug("MongoDB context created without transaction support for standalone server.");
return Task.FromResult(dbContext);
}
private async Task ResolveConnectionStringAsync(Guid? tenantId)
{
if (tenantId == _currentTenant.Id)
{
return await _connectionStringResolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName());
}
using (_currentTenant.Change(tenantId))
{
return await _connectionStringResolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName());
}
}
}
///
/// MongoDB数据库API包装类
///
public class MongoDbDatabaseApi : IDatabaseApi
{
public IAbpMongoDbContext DbContext { get; }
public MongoDbDatabaseApi(IAbpMongoDbContext dbContext)
{
DbContext = dbContext;
}
}
}