diff --git a/Masa.Contrib.sln b/Masa.Contrib.sln index e6962af9e..aebc59ff1 100644 --- a/Masa.Contrib.sln +++ b/Masa.Contrib.sln @@ -80,8 +80,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Masa.Contrib.Ddd.Domain", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Masa.Contrib.Ddd.Domain.Repository.EF", "Masa.Contrib.Ddd.Domain.Repository.EF", "{880E8263-AECC-4793-BD28-7CD03650D124}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Data.UoW.EF", "src\Data\Masa.Contrib.Data.UoW.EF\Masa.Contrib.Data.UoW.EF.csproj", "{1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Data.UoW.EF.Tests", "test\Masa.Contrib.Data.UoW.EF.Tests\Masa.Contrib.Data.UoW.EF.Tests.csproj", "{1B16DD58-0847-45A7-AF93-53EBFBEDAAE7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.Configuration", "src\Configuration\Masa.Contrib.Configuration\Masa.Contrib.Configuration.csproj", "{C056C688-8FFC-42BC-B4EA-EF3808A8A12C}" @@ -318,6 +316,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.BuildingBlocks.BasicAb EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Masa.Contrib.BasicAbility.Mc.Tests", "test\Masa.Contrib.BasicAbility.Mc.Tests\Masa.Contrib.BasicAbility.Mc.Tests.csproj", "{23633E49-F11A-4D14-899A-E2599C8182CE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Masa.Contrib.Data.UoW.EF", "src\Data\Masa.Contrib.Data.UoW.EF\Masa.Contrib.Data.UoW.EF.csproj", "{FC4E526A-DBFC-406A-8ED3-64983B67F688}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -470,14 +470,6 @@ Global {761C3313-A669-465F-A384-9E118FCE4F89}.Release|Any CPU.Build.0 = Release|Any CPU {761C3313-A669-465F-A384-9E118FCE4F89}.Release|x64.ActiveCfg = Release|Any CPU {761C3313-A669-465F-A384-9E118FCE4F89}.Release|x64.Build.0 = Release|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Debug|x64.ActiveCfg = Debug|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Debug|x64.Build.0 = Debug|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Release|Any CPU.Build.0 = Release|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Release|x64.ActiveCfg = Release|Any CPU - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F}.Release|x64.Build.0 = Release|Any CPU {1B16DD58-0847-45A7-AF93-53EBFBEDAAE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B16DD58-0847-45A7-AF93-53EBFBEDAAE7}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B16DD58-0847-45A7-AF93-53EBFBEDAAE7}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1302,6 +1294,14 @@ Global {4E237346-F948-46AC-801B-492545978280}.Release|Any CPU.Build.0 = Release|Any CPU {4E237346-F948-46AC-801B-492545978280}.Release|x64.ActiveCfg = Release|Any CPU {4E237346-F948-46AC-801B-492545978280}.Release|x64.Build.0 = Release|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Debug|x64.Build.0 = Debug|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Release|Any CPU.Build.0 = Release|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Release|x64.ActiveCfg = Release|Any CPU + {FC4E526A-DBFC-406A-8ED3-64983B67F688}.Release|x64.Build.0 = Release|Any CPU {75A25CF6-9BA4-46F5-8BC3-90396230CB64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75A25CF6-9BA4-46F5-8BC3-90396230CB64}.Debug|Any CPU.Build.0 = Debug|Any CPU {75A25CF6-9BA4-46F5-8BC3-90396230CB64}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1365,7 +1365,6 @@ Global {761C3313-A669-465F-A384-9E118FCE4F89} = {38E6C400-90C0-493E-9266-C1602E229F1B} {13EDB361-AF88-4F89-B4AB-46622BCCBC37} = {38E6C400-90C0-493E-9266-C1602E229F1B} {880E8263-AECC-4793-BD28-7CD03650D124} = {38E6C400-90C0-493E-9266-C1602E229F1B} - {1265AE3C-B5FD-4339-8A7D-BC598E6E1C9F} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} {1B16DD58-0847-45A7-AF93-53EBFBEDAAE7} = {38E6C400-90C0-493E-9266-C1602E229F1B} {C056C688-8FFC-42BC-B4EA-EF3808A8A12C} = {59DA3D5F-9E39-4173-8C31-126967CC189F} {428CDAF3-957A-4017-82EA-70737F205546} = {38E6C400-90C0-493E-9266-C1602E229F1B} @@ -1480,6 +1479,7 @@ Global {8D84666E-C79E-4D49-B73D-360E62D312EF} = {5DFAF4A2-ECB5-46E4-904D-1EA5F48B2D48} {7C4B88FA-3681-4C29-BC3F-0FDB111C5681} = {FBD326D3-E59C-433E-A88E-14E179E3093D} {592297DE-DA72-452D-9D88-61EE882FE9A6} = {38E6C400-90C0-493E-9266-C1602E229F1B} + {FC4E526A-DBFC-406A-8ED3-64983B67F688} = {E33ADF54-4D35-49B7-BDA6-412587CA39FF} {4E237346-F948-46AC-801B-492545978280} = {38E6C400-90C0-493E-9266-C1602E229F1B} {75A25CF6-9BA4-46F5-8BC3-90396230CB64} = {5DFAF4A2-ECB5-46E4-904D-1EA5F48B2D48} {DA816A33-F164-4456-92DD-A672BAD1A6B1} = {0D34A7F0-DC77-4789-A136-93089CBD15C3} diff --git a/src/BuildingBlocks/MASA.BuildingBlocks b/src/BuildingBlocks/MASA.BuildingBlocks index da00ccea9..324b0cd64 160000 --- a/src/BuildingBlocks/MASA.BuildingBlocks +++ b/src/BuildingBlocks/MASA.BuildingBlocks @@ -1 +1 @@ -Subproject commit da00ccea99a5f3efb0b4d8750ccbb59ab44fa2ef +Subproject commit 324b0cd643b6f9155eea593aa3ba05661c70e2c4 diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/Transaction.cs b/src/Data/Masa.Contrib.Data.UoW.EF/Transaction.cs index cab7c2a13..664d8c2e7 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/Transaction.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/Transaction.cs @@ -5,8 +5,8 @@ namespace Masa.Contrib.Data.UoW.EF; public class Transaction : ITransaction { - public Transaction(IUnitOfWork unitOfWork) => UnitOfWork = unitOfWork; - [JsonIgnore] public IUnitOfWork? UnitOfWork { get; set; } + + public Transaction(IUnitOfWork unitOfWork) => UnitOfWork = unitOfWork; } diff --git a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs index dff3321f4..5f2ba4430 100644 --- a/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs +++ b/src/Data/Masa.Contrib.Data.UoW.EF/UnitOfWork.cs @@ -39,21 +39,28 @@ public DbTransaction Transaction public async Task SaveChangesAsync(CancellationToken cancellationToken = default) { + if (EntityState == EntityState.UnChanged) + return; + await Context.SaveChangesAsync(cancellationToken); EntityState = EntityState.UnChanged; } public async Task CommitAsync(CancellationToken cancellationToken = default) { - if (!UseTransaction || !TransactionHasBegun) - throw new NotSupportedException("Transaction not opened"); + var domainEventBus = GetDomainEventBus(); + while (domainEventBus != null && await domainEventBus.AnyQueueAsync()) + { + await domainEventBus.PublishQueueAsync(); - await Context.Database.CommitTransactionAsync(cancellationToken); - CommitState = CommitState.Commited; + await SaveChangesAsync(cancellationToken); + } - var domainEventBus = ServiceProvider.GetService(); - if (domainEventBus != null) - await domainEventBus.PublishQueueAsync(); + if (UseTransaction && TransactionHasBegun && CommitState == CommitState.UnCommited) + { + await Context.Database.CommitTransactionAsync(cancellationToken); + CommitState = CommitState.Commited; + } } public async Task RollbackAsync(CancellationToken cancellationToken = default) @@ -64,7 +71,20 @@ public async Task RollbackAsync(CancellationToken cancellationToken = default) await Context.Database.RollbackTransactionAsync(cancellationToken); } + public Task AddDomainEventAsync(TDomainEvent @event) where TDomainEvent : class + { + var domainEventBus = GetDomainEventBus(); + if (domainEventBus == null || @event is not IDomainEvent domainEvent) + return Task.CompletedTask; + + domainEventBus.Enqueue(domainEvent); + return Task.CompletedTask; + } + public async ValueTask DisposeAsync() => await (_context?.DisposeAsync() ?? ValueTask.CompletedTask); public void Dispose() => _context?.Dispose(); + + private IDomainEventBus? GetDomainEventBus() + => ServiceProvider.GetService(); } diff --git a/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs b/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs index 9fcaf5ff6..08d615825 100644 --- a/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs +++ b/src/Ddd/Masa.Contrib.Ddd.Domain.Repository.EF/Repository.cs @@ -16,29 +16,6 @@ public Repository(TDbContext context, IUnitOfWork unitOfWork) : base(unitOfWork. UnitOfWork = unitOfWork; } - public override bool TransactionHasBegun - => Context.Database.CurrentTransaction != null; - - public override DbTransaction Transaction - { - get - { - if (!UseTransaction) - throw new NotSupportedException(nameof(Transaction)); - - if (TransactionHasBegun) - return Context.Database.CurrentTransaction!.GetDbTransaction(); - - return Context.Database.BeginTransaction().GetDbTransaction(); - } - } - - public override bool UseTransaction - { - get => UnitOfWork.UseTransaction; - set => UnitOfWork.UseTransaction = value; - } - public override IUnitOfWork UnitOfWork { get; } public override EntityState EntityState @@ -69,13 +46,6 @@ public override async Task AddRangeAsync( EntityState = EntityState.Changed; } - public override Task CommitAsync(CancellationToken cancellationToken = default) - => UnitOfWork.CommitAsync(cancellationToken); - - public override async ValueTask DisposeAsync() => await Context.DisposeAsync(); - - public override void Dispose() => Context.Dispose(); - public override Task FindAsync( IEnumerable> keyValues, CancellationToken cancellationToken = default) @@ -205,14 +175,6 @@ public override Task RemoveRangeAsync(IEnumerable entities, Cancellatio return Task.CompletedTask; } - public override Task RollbackAsync(CancellationToken cancellationToken = default) - => UnitOfWork.RollbackAsync(cancellationToken); - - public override async Task SaveChangesAsync(CancellationToken cancellationToken = default) - { - await UnitOfWork.SaveChangesAsync(cancellationToken); - } - public override Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) { Context.Set().Update(entity); @@ -245,7 +207,7 @@ private void CheckAndOpenTransaction() public class Repository : Repository, - IRepository, IUnitOfWork + IRepository where TEntity : class, IEntity where TDbContext : DbContext where TKey : IComparable diff --git a/src/Ddd/Masa.Contrib.Ddd.Domain/DomainEventBus.cs b/src/Ddd/Masa.Contrib.Ddd.Domain/DomainEventBus.cs index e881e5f78..fa7ac6dc7 100644 --- a/src/Ddd/Masa.Contrib.Ddd.Domain/DomainEventBus.cs +++ b/src/Ddd/Masa.Contrib.Ddd.Domain/DomainEventBus.cs @@ -67,6 +67,11 @@ public async Task PublishQueueAsync() } } + public Task AnyQueueAsync() + { + return Task.FromResult(_eventQueue.Count > 0); + } + public async Task CommitAsync(CancellationToken cancellationToken = default) => await _unitOfWork.CommitAsync(cancellationToken); diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.Events/EventBus.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.Events/EventBus.cs index fede9419c..118cdc9d6 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.Events/EventBus.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.Events/EventBus.cs @@ -35,7 +35,7 @@ public async Task PublishAsync(TEvent @event) where TEvent : IEvent if (_options.UnitOfWorkRelation[eventType]) { - ITransaction transactionEvent = (ITransaction) @event; + ITransaction transactionEvent = (ITransaction)@event; var unitOfWork = _serviceProvider.GetService(); if (unitOfWork != null) { @@ -46,13 +46,13 @@ public async Task PublishAsync(TEvent @event) where TEvent : IEvent } else { - middlewares = middlewares.Where(middleware => middleware is not TransactionMiddleware); + middlewares = middlewares.Where(middleware => middleware.SupportRecursive); } } } - EventHandlerDelegate publishEvent = async () => { await _dispatcher.PublishEventAsync(_serviceProvider, @event); }; - await middlewares.Reverse().Aggregate(publishEvent, (next, middleware) => () => middleware.HandleAsync(@event, next))(); + EventHandlerDelegate eventHandlerDelegate = async () => { await _dispatcher.PublishEventAsync(_serviceProvider, @event); }; + await middlewares.Reverse().Aggregate(eventHandlerDelegate, (next, middleware) => () => middleware.HandleAsync(@event, next))(); } public IEnumerable GetAllEventTypes() => _options.AllEventTypes; diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Middleware/TransactionMiddleware.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Middleware/TransactionMiddleware.cs index 78d2cd23c..da25e2a22 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Middleware/TransactionMiddleware.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.Events/Internal/Middleware/TransactionMiddleware.cs @@ -3,50 +3,38 @@ namespace Masa.Contrib.Dispatcher.Events.Internal.Middleware; -public class TransactionMiddleware : IMiddleware - where TEvent : notnull, IEvent +internal class TransactionMiddleware : Middleware + where TEvent : IEvent { private readonly IUnitOfWork? _unitOfWork; + public override bool SupportRecursive => false; + public TransactionMiddleware(IUnitOfWork? unitOfWork = null) { _unitOfWork = unitOfWork; } - public async Task HandleAsync(TEvent @event, EventHandlerDelegate next) + public override async Task HandleAsync(TEvent @event, EventHandlerDelegate next) { try { await next(); - if (_unitOfWork is { EntityState: EntityState.Changed }) + if (_unitOfWork != null) { await _unitOfWork.SaveChangesAsync(); - } - if (IsUseTransaction(@event, out ITransaction? transaction)) - { - await transaction!.UnitOfWork!.CommitAsync(); + await _unitOfWork.CommitAsync(); } } catch (Exception) { - if (IsUseTransaction(@event, out ITransaction? transaction) && !transaction!.UnitOfWork!.DisableRollbackOnFailure) + if (_unitOfWork is { DisableRollbackOnFailure: false }) { - await transaction.UnitOfWork!.RollbackAsync(); + await _unitOfWork!.RollbackAsync(); } - throw; - } - } - private bool IsUseTransaction(TEvent @event, out ITransaction? transaction) - { - if (@event is ITransaction { UnitOfWork: { UseTransaction: true, TransactionHasBegun: true, CommitState: CommitState.UnCommited } } transactionEvent) - { - transaction = transactionEvent; - return true; + throw; } - - transaction = null; - return false; } } diff --git a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs index 912c3e69b..b46e749ce 100644 --- a/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs +++ b/src/Dispatcher/Masa.Contrib.Dispatcher.IntegrationEvents.Dapr/_Imports.cs @@ -10,5 +10,4 @@ global using Masa.Contrib.Dispatcher.IntegrationEvents.Dapr.Options; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Microsoft.Extensions.Options; global using System.Reflection; diff --git a/src/Isolation/Masa.Contrib.Isolation/Middleware/IsolationMiddleware.cs b/src/Isolation/Masa.Contrib.Isolation/Middleware/IsolationMiddleware.cs index 16bf233d0..20ca0c518 100644 --- a/src/Isolation/Masa.Contrib.Isolation/Middleware/IsolationMiddleware.cs +++ b/src/Isolation/Masa.Contrib.Isolation/Middleware/IsolationMiddleware.cs @@ -3,7 +3,7 @@ namespace Masa.Contrib.Isolation.Middleware; -public class IsolationMiddleware : IMiddleware where TEvent : IEvent +public class IsolationMiddleware : Middleware where TEvent : IEvent { private readonly IEnumerable _middlewares; @@ -12,7 +12,7 @@ public IsolationMiddleware(IEnumerable middlewares) _middlewares = middlewares; } - public async Task HandleAsync(TEvent @event, EventHandlerDelegate next) + public override async Task HandleAsync(TEvent @event, EventHandlerDelegate next) { foreach (var middleware in _middlewares) { diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs b/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs index a8e17126c..b4487c804 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/TestUnitOfWork.cs @@ -3,6 +3,7 @@ using Masa.BuildingBlocks.Ddd.Domain.Events; using Microsoft.Extensions.Options; +using EntityState = Masa.BuildingBlocks.Data.UoW.EntityState; namespace Masa.Contrib.Data.UoW.EF.Tests; @@ -66,7 +67,9 @@ public async Task TestUseTranscationAsync() Name = Guid.NewGuid().ToString() }; dbContext.Add(user); + uoW.EntityState = EntityState.Changed; await uoW.SaveChangesAsync(); + uoW.CommitState = CommitState.UnCommited; await uoW.RollbackAsync(); Assert.IsTrue(!dbContext.User.ToList().Any()); @@ -86,21 +89,11 @@ public async Task TestNotUseTranscationAsync() Name = Guid.NewGuid().ToString() }; dbContext.Add(user); + uoW.EntityState = EntityState.Changed; await uoW.SaveChangesAsync(); await Assert.ThrowsExceptionAsync(async () => await uoW.RollbackAsync()); } - [TestMethod] - public async Task TestNotTransactionCommitAsync() - { - _options.Object.UseUoW(options => options.UseTestSqlite(_connectionString)); - var serviceProvider = _options.Object.Services.BuildServiceProvider(); - var dbContext = serviceProvider.GetRequiredService(); - await dbContext.Database.EnsureCreatedAsync(); - var uoW = new UnitOfWork(serviceProvider); - await Assert.ThrowsExceptionAsync(async () => await uoW.CommitAsync()); - } - [TestMethod] public async Task TestCommitAsync() { @@ -115,7 +108,9 @@ public async Task TestCommitAsync() }; var transcation = uoW.Transaction; dbContext.User.Add(user); + uoW.EntityState = EntityState.Changed; await uoW.SaveChangesAsync(); + uoW.CommitState = CommitState.UnCommited;//todo: Using Repository does not require manual changes to Commit status await uoW.CommitAsync(); Assert.IsTrue(dbContext.User.ToList().Count == 1); @@ -300,25 +295,5 @@ public async Task TestGetConnectionStringAsyncReturnTest1() Assert.IsTrue(await connectionStringProvider.GetConnectionStringAsync() == connectionString); } - [TestMethod] - public async Task TestCommitReturnPublishQueueIsValid() - { - IServiceCollection services = new ServiceCollection(); - Mock domainEventBus = new(); - domainEventBus.Setup(eventBus => eventBus.PublishQueueAsync()).Verifiable(); - services.AddScoped(serviceProvider => domainEventBus.Object); - Mock eventBuilder = new(); - eventBuilder.Setup(eb => eb.Services).Returns(services).Verifiable(); - eventBuilder.Object.UseUoW(options => options.UseTestSqlite($"Data Source=test_{Guid.NewGuid()}")); - - var serviceProvider = services.BuildServiceProvider(); - var dbContext = serviceProvider.GetRequiredService(); - dbContext.Database.EnsureCreated(); - var unitOfWork = new UnitOfWork(serviceProvider); - var _ = unitOfWork.Transaction; - await unitOfWork.CommitAsync(); - domainEventBus.Verify(eventBus => eventBus.PublishQueueAsync(), Times.Once()); - } - private string GetDataBaseConnectionString(CustomDbContext dbContext) => dbContext.Database.GetConnectionString()!; } diff --git a/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs b/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs index f535daad0..9f32db981 100644 --- a/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs +++ b/test/Masa.Contrib.Data.UoW.EF.Tests/_Imports.cs @@ -6,7 +6,6 @@ global using Masa.BuildingBlocks.Data.Options; global using Masa.BuildingBlocks.Data.UoW; global using Masa.BuildingBlocks.Dispatcher.Events; -global using Masa.Contrib.Configuration; global using Masa.Contrib.Data.EntityFrameworkCore; global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Microsoft.AspNetCore.Builder; diff --git a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/RegisterUserDomainEvent.cs b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/RegisterUserDomainEvent.cs new file mode 100644 index 000000000..c48ae9589 --- /dev/null +++ b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/RegisterUserDomainEvent.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Ddd.Domain.Entities.Tests.DomainEvents; + +public record RegisterUserDomainEvent : DomainEvent +{ + public Guid Id { get; set; } + + public string Name { get; set; } +} diff --git a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/UpdateUserDomainEvent.cs b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/UpdateUserDomainEvent.cs new file mode 100644 index 000000000..fca8a0570 --- /dev/null +++ b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/DomainEvents/UpdateUserDomainEvent.cs @@ -0,0 +1,14 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Ddd.Domain.Entities.Tests.DomainEvents; + +/// +/// todo: It doesn't make sense to send events just for the test loop +/// +public record UpdateUserDomainEvent : DomainEvent +{ + public Guid Id { get; set; } + + public string Name { get; set; } +} diff --git a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/Events/RegisterUserEvent.cs b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/Events/RegisterUserEvent.cs new file mode 100644 index 000000000..c3f99a14e --- /dev/null +++ b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/Events/RegisterUserEvent.cs @@ -0,0 +1,11 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Ddd.Domain.Entities.Tests.Events; + +public record RegisterUserEvent : DomainEvent +{ + public Guid Id { get; set; } + + public string Name { get; set; } +} diff --git a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/User.cs b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/User.cs index ddd9654b2..fcc0b2813 100644 --- a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/User.cs +++ b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/User.cs @@ -1,10 +1,37 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using Masa.Contrib.Ddd.Domain.Entities.Tests.Events; + namespace Masa.Contrib.Ddd.Domain.Entities.Tests; public class User : AggregateRoot { public string Name { get; set; } -} + public User() + { + Id = Guid.NewGuid(); + } + + public User(Guid id, string name) + { + Id = id; + Name = name; + this.AddDomainEvent(new RegisterUserDomainEvent() + { + Id = Id, + Name = name + }); + } + + public void UpdateName(string name) + { + this.Name = name; + this.AddDomainEvent(new UpdateUserDomainEvent() + { + Id = Id, + Name = name + }); + } +} diff --git a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/_Imports.cs b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/_Imports.cs index 05ba1cf95..12ba3224b 100644 --- a/test/Masa.Contrib.Ddd.Domain.Entities.Tests/_Imports.cs +++ b/test/Masa.Contrib.Ddd.Domain.Entities.Tests/_Imports.cs @@ -2,3 +2,5 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Masa.BuildingBlocks.Ddd.Domain.Entities; +global using Masa.BuildingBlocks.Ddd.Domain.Events; +global using Masa.Contrib.Ddd.Domain.Entities.Tests.DomainEvents; diff --git a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/CustomizeDbContext.cs b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/CustomizeDbContext.cs index 67ff71ba7..677452c3d 100644 --- a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/CustomizeDbContext.cs +++ b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/CustomizeDbContext.cs @@ -1,8 +1,6 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -using Masa.Contrib.Ddd.Domain.Entities.Tests; - namespace Masa.Contrib.Ddd.Domain.Integrated.Tests; public class CustomizeDbContext : MasaDbContext diff --git a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEventTest.cs b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEventTest.cs index 705b890f5..0689a3ba2 100644 --- a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEventTest.cs +++ b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEventTest.cs @@ -1,15 +1,6 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -using Masa.BuildingBlocks.Data; -using Masa.BuildingBlocks.Ddd.Domain.Events; -using Masa.Contrib.Ddd.Domain.Integrated.Tests.DomainEvents; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading.Tasks; -using Masa.Contrib.Ddd.Domain.Entities.Tests; -using Masa.Contrib.Dispatcher.IntegrationEvents; - namespace Masa.Contrib.Ddd.Domain.Integrated.Tests; [TestClass] @@ -31,37 +22,47 @@ public void Initialize() services.AddDomainEventBus(dispatchOptions => { dispatchOptions - .UseIntegrationEventBus(options => - { - options.UseDapr(); - options.UseEventLog(); - }) - .UseEventBus() - .UseUoW(dbOptions => dbOptions.UseSqlite()) - .UseRepository(); + .UseIntegrationEventBus(options => + { + options.UseDapr(); + options.UseEventLog(); + }) + .UseEventBus() + .UseUoW(dbOptions => dbOptions.UseSqlite()) + .UseRepository(); }); _serviceProvider = services.BuildServiceProvider(); } [TestMethod] - public async Task TestSaveChangesReturnDomainEventBus() + public async Task TestInsertAsyncReturnUserNameEqualTom() { - var dbContext = _serviceProvider.GetRequiredService(); + var services = new ServiceCollection(); + services.AddDomainEventBus(dispatcherOptions => + { + dispatcherOptions + .UseIntegrationEventBus(options => + { + options.UseDapr(); + options.UseEventLog(); + }) + .UseEventBus() + .UseUoW(dbOptions => dbOptions.UseTestSqlite($"data source=test-{Guid.NewGuid()}")) + .UseRepository(); + }); + var serviceProvider = services.BuildServiceProvider(); + var dbContext = serviceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); - var user = new User() + var eventBus = serviceProvider.GetRequiredService(); + var @event = new RegisterUserEvent() { + Id = Guid.NewGuid(), Name = "Jim" }; - user.AddDomainEvent(new AddUserIntegrationDomainEvent() - { - Name = user.Name - }); - await dbContext.Set().AddAsync(user); - await dbContext.SaveChangesAsync(); - var domainEventBus = _serviceProvider.GetRequiredService(); - var fields = domainEventBus.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).ToList(); - var field = fields.Where(field => field.Name == "_eventQueue").First(); - var eventQueue = (ConcurrentQueue)field.GetValue(domainEventBus)!; - Assert.IsTrue(eventQueue.Count == 1); + await eventBus.PublishAsync(@event); + + var user = await dbContext.Set().Where(u => u.Id == @event.Id).AsNoTracking().FirstOrDefaultAsync(); + Assert.IsNotNull(user); + Assert.IsTrue(user.Name == "Tom2"); } } diff --git a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEvents/AddUserIntegrationDomainEvent.cs b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEvents/AddUserIntegrationDomainEvent.cs deleted file mode 100644 index 7a13a14c1..000000000 --- a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/DomainEvents/AddUserIntegrationDomainEvent.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) MASA Stack All rights reserved. -// Licensed under the MIT License. See LICENSE.txt in the project root for license information. - -using Masa.BuildingBlocks.Ddd.Domain.Events; - -namespace Masa.Contrib.Ddd.Domain.Integrated.Tests.DomainEvents; - -public record AddUserIntegrationDomainEvent : IntegrationDomainEvent -{ - public string Name { get; set; } - - public override string Topic { get; set; } = nameof(AddUserIntegrationDomainEvent); -} diff --git a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/Handlers/UserHandler.cs b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/Handlers/UserHandler.cs new file mode 100644 index 000000000..97bc9796f --- /dev/null +++ b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/Handlers/UserHandler.cs @@ -0,0 +1,44 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Ddd.Domain.Integrated.Tests.Handlers; + +public class UserHandler +{ + private readonly IRepository _repository; + + public UserHandler(IRepository repository) + => _repository = repository; + + [EventHandler] + public async Task RegisterUser(RegisterUserEvent @event) + { + var user = new User(@event.Id, @event.Name); + await _repository.AddAsync(user); + } + + /// + /// todo: Event sequence is sent only for testing aggregate root + /// + [EventHandler] + public async Task RegisterUserByDomainEvent(RegisterUserDomainEvent @event) + { + var user = await _repository.FindAsync(@event.Id); + if (user != null) + { + user.UpdateName("Tom"); + await _repository.UpdateAsync(user); + } + } + + [EventHandler] + public async Task UpdateUser(UpdateUserDomainEvent @event) + { + var user = await _repository.FindAsync(@event.Id); + if (user != null && user.Name == "Tom") + { + user.Name = "Tom2"; + await _repository.UpdateAsync(user); + } + } +} diff --git a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/_Imports.cs b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/_Imports.cs index 9fd31a63d..7bf815ad3 100644 --- a/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/_Imports.cs +++ b/test/Masa.Contrib.Ddd.Domain.Integrated.Tests/_Imports.cs @@ -1,11 +1,18 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +global using Masa.BuildingBlocks.Data; +global using Masa.BuildingBlocks.Ddd.Domain.Repositories; +global using Masa.BuildingBlocks.Dispatcher.Events; global using Masa.Contrib.Data.EntityFrameworkCore; global using Masa.Contrib.Data.EntityFrameworkCore.Sqlite; global using Masa.Contrib.Data.UoW.EF; +global using Masa.Contrib.Ddd.Domain.Entities.Tests; +global using Masa.Contrib.Ddd.Domain.Entities.Tests.DomainEvents; +global using Masa.Contrib.Ddd.Domain.Entities.Tests.Events; global using Masa.Contrib.Ddd.Domain.Repository.EF; global using Masa.Contrib.Dispatcher.Events; +global using Masa.Contrib.Dispatcher.IntegrationEvents; global using Masa.Contrib.Dispatcher.IntegrationEvents.Dapr; global using Masa.Contrib.Dispatcher.IntegrationEvents.EventLogs.EF; global using Microsoft.EntityFrameworkCore; diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Domain/Entities/Orders.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Domain/Entities/Orders.cs index 07d036365..9660fdbe6 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Domain/Entities/Orders.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Domain/Entities/Orders.cs @@ -36,4 +36,3 @@ public Orders(int id) }; } } - diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Infrastructure/Repositories/OrderRepository.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Infrastructure/Repositories/OrderRepository.cs index 9d74cc4eb..05e039197 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Infrastructure/Repositories/OrderRepository.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/Infrastructure/Repositories/OrderRepository.cs @@ -14,16 +14,16 @@ public async Task AddAsync(Orders order) try { if (UnitOfWork.UseTransaction) - _ = base.Transaction; + _ = base.UnitOfWork.Transaction; await base.AddAsync(order, default); - await base.SaveChangesAsync(); - await base.CommitAsync(); + await base.UnitOfWork.SaveChangesAsync(); + await base.UnitOfWork.CommitAsync(); } catch (Exception) { if (UnitOfWork.UseTransaction) - await base.RollbackAsync(); + await base.UnitOfWork.RollbackAsync(); } } } diff --git a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs index d8fc76fc8..51a109d09 100644 --- a/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs +++ b/test/Masa.Contrib.Ddd.Domain.Repository.EF.Tests/RepositoryTest.cs @@ -327,7 +327,7 @@ await repository.AddAsync(new Orders(9999) Description = "HuaWei" }); Assert.IsTrue(repository.EntityState == BuildingBlocks.Data.UoW.EntityState.Changed); - await repository.SaveChangesAsync(); + await repository.UnitOfWork.SaveChangesAsync(); Assert.IsTrue(repository.EntityState == BuildingBlocks.Data.UoW.EntityState.UnChanged); } @@ -342,9 +342,9 @@ await repository.AddAsync(new Orders(9999) Description = "HuaWei" }); Assert.IsTrue(repository.CommitState == CommitState.UnCommited); - await repository.SaveChangesAsync(); + await repository.UnitOfWork.SaveChangesAsync(); Assert.IsTrue(repository.CommitState == CommitState.UnCommited); - await repository.CommitAsync(); + await repository.UnitOfWork.CommitAsync(); Assert.IsTrue(repository.CommitState == CommitState.Commited); } @@ -360,29 +360,11 @@ await repository.AddAsync(new Orders(9999) Description = "HuaWei" }); Assert.IsTrue(repository.CommitState == CommitState.Commited); - await repository.SaveChangesAsync(); - Assert.IsTrue(repository.CommitState == CommitState.Commited); - await Assert.ThrowsExceptionAsync(async () => - { - await repository.CommitAsync(); - }); + await repository.UnitOfWork.SaveChangesAsync(); Assert.IsTrue(repository.CommitState == CommitState.Commited); - } - - [TestMethod] - public void TestNotUseTransaction() - { - var repository = new Repository(_dbContext, _uoW); - repository.UseTransaction = false; - Assert.ThrowsException(() => repository.Transaction); - } - [TestMethod] - public async Task TestDbTransactionAsync() - { - var dbTransaction = (await _dbContext.Database.BeginTransactionAsync()).GetDbTransaction(); - var repository = new Repository(_dbContext, _uoW); - Assert.IsTrue(repository.Transaction.Equals(dbTransaction)); + var order = await repository.GetCountAsync(o => o.Id == 9999); + Assert.IsNotNull(order); } [TestMethod] @@ -401,7 +383,7 @@ await repository.AddAsync(new Orders(1) { Description = "HuaWei" }); - await repository.SaveChangesAsync(); + await repository.UnitOfWork.SaveChangesAsync(); Assert.IsTrue(await repository.GetCountAsync() == 1); } } diff --git a/test/Masa.Contrib.Ddd.Domain.Tests/_Imports.cs b/test/Masa.Contrib.Ddd.Domain.Tests/_Imports.cs index 494fcec1f..6be17b92e 100644 --- a/test/Masa.Contrib.Ddd.Domain.Tests/_Imports.cs +++ b/test/Masa.Contrib.Ddd.Domain.Tests/_Imports.cs @@ -8,10 +8,10 @@ global using Masa.BuildingBlocks.Dispatcher.IntegrationEvents; global using Masa.Contrib.Ddd.Domain.Tests.Events; global using Masa.Contrib.Ddd.Domain.Tests.Services; +global using Masa.Contrib.Dispatcher.Events; global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; global using Microsoft.VisualStudio.TestTools.UnitTesting; global using Moq; global using System.Reflection; -global using Masa.Contrib.Dispatcher.Events; -global using Microsoft.Extensions.Logging; diff --git a/test/Masa.Contrib.Dispatcher.Events.BenchmarkDotnet.Tests/Extensions/Middleware/LoggingMiddleware.cs b/test/Masa.Contrib.Dispatcher.Events.BenchmarkDotnet.Tests/Extensions/Middleware/LoggingMiddleware.cs index 3cb21dfc4..edb4fe3fb 100644 --- a/test/Masa.Contrib.Dispatcher.Events.BenchmarkDotnet.Tests/Extensions/Middleware/LoggingMiddleware.cs +++ b/test/Masa.Contrib.Dispatcher.Events.BenchmarkDotnet.Tests/Extensions/Middleware/LoggingMiddleware.cs @@ -3,12 +3,12 @@ namespace Masa.Contrib.Dispatcher.Events.BenchmarkDotnet.Tests.Extensions.Middleware; -public class LoggingMiddleware : IMiddleware where TEvent : notnull, IEvent +public class LoggingMiddleware : Middleware where TEvent : notnull, IEvent { private readonly ILogger>? _logger; public LoggingMiddleware(ILogger>? logger = null) => _logger = logger; - public async Task HandleAsync(TEvent @event, EventHandlerDelegate next) + public override async Task HandleAsync(TEvent @event, EventHandlerDelegate next) { var eventType = @event.GetType(); _logger?.LogInformation("----- Handling command {CommandName} ({@Command})", eventType.FullName, @event); diff --git a/test/Masa.Contrib.Dispatcher.Events.Tests/FeaturesTest.cs b/test/Masa.Contrib.Dispatcher.Events.Tests/FeaturesTest.cs index 6b5d5e846..85a0e7c81 100644 --- a/test/Masa.Contrib.Dispatcher.Events.Tests/FeaturesTest.cs +++ b/test/Masa.Contrib.Dispatcher.Events.Tests/FeaturesTest.cs @@ -237,7 +237,6 @@ public async Task TestUseUoWCommitAsync() var eventBus = serviceProvider.GetRequiredService(); await eventBus.PublishAsync(@event); - await eventBus.CommitAsync(default); uoW.Verify(u => u.CommitAsync(default), Times.Once); } diff --git a/test/Masa.Contrib.Dispatcher.Events.Tests/Middleware/LoggingMiddleware.cs b/test/Masa.Contrib.Dispatcher.Events.Tests/Middleware/LoggingMiddleware.cs index ca40e86ad..708bd1e19 100644 --- a/test/Masa.Contrib.Dispatcher.Events.Tests/Middleware/LoggingMiddleware.cs +++ b/test/Masa.Contrib.Dispatcher.Events.Tests/Middleware/LoggingMiddleware.cs @@ -3,12 +3,12 @@ namespace Masa.Contrib.Dispatcher.Events.Tests.Middleware; -public class LoggingMiddleware : IMiddleware where TEvent : notnull, IEvent +public class LoggingMiddleware : Middleware where TEvent : IEvent { private readonly ILogger>? _logger; public LoggingMiddleware(ILogger>? logger = null) => _logger = logger; - public async Task HandleAsync(TEvent @event, EventHandlerDelegate next) + public override async Task HandleAsync(TEvent @event, EventHandlerDelegate next) { var eventType = @event.GetType(); _logger?.LogInformation("----- Handling command {FullName} ({event})", eventType.FullName, @event);