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.Repositories.MongoDB;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB.Clients;
using Volo.Abp.MongoDB.DependencyInjection;
using Volo.Abp.Uow.MongoDB;
using Volo.Abp.MongoDB.DistributedEvents;
@ -31,8 +30,6 @@ public class AbpMongoDbModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<IMongoClientFactory, MongoClientFactory>();
context.Services.TryAddTransient(
typeof(IMongoDbContextProvider<>),
typeof(UnitOfWorkMongoDbContextProvider<>)
@ -64,4 +61,4 @@ public class AbpMongoDbModule : AbpModule
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.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Volo.Abp.Data;
@ -25,17 +24,16 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider;
protected readonly ICurrentTenant CurrentTenant;
protected readonly AbpMongoDbContextOptions Options;
protected readonly IMongoDbContextTypeProvider DbContextTypeProvider;
protected readonly IMongoClientFactory MongoClientFactory;
protected readonly IAbpMongoClientFactory MongoClientFactory;
public UnitOfWorkMongoDbContextProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
ICancellationTokenProvider cancellationTokenProvider,
ICurrentTenant currentTenant,
IOptions<AbpMongoDbContextOptions> options,
IMongoDbContextTypeProvider dbContextTypeProvider, IMongoClientFactory mongoClientFactory)
IMongoDbContextTypeProvider dbContextTypeProvider,
IAbpMongoClientFactory mongoClientFactory)
{
UnitOfWorkManager = unitOfWorkManager;
ConnectionStringResolver = connectionStringResolver;
@ -43,7 +41,6 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
CurrentTenant = currentTenant;
DbContextTypeProvider = dbContextTypeProvider;
MongoClientFactory = mongoClientFactory;
Options = options.Value;
Logger = NullLogger<UnitOfWorkMongoDbContextProvider<TMongoDbContext>>.Instance;
}
@ -129,7 +126,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
[Obsolete("Use CreateDbContextAsync")]
private TMongoDbContext CreateDbContext(IUnitOfWork unitOfWork, MongoUrl mongoUrl, string databaseName)
{
var client = CreateMongoClient(mongoUrl);
var client = MongoClientFactory.Get(mongoUrl);
var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional)
@ -149,7 +146,7 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
string databaseName,
CancellationToken cancellationToken = default)
{
var client = CreateMongoClient(mongoUrl);
var client = await MongoClientFactory.GetAsync(mongoUrl);
var database = client.GetDatabase(databaseName);
if (unitOfWork.Options.IsTransactional)
@ -298,13 +295,8 @@ public class UnitOfWorkMongoDbContextProvider<TMongoDbContext> : IMongoDbContext
return ConnectionStringResolver.Resolve(dbContextType);
}
protected virtual MongoClient CreateMongoClient(MongoUrl mongoUrl)
{
return MongoClientFactory.GetClient(mongoUrl.ToString());
}
protected virtual CancellationToken GetCancellationToken(CancellationToken preferredValue = default)
{
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.Threading.Tasks;
using MongoDB.Driver;
using Volo.Abp.TestApp.Testing;
using Xunit;
namespace Volo.Abp.MongoDB.Clients;
@ -9,74 +8,72 @@ namespace Volo.Abp.MongoDB.Clients;
[Collection(MongoTestCollection.Name)]
public class MongoClient_Factory_Tests : MongoDbTestBase
{
private readonly IMongoClientFactory _factory;
private readonly IAbpMongoClientFactory _factory;
public MongoClient_Factory_Tests()
{
_factory = GetRequiredService<IMongoClientFactory>();
_factory = GetRequiredService<IAbpMongoClientFactory>();
}
[Fact]
public void Should_Return_Same_Instance_For_Same_ConnectionString()
public async Task Should_Return_Same_Instance_For_Same_ConnectionString()
{
// Arrange
var connectionString = "mongodb://localhost:27017/my-db";
var mongoUrl = new MongoUrl("mongodb://localhost:27017/my-db");
// Act
var client1 = _factory.GetClient(connectionString);
var client2 = _factory.GetClient(connectionString);
var client1 = await _factory.GetAsync(mongoUrl);
var client2 = await _factory.GetAsync(mongoUrl);
// Assert
Assert.Same(client1, client2);
}
[Fact]
public void Should_Return_Different_Instances_For_Different_ConnectionStrings()
public async Task Should_Return_Different_Instances_For_Different_ConnectionStrings()
{
// Arrange
var cs1 = "mongodb://localhost:27017/db1";
var cs2 = "mongodb://localhost:27017/db2";
var mongoUrl1 = new MongoUrl("mongodb://localhost:27017/db1");
var mongoUrl2 = new MongoUrl("mongodb://localhost:27017/db2");
// Act
var client1 = _factory.GetClient(cs1);
var client2 = _factory.GetClient(cs2);
var client1 = await _factory.GetAsync(mongoUrl1);
var client2 = await _factory.GetAsync(mongoUrl2);
// Assert
Assert.NotSame(client1, client2);
}
[Fact]
public void Should_Not_Throw_For_Valid_But_Unreachable_Connection()
public async Task Should_Not_Throw_For_Valid_But_Unreachable_Connection()
{
// Arrange
var cs = "mongodb://unreachablehost:12345/any";
var mongoUrl = new MongoUrl("mongodb://unreachablehost:12345/any");
// Act
var client = _factory.GetClient(cs);
var client = await _factory.GetAsync(mongoUrl);
// Assert
Assert.NotNull(client); // Even though it's not connectable now, the instance can be created
}
[Fact]
public void Should_Be_ThreadSafe_When_Accessed_Concurrently()
public async Task Should_Be_ThreadSafe_When_Accessed_Concurrently()
{
var connectionString = "mongodb://localhost:27017/threadsafe";
MongoClient[] results = new MongoClient[100];
var mongoUrl = new MongoUrl("mongodb://localhost:27017/threadsafe");
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));
}
[Theory]
[InlineData(null)]
[InlineData("")]
public void Should_Throw_If_ConnectionString_Is_Null_Or_Empty(string connectionString)
[Fact]
public async Task Should_Throw_If_ConnectionString_Is_Null_Or_Empty()
{
Assert.Throws<ArgumentException>(() => _factory.GetClient(connectionString));
await Assert.ThrowsAsync<ArgumentNullException>(() => _factory.GetAsync(null!));
}
}
}

Loading…
Cancel
Save