generated from SwissLife-OSS/template
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add mongo session provider support (#89)
Add session provider Co-authored-by: Normen Scheiber <nscheibe@live.com>
- Loading branch information
Showing
11 changed files
with
381 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using MongoDB.Bson; | ||
using MongoDB.Driver; | ||
using MongoDB.Extensions.Context; | ||
using Squadron; | ||
using Xunit; | ||
|
||
namespace MongoDB.Extensions.Session.Tests; | ||
|
||
public class MongoSessionProviderTests : IClassFixture<MongoReplicaSetResource> | ||
{ | ||
private readonly IServiceProvider _serviceProvider; | ||
|
||
public MongoSessionProviderTests(MongoReplicaSetResource mongoResource) | ||
{ | ||
var mongoOptions = new MongoOptions | ||
{ | ||
ConnectionString = mongoResource.ConnectionString, | ||
DatabaseName = mongoResource.CreateDatabase().DatabaseNamespace.DatabaseName | ||
}; | ||
|
||
_serviceProvider = new ServiceCollection() | ||
.AddSingleton(mongoOptions) | ||
.AddMongoSessionProvider<TestDbContext, ITestScope>() | ||
.BuildServiceProvider(); | ||
} | ||
|
||
[Fact] | ||
public async Task BeginTransactionAsync_ShouldBeginTransaction() | ||
{ | ||
// Arrange | ||
ISessionProvider<ITestScope> sessionProvider = _serviceProvider | ||
.GetRequiredService<ISessionProvider<ITestScope>>(); | ||
|
||
// Act | ||
ITransactionSession transactionSession = await sessionProvider | ||
.BeginTransactionAsync(CancellationToken.None); | ||
|
||
// Assert | ||
transactionSession.Should().NotBeNull(); | ||
IClientSessionHandle clientSessionHandle = transactionSession.GetSessionHandle(); | ||
clientSessionHandle.ServerSession.Id["id"].AsGuid.Should().NotBeEmpty(); | ||
clientSessionHandle.IsInTransaction.Should().BeTrue(); | ||
} | ||
|
||
[Fact] | ||
public async Task StartSessionAsync_ShouldStartSession() | ||
{ | ||
// Arrange | ||
ISessionProvider<ITestScope> sessionProvider = _serviceProvider | ||
.GetRequiredService<ISessionProvider<ITestScope>>(); | ||
|
||
// Act | ||
ISession session = await sessionProvider | ||
.StartSessionAsync(CancellationToken.None); | ||
|
||
// Assert | ||
session.Should().NotBeNull(); | ||
IClientSessionHandle clientSessionHandle = session.GetSessionHandle(); | ||
clientSessionHandle.ServerSession.Id["id"].AsGuid.Should().NotBeEmpty(); | ||
clientSessionHandle.IsInTransaction.Should().BeFalse(); | ||
} | ||
|
||
[Fact] | ||
public async Task MongoSession_Dispose_ShouldDisposeSession() | ||
{ | ||
// Arrange | ||
ISessionProvider<ITestScope> sessionProvider = _serviceProvider | ||
.GetRequiredService<ISessionProvider<ITestScope>>(); | ||
|
||
ISession session = await sessionProvider | ||
.StartSessionAsync(CancellationToken.None); | ||
|
||
// Act | ||
session.Dispose(); | ||
|
||
// Assert | ||
IClientSessionHandle clientSessionHandle = session.GetSessionHandle(); | ||
Assert.Throws<ObjectDisposedException>(() => clientSessionHandle.ServerSession); | ||
} | ||
|
||
[Fact] | ||
public async Task MongoTransactionSession_Dispose_ShouldDisposeSession() | ||
{ | ||
// Arrange | ||
ISessionProvider<ITestScope> sessionProvider = _serviceProvider | ||
.GetRequiredService<ISessionProvider<ITestScope>>(); | ||
|
||
ITransactionSession transactionSession = await sessionProvider | ||
.BeginTransactionAsync(CancellationToken.None); | ||
|
||
// Act | ||
transactionSession.Dispose(); | ||
|
||
// Assert | ||
IClientSessionHandle clientSessionHandle = transactionSession.GetSessionHandle(); | ||
Assert.Throws<ObjectDisposedException>(() => clientSessionHandle.ServerSession); | ||
} | ||
|
||
[Fact] | ||
public async Task MongoTransactionSession_NotCommitting_ShouldNotAffectDatabase() | ||
{ | ||
// Arrange | ||
ISessionProvider<ITestScope> sessionProvider = _serviceProvider | ||
.GetRequiredService<ISessionProvider<ITestScope>>(); | ||
|
||
ITransactionSession transactionSession = await sessionProvider | ||
.BeginTransactionAsync(CancellationToken.None); | ||
TestDbContext context = _serviceProvider.GetRequiredService<TestDbContext>(); | ||
IMongoCollection<BsonDocument> collection = context.CreateCollection<BsonDocument>(); | ||
await collection.InsertOneAsync(transactionSession.GetSessionHandle(), new BsonDocument()); | ||
|
||
// Act | ||
// Not committing the transaction | ||
|
||
// Assert | ||
(await collection | ||
.Find(FilterDefinition<BsonDocument>.Empty) | ||
.ToListAsync()) | ||
.Count.Should().Be(0); | ||
} | ||
|
||
private class TestDbContext : MongoDbContext | ||
{ | ||
public TestDbContext(MongoOptions mongoOptions) | ||
: base(mongoOptions) | ||
{ | ||
} | ||
|
||
protected override void OnConfiguring(IMongoDatabaseBuilder mongoDatabaseBuilder) | ||
{ | ||
} | ||
} | ||
|
||
private interface ITestScope | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
public interface ISession : IDisposable | ||
{ | ||
Task<T> WithTransactionAsync<T>( | ||
Func<ISession, CancellationToken, Task<T>> action, | ||
CancellationToken cancellationToken); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
public interface ISessionProvider<TScope> | ||
{ | ||
Task<ITransactionSession> BeginTransactionAsync( | ||
CancellationToken cancellationToken); | ||
|
||
Task<ISession> StartSessionAsync( | ||
CancellationToken cancellationToken); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
public interface ITransactionSession : IDisposable | ||
{ | ||
Task CommitAsync(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MongoDB.Driver; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
internal sealed class MongoSession : ISession | ||
{ | ||
private bool _disposed; | ||
|
||
private static TransactionOptions TransactionOptions { get; } = new( | ||
ReadConcern.Majority, | ||
ReadPreference.Primary, | ||
WriteConcern.WMajority.With(journal: true), | ||
TimeSpan.FromSeconds(180)); | ||
|
||
public MongoSession(IClientSessionHandle clientSession) | ||
{ | ||
Session = clientSession; | ||
} | ||
|
||
public IClientSessionHandle Session { get; } | ||
|
||
public Task<T> WithTransactionAsync<T>( | ||
Func<ISession, CancellationToken, Task<T>> action, | ||
CancellationToken cancellationToken) | ||
{ | ||
return Session.WithTransactionAsync<T>( | ||
(_, ct) => action(this, ct), | ||
TransactionOptions, | ||
cancellationToken); | ||
} | ||
|
||
private void Dispose(bool disposing) | ||
{ | ||
if (!_disposed) | ||
{ | ||
if (disposing) | ||
{ | ||
Session.Dispose(); | ||
} | ||
|
||
_disposed = true; | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MongoDB.Driver; | ||
using MongoDB.Extensions.Context; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
public class MongoSessionProvider<TContext, TScope> : ISessionProvider<TScope> | ||
where TContext : IMongoDbContext | ||
{ | ||
private readonly IMongoClient _mongoClient; | ||
|
||
public MongoSessionProvider(TContext context) | ||
{ | ||
_mongoClient = context.Client; | ||
} | ||
|
||
public Task<ITransactionSession> BeginTransactionAsync( | ||
CancellationToken cancellationToken) | ||
{ | ||
return BeginTransactionAsync(true, cancellationToken); | ||
} | ||
|
||
private async Task<ITransactionSession> BeginTransactionAsync( | ||
bool safeModeEnabled, | ||
CancellationToken cancellationToken) | ||
{ | ||
IClientSessionHandle clientSession = await _mongoClient | ||
.StartSessionAsync(cancellationToken: cancellationToken); | ||
|
||
var transactionOptions = new TransactionOptions( | ||
ReadConcern.Majority, | ||
ReadPreference.Primary, | ||
WriteConcern.WMajority.With(journal: safeModeEnabled), | ||
TimeSpan.FromSeconds(180)); | ||
|
||
clientSession.StartTransaction(transactionOptions); | ||
|
||
return new MongoTransactionSession(clientSession, cancellationToken); | ||
} | ||
|
||
public async Task<ISession> StartSessionAsync( | ||
CancellationToken cancellationToken) | ||
{ | ||
IClientSessionHandle clientSession = await _mongoClient | ||
.StartSessionAsync(cancellationToken: cancellationToken); | ||
|
||
return new MongoSession(clientSession); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using MongoDB.Driver; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
internal class MongoTransactionSession : ITransactionSession | ||
{ | ||
private readonly CancellationToken _cancellationToken; | ||
private bool _disposed; | ||
|
||
public MongoTransactionSession( | ||
IClientSessionHandle clientSession, | ||
CancellationToken cancellationToken) | ||
{ | ||
Session = clientSession; | ||
_cancellationToken = cancellationToken; | ||
} | ||
|
||
public IClientSessionHandle Session { get; } | ||
|
||
public async Task CommitAsync() | ||
{ | ||
await Session.CommitTransactionAsync(_cancellationToken); | ||
} | ||
|
||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (!_disposed) | ||
{ | ||
if (disposing) | ||
{ | ||
Session.Dispose(); | ||
} | ||
|
||
_disposed = true; | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using MongoDB.Extensions.Context; | ||
|
||
namespace MongoDB.Extensions.Session; | ||
|
||
public static class ServiceCollectionExtensions | ||
{ | ||
public static IServiceCollection AddMongoSessionProvider<TContext, TScope>( | ||
this IServiceCollection services) | ||
where TContext : class, IMongoDbContext | ||
{ | ||
services.TryAddSingleton<TContext>(); | ||
services.TryAddSingleton<ISessionProvider<TScope>, MongoSessionProvider<TContext, TScope>>(); | ||
|
||
return services; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.