AppDbContext.cs 6.7 KB
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Rcs.Application;
using Rcs.Domain;
using Rcs.Domain.Entities;

namespace Rcs.Infrastructure.DB.MsSql
{
    public class AppDbContext : DbContext, IAppDbContext
    {
        private readonly IDomainEventDispatcher? _domainEventDispatcher;
        private readonly ILogger<AppDbContext>? _logger;

        public AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options)
        {
        }

        public AppDbContext(
            DbContextOptions<AppDbContext> options,
            IDomainEventDispatcher domainEventDispatcher,
            ILogger<AppDbContext> logger)
            : base(options)
        {
            _domainEventDispatcher = domainEventDispatcher;
            _logger = logger;
        }
        
        // RCS领域
        public DbSet<Robot> Robots { get; set; }
        
        // Map聚合
        public DbSet<Map> Maps { get; set; }
        public DbSet<MapNode> MapNodes { get; set; }
        public DbSet<MapEdge> MapEdges { get; set; }
        public DbSet<MapResource> MapResources { get; set; }
        public DbSet<MapFile> MapFiles { get; set; }
        
        // TaskTemplate聚合
        public DbSet<TaskTemplate> TaskTemplates { get; set; }
        public DbSet<TaskStep> TaskSteps { get; set; }
        public DbSet<StepProperty> StepProperties { get; set; }
        public DbSet<StepAction> StepActions { get; set; }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
            base.OnModelCreating(modelBuilder);
        }

        /// <summary>
        /// 重写SaveChangesAsync以自动处理领域事件
        /// </summary>
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                // 1. 首先保存到数据库
                var result = await base.SaveChangesAsync(cancellationToken);

                // 2. 分发领域事件
                await DispatchDomainEventsAsync(cancellationToken);

                return result;
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "保存数据库更改时发生错误");
                throw;
            }
        }

        /// <summary>
        /// 重写SaveChanges以自动处理领域事件
        /// </summary>
        public override int SaveChanges()
        {
            return SaveChangesAsync().GetAwaiter().GetResult();
        }

        /// <summary>
        /// 分发领域事件(优化版本 - 事务顺序控制)
        /// </summary>
        private async Task DispatchDomainEventsAsync(CancellationToken cancellationToken = default)
        {
            if (_domainEventDispatcher == null)
            {
                return;
            }

            try
            {
                // 获取所有具有领域事件的实体
                var domainEntities = ChangeTracker.Entries<Entity>()
                    .Where(e => e.Entity.DomainEvents != null && e.Entity.DomainEvents.Any())
                    .Select(e => e.Entity)
                    .ToList();

                if (domainEntities.Count == 0)
                {
                    return;
                }

                _logger?.LogInformation("发现 {Count} 个包含领域事件的实体,开始全局事件排序处理", domainEntities.Count);

                // 🔧 优化:全局事件收集和排序
                var allEvents = new List<(Entity Entity, IDomainEvent Event)>();

                // 1. 收集所有事件
                foreach (var entity in domainEntities)
                {
                    var entityEvents = entity.DomainEvents.Select(e => (Entity: entity, Event: e));
                    allEvents.AddRange(entityEvents);
                }

                // 2. 按时间戳排序所有事件(事务内的顺序)
                var sortedEvents = allEvents
                    .OrderBy(x => GetEventTimestamp(x.Event)) // 只按时间戳排序
                    .ToList();

                _logger?.LogDebug("事件排序完成,共 {EventCount} 个事件", sortedEvents.Count);

                // 3. 按顺序处理事件
                for (int i = 0; i < sortedEvents.Count; i++)
                {
                    var (entity, domainEvent) = sortedEvents[i];

                    _logger?.LogDebug("处理第 {Index}/{Total} 个事件: {EventType} - 实体: {EntityType}",
                        i + 1, sortedEvents.Count, domainEvent.GetType().Name, entity.GetType().Name);

                    await _domainEventDispatcher.DispatchAsync(domainEvent, cancellationToken);
                }

                // 4. 清除所有实体的事件
                foreach (var entity in domainEntities)
                {
                    entity.ClearDomainEvents();
                }

                _logger?.LogInformation("所有 {EventCount} 个领域事件按序处理完成", sortedEvents.Count);
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "分发领域事件时发生错误");
                throw;
            }
        }

        
        /// <summary>
        /// 获取事件时间戳
        /// </summary>
        private static DateTime GetEventTimestamp(IDomainEvent domainEvent)
        {
            var properties = domainEvent.GetType().GetProperties()
                .Where(p => p.Name.Contains("Time") || p.Name.Contains("Date") || p.Name.Contains("At"))
                .ToList();

            foreach (var property in properties)
            {
                if (property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?))
                {
                    var value = property.GetValue(domainEvent);
                    if (value is DateTime dateTime)
                    {
                        return dateTime;
                    }
                }
            }

            return DateTime.Now;
        }
    }

    public interface IAppDbContext
    {
        public DbSet<Robot> Robots { get; set; }
        
        // Map聚合
        public DbSet<Map> Maps { get; set; }
        public DbSet<MapNode> MapNodes { get; set; }
        public DbSet<MapEdge> MapEdges { get; set; }
        public DbSet<MapResource> MapResources { get; set; }
        public DbSet<MapFile> MapFiles { get; set; }
        
        // TaskTemplate聚合
        public DbSet<TaskTemplate> TaskTemplates { get; set; }
        public DbSet<TaskStep> TaskSteps { get; set; }
        public DbSet<StepProperty> StepProperties { get; set; }
        public DbSet<StepAction> StepActions { get; set; }
    }
}