Browse Source

Refactor `AbpMongoClientFactory`.

pull/23007/head
maliming 1 year ago
parent
commit
02436d8c70
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 5
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs
  2. 44
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/AbpMongoClientFactory.cs
  3. 13
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IAbpMongoClientFactory.cs
  4. 9
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IMongoClientFactory.cs
  5. 32
      framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/MongoClientFactory.cs
  6. 20
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDbContextProvider.cs
  7. 51
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Clients/MongoClient_Factory_Tests.cs

5
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs

@ -8,7 +8,6 @@ using Volo.Abp.Domain;
using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.Clients;
using Volo.Abp.MongoDB.DependencyInjection; using Volo.Abp.MongoDB.DependencyInjection;
using Volo.Abp.Uow.MongoDB; using Volo.Abp.Uow.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents; using Volo.Abp.MongoDB.DistributedEvents;
@ -31,8 +30,6 @@ public class AbpMongoDbModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.AddSingleton<IMongoClientFactory, MongoClientFactory>();
context.Services.TryAddTransient( context.Services.TryAddTransient(
typeof(IMongoDbContextProvider<>), typeof(IMongoDbContextProvider<>),
typeof(UnitOfWorkMongoDbContextProvider<>) typeof(UnitOfWorkMongoDbContextProvider<>)
@ -64,4 +61,4 @@ public class AbpMongoDbModule : AbpModule
options.IgnoredEventSelectors.Add<IncomingEventRecord>(); options.IgnoredEventSelectors.Add<IncomingEventRecord>();
}); });
} }
} }

44
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/AbpMongoClientFactory.cs

@ -0,0 +1,44 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.Clients;
public class AbpMongoClientFactory : IAbpMongoClientFactory, ISingletonDependency
{
protected ConcurrentDictionary<string, MongoClient> ClientCache { get; }
protected AbpMongoDbContextOptions Options { get; }
public AbpMongoClientFactory(IOptions<AbpMongoDbContextOptions> options)
{
Options = options.Value;
ClientCache = new ConcurrentDictionary<string, MongoClient>();
}
public virtual Task<MongoClient> GetAsync(MongoUrl mongoUrl)
{
Check.NotNull(mongoUrl, nameof(mongoUrl));
return Task.FromResult(
ClientCache.GetOrAdd(mongoUrl.ToString(), _ =>
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
}));
}
public virtual MongoClient Get(MongoUrl mongoUrl)
{
Check.NotNull(mongoUrl, nameof(mongoUrl));
return ClientCache.GetOrAdd(mongoUrl.ToString(), _ =>
{
var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl);
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
});
}
}

13
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IAbpMongoClientFactory.cs

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.Clients;
public interface IAbpMongoClientFactory
{
Task<MongoClient> GetAsync(MongoUrl mongoUrl);
[Obsolete("Use GetAsync method")]
MongoClient Get(MongoUrl mongoUrl);
}

9
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/IMongoClientFactory.cs

@ -1,9 +0,0 @@
using MongoDB.Driver;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.Clients;
public interface IMongoClientFactory : ISingletonDependency
{
MongoClient GetClient(string connectionString);
}

32
framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/Clients/MongoClientFactory.cs

@ -1,32 +0,0 @@
using System;
using System.Collections.Concurrent;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace Volo.Abp.MongoDB.Clients;
public class MongoClientFactory : IMongoClientFactory
{
private readonly ConcurrentDictionary<string, MongoClient> _clients = new();
private readonly AbpMongoDbContextOptions Options;
public MongoClientFactory(IOptions<AbpMongoDbContextOptions> options)
{
Options = options.Value;
}
public MongoClient GetClient(string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentException("Connection string must not be null or empty.", nameof(connectionString));
}
return _clients.GetOrAdd(connectionString, cs =>
{
var mongoClientSettings = MongoClientSettings.FromUrl(new MongoUrl(cs));
Options.MongoClientSettingsConfigurer?.Invoke(mongoClientSettings);
return new MongoClient(mongoClientSettings);
});
}
}

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

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Driver; using MongoDB.Driver;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -25,17 +24,16 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
protected readonly IConnectionStringResolver ConnectionStringResolver; protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider; protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant; protected readonly ICurrentTenant CurrentTenant;
protected readonly AbpMongoDbContextOptions Options;
protected readonly IMongoDbContextTypeProvider DbContextTypeProvider; protected readonly IMongoDbContextTypeProvider DbContextTypeProvider;
protected readonly IMongoClientFactory MongoClientFactory; protected readonly IAbpMongoClientFactory MongoClientFactory;
public UnitOfWorkMongoDbContextProvider( public UnitOfWorkMongoDbContextProvider(
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver, IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IOptions<AbpMongoDbContextOptions> options, IMongoDbContextTypeProvider dbContextTypeProvider,
IMongoDbContextTypeProvider dbContextTypeProvider, IMongoClientFactory mongoClientFactory) IAbpMongoClientFactory mongoClientFactory)
{ {
UnitOfWorkManager = unitOfWorkManager; UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver; ConnectionStringResolver = connectionStringResolver;
@ -43,7 +41,6 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
DbContextTypeProvider = dbContextTypeProvider; DbContextTypeProvider = dbContextTypeProvider;
MongoClientFactory = mongoClientFactory; MongoClientFactory = mongoClientFactory;
Options = options.Value;
Logger = NullLogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>>.Instance; Logger = NullLogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>>.Instance;
} }
@ -129,7 +126,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
[Obsolete("Use CreateDbContextAsync")] [Obsolete("Use CreateDbContextAsync")]
private TMongoDbContext CreateDbContext(IUnitOfWork unitOfWork, MongoUrl mongoUrl, string databaseName) private TMongoDbContext CreateDbContext(IUnitOfWork unitOfWork, MongoUrl mongoUrl, string databaseName)
{ {
var client = CreateMongoClient(mongoUrl); var client = MongoClientFactory.Get(mongoUrl);
var database = client.GetDatabase(databaseName); var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional) if (unitOfWork.Options.IsTransactional)
@ -149,7 +146,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
string databaseName, string databaseName,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var client = CreateMongoClient(mongoUrl); var client = await MongoClientFactory.GetAsync(mongoUrl);
var database = client.GetDatabase(databaseName); var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional) if (unitOfWork.Options.IsTransactional)
@ -298,13 +295,8 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return ConnectionStringResolver.Resolve(dbContextType); return ConnectionStringResolver.Resolve(dbContextType);
} }
protected virtual MongoClient CreateMongoClient(MongoUrl mongoUrl)
{
return MongoClientFactory.GetClient(mongoUrl.ToString());
}
protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default) protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default)
{ {
return CancellationTokenProvider.FallbackToProvider(preferredValue); return CancellationTokenProvider.FallbackToProvider(preferredValue);
} }
} }

51
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Clients/MongoClient_Factory_Tests.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver; using MongoDB.Driver;
using Volo.Abp.TestApp.Testing;
using Xunit; using Xunit;
namespace Volo.Abp.MongoDB.Clients; namespace Volo.Abp.MongoDB.Clients;
@ -9,74 +8,72 @@ namespace Volo.Abp.MongoDB.Clients;
[Collection(MongoTestCollection.Name)] [Collection(MongoTestCollection.Name)]
public class MongoClient_Factory_Tests : MongoDbTestBase public class MongoClient_Factory_Tests : MongoDbTestBase
{ {
private readonly IMongoClientFactory _factory; private readonly IAbpMongoClientFactory _factory;
public MongoClient_Factory_Tests() public MongoClient_Factory_Tests()
{ {
_factory = GetRequiredService<IMongoClientFactory>(); _factory = GetRequiredService<IAbpMongoClientFactory>();
} }
[Fact] [Fact]
public void Should_Return_Same_Instance_For_Same_ConnectionString() public async Task Should_Return_Same_Instance_For_Same_ConnectionString()
{ {
// Arrange // Arrange
var connectionString = "mongodb://localhost:27017/my-db"; var mongoUrl = new MongoUrl("mongodb://localhost:27017/my-db");
// Act // Act
var client1 = _factory.GetClient(connectionString); var client1 = await _factory.GetAsync(mongoUrl);
var client2 = _factory.GetClient(connectionString); var client2 = await _factory.GetAsync(mongoUrl);
// Assert // Assert
Assert.Same(client1, client2); Assert.Same(client1, client2);
} }
[Fact] [Fact]
public void Should_Return_Different_Instances_For_Different_ConnectionStrings() public async Task Should_Return_Different_Instances_For_Different_ConnectionStrings()
{ {
// Arrange // Arrange
var cs1 = "mongodb://localhost:27017/db1"; var mongoUrl1 = new MongoUrl("mongodb://localhost:27017/db1");
var cs2 = "mongodb://localhost:27017/db2"; var mongoUrl2 = new MongoUrl("mongodb://localhost:27017/db2");
// Act // Act
var client1 = _factory.GetClient(cs1); var client1 = await _factory.GetAsync(mongoUrl1);
var client2 = _factory.GetClient(cs2); var client2 = await _factory.GetAsync(mongoUrl2);
// Assert // Assert
Assert.NotSame(client1, client2); Assert.NotSame(client1, client2);
} }
[Fact] [Fact]
public void Should_Not_Throw_For_Valid_But_Unreachable_Connection() public async Task Should_Not_Throw_For_Valid_But_Unreachable_Connection()
{ {
// Arrange // Arrange
var cs = "mongodb://unreachablehost:12345/any"; var mongoUrl = new MongoUrl("mongodb://unreachablehost:12345/any");
// Act // Act
var client = _factory.GetClient(cs); var client = await _factory.GetAsync(mongoUrl);
// Assert // Assert
Assert.NotNull(client); // Even though it's not connectable now, the instance can be created Assert.NotNull(client); // Even though it's not connectable now, the instance can be created
} }
[Fact] [Fact]
public void Should_Be_ThreadSafe_When_Accessed_Concurrently() public async Task Should_Be_ThreadSafe_When_Accessed_Concurrently()
{ {
var connectionString = "mongodb://localhost:27017/threadsafe"; var mongoUrl = new MongoUrl("mongodb://localhost:27017/threadsafe");
MongoClient[] results = new MongoClient[100]; var results = new MongoClient[100];
Parallel.For(0, 100, i => await Parallel.ForAsync(0, 100, async (i, _) =>
{ {
results[i] = _factory.GetClient(connectionString); results[i] = await _factory.GetAsync(mongoUrl);
}); });
Assert.All(results, client => Assert.Same(results[0], client)); Assert.All(results, client => Assert.Same(results[0], client));
} }
[Theory] [Fact]
[InlineData(null)] public async Task Should_Throw_If_ConnectionString_Is_Null_Or_Empty()
[InlineData("")]
public void Should_Throw_If_ConnectionString_Is_Null_Or_Empty(string connectionString)
{ {
Assert.Throws<ArgumentException>(() => _factory.GetClient(connectionString)); await Assert.ThrowsAsync<ArgumentNullException>(() => _factory.GetAsync(null!));
} }
} }

Loading…
Cancel
Save