diff --git a/common.props b/common.props index 588e160..ef9649e 100644 --- a/common.props +++ b/common.props @@ -19,6 +19,7 @@ false true true + 7.1 diff --git a/src/Stove/Stove.csproj.DotSettings b/src/Stove/Stove.csproj.DotSettings new file mode 100644 index 0000000..58ad6c8 --- /dev/null +++ b/src/Stove/Stove.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/src/Stove/StoveComponentBase.cs b/src/Stove/StoveComponentBase.cs index 7dd9803..c39d1ec 100644 --- a/src/Stove/StoveComponentBase.cs +++ b/src/Stove/StoveComponentBase.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using System.Transactions; using Stove.Domain.Uow; @@ -65,7 +67,7 @@ public IUnitOfWorkManager UnitOfWorkManager /// public IMessageBus MessageBus { get; set; } - public void UseUow(Action act) + protected void UseUow(Action act) { using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin()) { @@ -75,6 +77,19 @@ public void UseUow(Action act) } } + protected Task UseUow(Func func, CancellationToken cancellationToken = default) + { + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin()) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUow(Action act, IsolationLevel isolation) { using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions { IsolationLevel = isolation })) @@ -85,6 +100,19 @@ protected void UseUow(Action act, IsolationLevel isolation) } } + protected Task UseUow(Func func, IsolationLevel isolation, CancellationToken cancellationToken = default) + { + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions { IsolationLevel = isolation })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUow(Action act, bool isTransactional) { using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions { IsTransactional = isTransactional })) @@ -95,6 +123,19 @@ protected void UseUow(Action act, bool isTransactional) } } + protected Task UseUow(Func func, bool isTransactional, CancellationToken cancellationToken = default) + { + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions { IsTransactional = isTransactional })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUow(Action act, TransactionScopeOption scope) { using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions @@ -108,6 +149,22 @@ protected void UseUow(Action act, TransactionScopeOption scope) } } + protected Task UseUow(Func func, TransactionScopeOption scope, CancellationToken cancellationToken = default) + { + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions + { + Scope = scope + })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUow(Action act, IsolationLevel isolation, TransactionScopeOption scope) { using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions @@ -122,6 +179,23 @@ protected void UseUow(Action act, IsolationLevel isolation, TransactionScopeOpti } } + protected Task UseUow(Func func, IsolationLevel isolation, TransactionScopeOption scope, CancellationToken cancellationToken = default) + { + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions + { + IsolationLevel = isolation, + Scope = scope + })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUow(Action act, Action optsAction) { var options = new UnitOfWorkOptions(); @@ -136,6 +210,23 @@ protected void UseUow(Action act, Action optsAction) } } + protected Task UseUow(Func func, Action optsAction, CancellationToken cancellationToken = default) + { + var options = new UnitOfWorkOptions(); + + optsAction(options); + + Task task; + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(options)) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + + return task; + } + protected void UseUowIfNot(Action act) { if (UnitOfWorkManager.Current == null) @@ -153,6 +244,26 @@ protected void UseUowIfNot(Action act) } } + protected Task UseUowIfNot(Func func, CancellationToken cancellationToken = default) + { + Task task; + if (UnitOfWorkManager.Current == null) + { + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin()) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + } + else + { + task = func(); + } + + return task; + } + protected void UseUowIfNot(Action act, bool isTransactional) { if (UnitOfWorkManager.Current == null) @@ -173,6 +284,29 @@ protected void UseUowIfNot(Action act, bool isTransactional) } } + protected Task UseUowIfNot(Func func, bool isTransactional, CancellationToken cancellationToken = default) + { + Task task; + if (UnitOfWorkManager.Current == null) + { + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions + { + IsTransactional = isTransactional + })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + } + else + { + task = func(); + } + + return task; + } + protected void UseUowIfNot(Action act, IsolationLevel isolation) { if (UnitOfWorkManager.Current == null) @@ -193,6 +327,29 @@ protected void UseUowIfNot(Action act, IsolationLevel isolation) } } + protected Task UseUowIfNot(Func func, IsolationLevel isolation, CancellationToken cancellationToken = default) + { + Task task; + if (UnitOfWorkManager.Current == null) + { + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions + { + IsolationLevel = isolation + })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + } + else + { + task = func(); + } + + return task; + } + protected void UseUowIfNot(Action act, IsolationLevel isolation, TransactionScopeOption scope) { if (UnitOfWorkManager.Current == null) @@ -214,6 +371,30 @@ protected void UseUowIfNot(Action act, IsolationLevel isolation, TransactionScop } } + protected Task UseUowIfNot(Func func, IsolationLevel isolation, TransactionScopeOption scope, CancellationToken cancellationToken = default) + { + Task task; + if (UnitOfWorkManager.Current == null) + { + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions + { + IsolationLevel = isolation, + Scope = scope + })) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + } + else + { + task = func(); + } + + return task; + } + protected void UseUowIfNot(Action act, Action optsAction) { var options = new UnitOfWorkOptions(); @@ -234,6 +415,29 @@ protected void UseUowIfNot(Action act, Action optsAction) } } + protected Task UseUowIfNot(Func func, Action optsAction, CancellationToken cancellationToken = default) + { + var options = new UnitOfWorkOptions(); + optsAction(options); + + Task task; + if (UnitOfWorkManager.Current == null) + { + using (IUnitOfWorkCompleteHandle uow = UnitOfWorkManager.Begin(options)) + { + task = func(); + + uow.CompleteAsync(cancellationToken); + } + } + else + { + task = func(); + } + + return task; + } + protected void OnUowCompleted(Action action) { CurrentUnitOfWork.Completed += (sender, args) => diff --git a/test/Stove.Tests.SampleApplication/Domain/SomeDomainService.cs b/test/Stove.Tests.SampleApplication/Domain/SomeDomainService.cs new file mode 100644 index 0000000..2facd57 --- /dev/null +++ b/test/Stove.Tests.SampleApplication/Domain/SomeDomainService.cs @@ -0,0 +1,103 @@ +using System.Threading; +using System.Threading.Tasks; +using System.Transactions; + +using Stove.Domain.Repositories; +using Stove.Domain.Services; +using Stove.Tests.SampleApplication.Domain.Entities; + +namespace Stove.Tests.SampleApplication.Domain +{ + public class SomeDomainService : DomainService + { + private readonly IRepository _messageRepository; + private readonly IRepository _repository; + + public SomeDomainService(IRepository repository, IRepository messageRepository) + { + _repository = repository; + _messageRepository = messageRepository; + } + + public User GetUserByName(string name) + { + User user = null; + UseUow(() => + { + user = _repository.FirstOrDefault(x => x.Name == name); + }); + + return user; + } + + public async Task GetUserByName_async(string name) + { + User user = null; + await UseUow(async () => + { + user = await _repository.FirstOrDefaultAsync(x => x.Name == name); + }, CancellationToken.None); + + return user; + } + + public async Task GetUserByName_async_With_IsolationLevel(string name) + { + User user = null; + + await UseUow(async () => + { + user = await _repository.FirstOrDefaultAsync(x => x.Name == name); + }, IsolationLevel.ReadCommitted); + + return user; + } + + public User GetUserByName_with_isolationlevel(string name) + { + User user = null; + + UseUow(() => + { + user = _repository.FirstOrDefault(x => x.Name == name); + }, IsolationLevel.Chaos); + + return user; + } + + public User GetUserByName_isTransactional(string name) + { + User user = null; + + UseUow(() => + { + user = _repository.FirstOrDefault(x => x.Name == name); + }, true); + + return user; + } + + public async Task GetUserByName_async_isTransactional(string name) + { + User user = null; + + await UseUow(async () => + { + user = await _repository.FirstOrDefaultAsync(x => x.Name == name); + }, true); + + return user; + } + + public async Task CreateMessageAndGet(string message) + { + Message msg = null; + await UseUowIfNot(async () => + { + msg = await _messageRepository.InsertAsync(new Message(message)); + }); + + return msg; + } + } +} diff --git a/test/Stove.Tests.SampleApplication/StoveComponentBase_Tests.cs b/test/Stove.Tests.SampleApplication/StoveComponentBase_Tests.cs new file mode 100644 index 0000000..f4e193b --- /dev/null +++ b/test/Stove.Tests.SampleApplication/StoveComponentBase_Tests.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; + +using Shouldly; + +using Stove.Tests.SampleApplication; +using Stove.Tests.SampleApplication.Domain; +using Stove.Tests.SampleApplication.Domain.Entities; + +using Xunit; + +namespace Stove.Tests +{ + public class StoveComponentBase_Tests : SampleApplicationTestBase + { + public StoveComponentBase_Tests() + { + Building(builder => { }).Ok(); + } + + [Fact] + public void UseUow_should_work() + { + User user = The().GetUserByName("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public async Task UseUow_async_should_work() + { + User user = await The().GetUserByName_async("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public async Task UseUow_with_IsolationLevel_should_work() + { + User user = await The().GetUserByName_async_With_IsolationLevel("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public void UseUow_isTransactional_should_work() + { + User user = The().GetUserByName_isTransactional("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public void UseUow_isolationLevel_should_work() + { + User user = The().GetUserByName_with_isolationlevel("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public async Task UseUow_async_isTransactional_should_work() + { + User user = await The().GetUserByName_async_isTransactional("Oğuzhan"); + user.ShouldNotBeNull(); + } + + [Fact] + public async Task UseUow_and_auditing() + { + using (StoveSession.Use(266)) + { + Message message = await The().CreateMessageAndGet("message"); + message.Subject.ShouldBe("message"); + message.CreatorUserId.ShouldBe(266); + } + } + } +}