NonTransactionalMongoDbContextProvider.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using Microsoft.Extensions.DependencyInjection;
  2. using Microsoft.Extensions.Logging;
  3. using MongoDB.Driver;
  4. using System;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Volo.Abp;
  8. using Volo.Abp.Data;
  9. using Volo.Abp.MongoDB;
  10. using Volo.Abp.MultiTenancy;
  11. using Volo.Abp.Threading;
  12. using Volo.Abp.Uow;
  13. namespace Bussiness.MongoDB
  14. {
  15. /// <summary>
  16. /// 自定义MongoDB上下文提供程序,禁用事务支持
  17. /// 用于解决Standalone MongoDB服务器不支持事务的问题
  18. /// </summary>
  19. public class NonTransactionalMongoDbContextProvider<TMongoDbContext> : IMongoDbContextProvider<TMongoDbContext>
  20. where TMongoDbContext : class, IAbpMongoDbContext
  21. {
  22. private readonly IUnitOfWorkManager _unitOfWorkManager;
  23. private readonly IConnectionStringResolver _connectionStringResolver;
  24. private readonly ICancellationTokenProvider _cancellationTokenProvider;
  25. private readonly ICurrentTenant _currentTenant;
  26. private readonly IServiceProvider _serviceProvider;
  27. private readonly ILogger<NonTransactionalMongoDbContextProvider<TMongoDbContext>> _logger;
  28. public NonTransactionalMongoDbContextProvider(
  29. IUnitOfWorkManager unitOfWorkManager,
  30. IConnectionStringResolver connectionStringResolver,
  31. ICancellationTokenProvider cancellationTokenProvider,
  32. ICurrentTenant currentTenant,
  33. IServiceProvider serviceProvider,
  34. ILogger<NonTransactionalMongoDbContextProvider<TMongoDbContext>> logger)
  35. {
  36. _unitOfWorkManager = unitOfWorkManager;
  37. _connectionStringResolver = connectionStringResolver;
  38. _cancellationTokenProvider = cancellationTokenProvider;
  39. _currentTenant = currentTenant;
  40. _serviceProvider = serviceProvider;
  41. _logger = logger;
  42. }
  43. public async Task<TMongoDbContext> GetDbContextAsync(CancellationToken cancellationToken = default)
  44. {
  45. var unitOfWork = _unitOfWorkManager.Current;
  46. if (unitOfWork == null)
  47. {
  48. throw new AbpException("A DbContext can only be created inside a unit of work!");
  49. }
  50. var targetTenantId = _currentTenant.Id;
  51. var connectionString = await ResolveConnectionStringAsync(targetTenantId);
  52. var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}";
  53. var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
  54. if (databaseApi == null)
  55. {
  56. // 创建不带事务的数据库API
  57. databaseApi = new MongoDbDatabaseApi(await CreateDbContextAsync(unitOfWork, connectionString, cancellationToken));
  58. unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
  59. }
  60. return ((MongoDbDatabaseApi)databaseApi).DbContext.As<TMongoDbContext>();
  61. }
  62. [Obsolete("Use GetDbContextAsync method.")]
  63. public TMongoDbContext GetDbContext()
  64. {
  65. return AsyncHelper.RunSync(() => GetDbContextAsync());
  66. }
  67. private Task<IAbpMongoDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionString, CancellationToken cancellationToken)
  68. {
  69. var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TMongoDbContext>();
  70. var mongoUrl = new MongoUrl(connectionString);
  71. var databaseName = mongoUrl.DatabaseName;
  72. if (databaseName.IsNullOrWhiteSpace())
  73. {
  74. databaseName = ConnectionStringNameAttribute.GetConnStringName<TMongoDbContext>();
  75. }
  76. // 创建MongoDB客户端(不使用事务)
  77. var client = new MongoClient(mongoUrl);
  78. var database = client.GetDatabase(databaseName);
  79. // 初始化DbContext,不传入session(即不使用事务)
  80. dbContext.ToAbpMongoDbContext().InitializeDatabase(database, client, null);
  81. _logger.LogDebug("MongoDB context created without transaction support for standalone server.");
  82. return Task.FromResult<IAbpMongoDbContext>(dbContext);
  83. }
  84. private async Task<string> ResolveConnectionStringAsync(Guid? tenantId)
  85. {
  86. if (tenantId == _currentTenant.Id)
  87. {
  88. return await _connectionStringResolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName<TMongoDbContext>());
  89. }
  90. using (_currentTenant.Change(tenantId))
  91. {
  92. return await _connectionStringResolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName<TMongoDbContext>());
  93. }
  94. }
  95. }
  96. /// <summary>
  97. /// MongoDB数据库API包装类
  98. /// </summary>
  99. public class MongoDbDatabaseApi : IDatabaseApi
  100. {
  101. public IAbpMongoDbContext DbContext { get; }
  102. public MongoDbDatabaseApi(IAbpMongoDbContext dbContext)
  103. {
  104. DbContext = dbContext;
  105. }
  106. }
  107. }