mirror of https://github.com/abpframework/abp.git
10 changed files with 374 additions and 34 deletions
@ -0,0 +1,42 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
|
|||
namespace Volo.Abp.Uow.MongoDB |
|||
{ |
|||
public class MongoDbTransactionApi : ITransactionApi, ISupportsRollback |
|||
{ |
|||
public IClientSessionHandle SessionHandle { get; } |
|||
|
|||
public MongoDbTransactionApi(IClientSessionHandle sessionHandle) |
|||
{ |
|||
SessionHandle = sessionHandle; |
|||
} |
|||
|
|||
public async Task CommitAsync() |
|||
{ |
|||
await SessionHandle.CommitTransactionAsync(); |
|||
} |
|||
|
|||
protected void Commit() |
|||
{ |
|||
SessionHandle.CommitTransaction(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
SessionHandle.Dispose(); |
|||
} |
|||
|
|||
public void Rollback() |
|||
{ |
|||
SessionHandle.AbortTransaction(); |
|||
} |
|||
|
|||
public async Task RollbackAsync(CancellationToken cancellationToken) |
|||
{ |
|||
await SessionHandle.AbortTransactionAsync(cancellationToken); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using MongoDB.Driver; |
|||
|
|||
namespace Volo.Abp.MongoDB |
|||
{ |
|||
/// <summary>
|
|||
/// TODO It can be removed, when Mongo2Go solves this issue : https://github.com/Mongo2Go/Mongo2Go/issues/89
|
|||
/// </summary>
|
|||
internal static class MongoClientExtension |
|||
{ |
|||
private static readonly TimeSpan InitialDelay = TimeSpan.FromMilliseconds(500); |
|||
private static readonly TimeSpan MaxDelay = TimeSpan.FromSeconds(5000); |
|||
|
|||
|
|||
public static void EnsureReplicationSetReady(this IMongoClient mongoClient) |
|||
{ |
|||
var delay = InitialDelay; |
|||
var database = mongoClient.GetDatabase("__dummy-db"); |
|||
try |
|||
{ |
|||
while (true) |
|||
{ |
|||
try |
|||
{ |
|||
_ = database.GetCollection<DummyEntry>("__dummy"); |
|||
database.DropCollection("__dummy"); |
|||
|
|||
var session = mongoClient.StartSession(); |
|||
|
|||
try |
|||
{ |
|||
session.StartTransaction(); |
|||
session.AbortTransaction(); |
|||
} |
|||
finally |
|||
{ |
|||
session.Dispose(); |
|||
} |
|||
break; |
|||
} |
|||
catch (NotSupportedException) { } |
|||
|
|||
Thread.Sleep(delay); |
|||
delay = Min(Double(delay), MaxDelay); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
mongoClient.DropDatabase("__dummy-db"); |
|||
} |
|||
} |
|||
|
|||
private static TimeSpan Min(TimeSpan left, TimeSpan right) |
|||
{ |
|||
return new TimeSpan(Math.Min(left.Ticks, right.Ticks)); |
|||
} |
|||
|
|||
private static TimeSpan Double(TimeSpan timeSpan) |
|||
{ |
|||
long ticks; |
|||
try |
|||
{ |
|||
ticks = checked(timeSpan.Ticks * 2); |
|||
} |
|||
catch (OverflowException) |
|||
{ |
|||
if (timeSpan.Ticks >= 0) |
|||
{ |
|||
return TimeSpan.MaxValue; |
|||
} |
|||
|
|||
return TimeSpan.MinValue; |
|||
} |
|||
|
|||
return new TimeSpan(ticks); |
|||
} |
|||
|
|||
private sealed class DummyEntry |
|||
{ |
|||
public int Id { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Shouldly; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.TestApp.Domain; |
|||
using Volo.Abp.TestApp.Testing; |
|||
using Volo.Abp.Uow; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.MongoDB.Transactions |
|||
{ |
|||
public class Transaction_Tests : TestAppTestBase<AbpMongoDbTestModule> |
|||
{ |
|||
|
|||
private readonly IBasicRepository<Person, Guid> _personRepository; |
|||
private readonly IBasicRepository<City, Guid> _cityRepository; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
|
|||
public Transaction_Tests() |
|||
{ |
|||
_personRepository = ServiceProvider.GetRequiredService<IBasicRepository<Person, Guid>>(); |
|||
_cityRepository = ServiceProvider.GetRequiredService<IBasicRepository<City, Guid>>(); |
|||
_unitOfWorkManager = ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_When_An_Exception_Is_Thrown() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
const string exceptionMessage = "thrown to rollback the transaction!"; |
|||
|
|||
try |
|||
{ |
|||
await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions {IsTransactional = true}, async () => |
|||
{ |
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
throw new Exception(exceptionMessage); |
|||
}); |
|||
} |
|||
catch (Exception e) when (e.Message == exceptionMessage) |
|||
{ |
|||
|
|||
} |
|||
|
|||
var person = await _personRepository.FindAsync(personId); |
|||
person.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_Manually() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
|
|||
await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions {IsTransactional = true}, async () => |
|||
{ |
|||
_unitOfWorkManager.Current.ShouldNotBeNull(); |
|||
|
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
|
|||
await _unitOfWorkManager.Current.RollbackAsync(); |
|||
}); |
|||
|
|||
var person = await _personRepository.FindAsync(personId); |
|||
person.ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Rollback_Transaction_Manually_With_Double_DbContext_Transaction() |
|||
{ |
|||
var personId = Guid.NewGuid(); |
|||
var cityId = Guid.NewGuid(); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>(); |
|||
|
|||
using (uowManager.Begin(new AbpUnitOfWorkOptions {IsTransactional = true})) |
|||
{ |
|||
_unitOfWorkManager.Current.ShouldNotBeNull(); |
|||
|
|||
await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); |
|||
await _cityRepository.InsertAsync(new City(cityId, cityId.ToString())); |
|||
|
|||
await _unitOfWorkManager.Current.SaveChangesAsync(); |
|||
|
|||
//Will automatically rollback since not called the Complete!
|
|||
} |
|||
} |
|||
|
|||
(await _personRepository.FindAsync(personId)).ShouldBeNull(); |
|||
(await _cityRepository.FindAsync(cityId)).ShouldBeNull(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue