- Simplified the .env.example file by removing outdated comments and consolidating environment variable descriptions. - Updated the architecture documentation to reflect the new structure and components of the .NET 10 microservice template. - Enhanced clarity in the README.md to provide a more comprehensive overview of the template's features and requirements. - Removed obsolete appsettings files to streamline the project structure and improve usability.
161 lines
4.9 KiB
C#
161 lines
4.9 KiB
C#
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using MyService.Domain.AggregatesModel.SampleAggregate;
|
|
using MyService.Domain.SeedWork;
|
|
using MyService.Infrastructure.EntityConfigurations;
|
|
|
|
namespace MyService.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// EN: EF Core DbContext for MyService.
|
|
/// VI: EF Core DbContext cho MyService.
|
|
/// </summary>
|
|
public class MyServiceContext : DbContext, IUnitOfWork
|
|
{
|
|
private readonly IMediator _mediator;
|
|
private IDbContextTransaction? _currentTransaction;
|
|
|
|
/// <summary>
|
|
/// EN: Samples table.
|
|
/// VI: Bảng Samples.
|
|
/// </summary>
|
|
public DbSet<Sample> Samples => Set<Sample>();
|
|
|
|
/// <summary>
|
|
/// EN: Read-only access to current transaction.
|
|
/// VI: Truy cập chỉ đọc đến transaction hiện tại.
|
|
/// </summary>
|
|
public IDbContextTransaction? CurrentTransaction => _currentTransaction;
|
|
|
|
/// <summary>
|
|
/// EN: Check if there is an active transaction.
|
|
/// VI: Kiểm tra xem có transaction đang hoạt động không.
|
|
/// </summary>
|
|
public bool HasActiveTransaction => _currentTransaction != null;
|
|
|
|
public MyServiceContext(DbContextOptions<MyServiceContext> options) : base(options)
|
|
{
|
|
_mediator = null!;
|
|
}
|
|
|
|
public MyServiceContext(DbContextOptions<MyServiceContext> options, IMediator mediator) : base(options)
|
|
{
|
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
|
|
|
System.Diagnostics.Debug.WriteLine("MyServiceContext::ctor - " + GetHashCode());
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
// EN: Apply entity configurations
|
|
// VI: Áp dụng các cấu hình entity
|
|
modelBuilder.ApplyConfiguration(new SampleEntityTypeConfiguration());
|
|
modelBuilder.ApplyConfiguration(new SampleStatusEntityTypeConfiguration());
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Save entities and dispatch domain events.
|
|
/// VI: Lưu entities và dispatch domain events.
|
|
/// </summary>
|
|
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
// EN: Dispatch domain events before saving (side effects)
|
|
// VI: Dispatch domain events trước khi lưu (side effects)
|
|
await DispatchDomainEventsAsync();
|
|
|
|
// EN: Save changes to database
|
|
// VI: Lưu thay đổi vào database
|
|
await base.SaveChangesAsync(cancellationToken);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Begin a new transaction if none is active.
|
|
/// VI: Bắt đầu một transaction mới nếu không có transaction nào đang hoạt động.
|
|
/// </summary>
|
|
public async Task<IDbContextTransaction?> BeginTransactionAsync()
|
|
{
|
|
if (_currentTransaction != null) return null;
|
|
|
|
_currentTransaction = await Database.BeginTransactionAsync(System.Data.IsolationLevel.ReadCommitted);
|
|
|
|
return _currentTransaction;
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Commit the current transaction.
|
|
/// VI: Commit transaction hiện tại.
|
|
/// </summary>
|
|
public async Task CommitTransactionAsync(IDbContextTransaction transaction)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(transaction);
|
|
|
|
if (transaction != _currentTransaction)
|
|
throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
|
|
|
|
try
|
|
{
|
|
await SaveChangesAsync();
|
|
await transaction.CommitAsync();
|
|
}
|
|
catch
|
|
{
|
|
RollbackTransaction();
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
if (_currentTransaction != null)
|
|
{
|
|
_currentTransaction.Dispose();
|
|
_currentTransaction = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Rollback the current transaction.
|
|
/// VI: Rollback transaction hiện tại.
|
|
/// </summary>
|
|
public void RollbackTransaction()
|
|
{
|
|
try
|
|
{
|
|
_currentTransaction?.Rollback();
|
|
}
|
|
finally
|
|
{
|
|
if (_currentTransaction != null)
|
|
{
|
|
_currentTransaction.Dispose();
|
|
_currentTransaction = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Dispatch all domain events from tracked entities.
|
|
/// VI: Dispatch tất cả domain events từ các entities đang được track.
|
|
/// </summary>
|
|
private async Task DispatchDomainEventsAsync()
|
|
{
|
|
var domainEntities = ChangeTracker
|
|
.Entries<Entity>()
|
|
.Where(x => x.Entity.DomainEvents.Any())
|
|
.ToList();
|
|
|
|
var domainEvents = domainEntities
|
|
.SelectMany(x => x.Entity.DomainEvents)
|
|
.ToList();
|
|
|
|
domainEntities.ForEach(entity => entity.Entity.ClearDomainEvents());
|
|
|
|
foreach (var domainEvent in domainEvents)
|
|
{
|
|
await _mediator.Publish(domainEvent);
|
|
}
|
|
}
|
|
}
|