using System.Linq.Expressions; using BuildFlow.Shared.Domain.Common; using BuildFlow.Shared.Infrastructure; using Microsoft.EntityFrameworkCore; using Scripting.Domain.Entities; namespace Scripting.Infrastructure.Persistence; /// /// Scripting 持久化上下文。承载脚本函数定义与执行日志。 /// 注册 AuditInterceptor 自动维护审计字段;对实现 ISoftDelete 的实体统一生成全局查询过滤器, /// 自动排除已软删除记录。约定与 WorkflowDbContext 保持一致。 /// public class ScriptingDbContext : DbContext { private readonly ICurrentUserContext? _currentUserContext; public DbSet Scripts => Set(); public DbSet ScriptExecutionLogs => Set(); public ScriptingDbContext(DbContextOptions options) : base(options) { } public ScriptingDbContext(DbContextOptions options, ICurrentUserContext currentUserContext) : base(options) { _currentUserContext = currentUserContext; } // 注意:此处通过 OnConfiguring 注册拦截器(而非 DI AddInterceptors),与 rag-backend 约定一致, // 保证无参构造(如设计时工厂、测试夹具)也能正常工作。 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (_currentUserContext is not null) { optionsBuilder.AddInterceptors(new AuditInterceptor(_currentUserContext)); } } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 自动发现本程序集内所有 IEntityTypeConfiguration 实现 modelBuilder.ApplyConfigurationsFromAssembly(typeof(ScriptingDbContext).Assembly); // 为所有实现 ISoftDelete 的实体动态生成 HasQueryFilter(e => !e.IsDeleted) foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType)) { modelBuilder.Entity(entityType.ClrType).HasQueryFilter( CreateSoftDeleteFilter(entityType.ClrType)); } } } /// 用表达式树动态构造 e => !e.IsDeleted,避免对每个实体类型手写泛型过滤。 private static LambdaExpression CreateSoftDeleteFilter(Type entityType) { var param = Expression.Parameter(entityType, "e"); var isDeletedProp = Expression.Property(param, nameof(ISoftDelete.IsDeleted)); var filter = Expression.Lambda(Expression.Not(isDeletedProp), param); return filter; } }