223 lines
10 KiB
C#
223 lines
10 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using DiunaBI.Domain.Entities;
|
|
|
|
namespace DiunaBI.Infrastructure.Data;
|
|
|
|
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
|
{
|
|
public DbSet<User> Users { get; init; }
|
|
public DbSet<Layer> Layers { get; init; }
|
|
public DbSet<Record> Records { get; init; }
|
|
public DbSet<RecordHistory> RecordHistory { get; init; }
|
|
public DbSet<ProcessSource> ProcessSources { get; init; }
|
|
public DbSet<DataInbox> DataInbox { get; init; }
|
|
public DbSet<QueueJob> QueueJobs { get; init; }
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
modelBuilder.Entity<User>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<User>().Property(x => x.Email).HasMaxLength(50);
|
|
modelBuilder.Entity<User>().Property(x => x.UserName).HasMaxLength(50);
|
|
|
|
modelBuilder.Entity<Layer>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<Layer>().Property(x => x.Number).IsRequired();
|
|
modelBuilder.Entity<Layer>().Property(x => x.Name).IsRequired().HasMaxLength(50);
|
|
modelBuilder.Entity<Layer>().Property(x => x.Type).IsRequired().HasConversion<int>();
|
|
modelBuilder.Entity<Layer>().Property(x => x.CreatedAt).IsRequired();
|
|
modelBuilder.Entity<Layer>().Property(x => x.ModifiedAt).IsRequired();
|
|
modelBuilder.Entity<Layer>().Property(x => x.IsDeleted).IsRequired().HasDefaultValue(false);
|
|
modelBuilder.Entity<Layer>().Property(x => x.IsCancelled).IsRequired().HasDefaultValue(false);
|
|
modelBuilder.Entity<Layer>().Property(x => x.CreatedById).IsRequired();
|
|
modelBuilder.Entity<Layer>().Property(x => x.ModifiedById).IsRequired();
|
|
|
|
modelBuilder.Entity<Layer>()
|
|
.HasOne(x => x.CreatedBy)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.CreatedById)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<Layer>()
|
|
.HasOne(x => x.ModifiedBy)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.ModifiedById)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<Layer>()
|
|
.HasMany(x => x.Records)
|
|
.WithOne()
|
|
.HasForeignKey(r => r.LayerId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
modelBuilder.Entity<Record>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<Record>().Property(x => x.Code).IsRequired().HasMaxLength(50);
|
|
modelBuilder.Entity<Record>().Property(x => x.Desc1).HasMaxLength(10000);
|
|
modelBuilder.Entity<Record>().Property(x => x.CreatedAt);
|
|
modelBuilder.Entity<Record>().Property(x => x.ModifiedAt);
|
|
modelBuilder.Entity<Record>().Property(x => x.IsDeleted);
|
|
modelBuilder.Entity<Record>().Property(x => x.CreatedById).IsRequired();
|
|
modelBuilder.Entity<Record>().Property(x => x.ModifiedById).IsRequired();
|
|
modelBuilder.Entity<Record>().Property(x => x.LayerId).IsRequired();
|
|
|
|
modelBuilder.Entity<Record>()
|
|
.HasOne(x => x.CreatedBy)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.CreatedById)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<Record>()
|
|
.HasOne(x => x.ModifiedBy)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.ModifiedById)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<Record>()
|
|
.HasOne<Layer>()
|
|
.WithMany(l => l.Records!)
|
|
.HasForeignKey(x => x.LayerId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
modelBuilder.Entity<RecordHistory>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.RecordId).IsRequired();
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.LayerId).IsRequired();
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.ChangedAt).IsRequired();
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.ChangedById).IsRequired();
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.ChangeType).IsRequired().HasConversion<int>();
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.Code).IsRequired().HasMaxLength(50);
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.Desc1).HasMaxLength(10000);
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.ChangedFields).HasMaxLength(200);
|
|
modelBuilder.Entity<RecordHistory>().Property(x => x.ChangesSummary).HasMaxLength(4000);
|
|
|
|
// Indexes for efficient history queries
|
|
modelBuilder.Entity<RecordHistory>()
|
|
.HasIndex(x => new { x.RecordId, x.ChangedAt });
|
|
|
|
modelBuilder.Entity<RecordHistory>()
|
|
.HasIndex(x => new { x.LayerId, x.ChangedAt });
|
|
|
|
modelBuilder.Entity<RecordHistory>()
|
|
.HasOne(x => x.ChangedBy)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.ChangedById)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<ProcessSource>().HasKey(x => new { x.LayerId, x.SourceId });
|
|
modelBuilder.Entity<ProcessSource>().Property(x => x.LayerId).IsRequired();
|
|
modelBuilder.Entity<ProcessSource>().Property(x => x.SourceId).IsRequired();
|
|
|
|
modelBuilder.Entity<ProcessSource>()
|
|
.HasOne<Layer>()
|
|
.WithMany()
|
|
.HasForeignKey(x => x.LayerId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
modelBuilder.Entity<ProcessSource>()
|
|
.HasOne(x => x.Source)
|
|
.WithMany()
|
|
.HasForeignKey(x => x.SourceId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
modelBuilder.Entity<DataInbox>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<DataInbox>().Property(x => x.Name).IsRequired().HasMaxLength(50);
|
|
modelBuilder.Entity<DataInbox>().Property(x => x.Source).IsRequired().HasMaxLength(50);
|
|
modelBuilder.Entity<DataInbox>().Property(x => x.Data).IsRequired();
|
|
modelBuilder.Entity<DataInbox>().Property(x => x.CreatedAt);
|
|
|
|
modelBuilder.Entity<QueueJob>().HasKey(x => x.Id);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.LayerId).IsRequired();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.LayerName).IsRequired().HasMaxLength(200);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.PluginName).IsRequired().HasMaxLength(100);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.JobType).IsRequired().HasConversion<int>();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.Priority);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.CreatedAt).IsRequired();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.RetryCount);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.MaxRetries);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.Status).IsRequired().HasConversion<int>();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.LastError).HasMaxLength(1000);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.LastAttemptAt);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.CompletedAt);
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.CreatedById).IsRequired();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.ModifiedById).IsRequired();
|
|
modelBuilder.Entity<QueueJob>().Property(x => x.ModifiedAt).IsRequired();
|
|
|
|
// Configure automatic timestamps for entities with CreatedAt/ModifiedAt
|
|
ConfigureTimestamps(modelBuilder);
|
|
}
|
|
|
|
private void ConfigureTimestamps(ModelBuilder modelBuilder)
|
|
{
|
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
|
{
|
|
// Check if entity has CreatedAt property
|
|
var createdAtProperty = entityType.FindProperty("CreatedAt");
|
|
if (createdAtProperty != null)
|
|
{
|
|
modelBuilder.Entity(entityType.ClrType)
|
|
.Property("CreatedAt")
|
|
.HasDefaultValueSql("GETUTCDATE()");
|
|
}
|
|
|
|
// Check if entity has ModifiedAt property
|
|
var modifiedAtProperty = entityType.FindProperty("ModifiedAt");
|
|
if (modifiedAtProperty != null)
|
|
{
|
|
modelBuilder.Entity(entityType.ClrType)
|
|
.Property("ModifiedAt")
|
|
.HasDefaultValueSql("GETUTCDATE()");
|
|
}
|
|
}
|
|
}
|
|
|
|
public override int SaveChanges()
|
|
{
|
|
UpdateTimestamps();
|
|
return base.SaveChanges();
|
|
}
|
|
|
|
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
UpdateTimestamps();
|
|
return base.SaveChangesAsync(cancellationToken);
|
|
}
|
|
|
|
private void UpdateTimestamps()
|
|
{
|
|
var entities = ChangeTracker.Entries()
|
|
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);
|
|
|
|
foreach (var entity in entities)
|
|
{
|
|
// Try to set CreatedAt for new entities
|
|
if (entity.State == EntityState.Added)
|
|
{
|
|
var createdAtProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "CreatedAt");
|
|
if (createdAtProperty != null)
|
|
{
|
|
createdAtProperty.CurrentValue = DateTime.UtcNow;
|
|
}
|
|
|
|
// Ensure IsDeleted and IsCancelled have default values for Layer entities
|
|
if (entity.Entity is Layer)
|
|
{
|
|
var isDeletedProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "IsDeleted");
|
|
if (isDeletedProperty != null && isDeletedProperty.CurrentValue == null)
|
|
{
|
|
isDeletedProperty.CurrentValue = false;
|
|
}
|
|
|
|
var isCancelledProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "IsCancelled");
|
|
if (isCancelledProperty != null && isCancelledProperty.CurrentValue == null)
|
|
{
|
|
isCancelledProperty.CurrentValue = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Always update ModifiedAt
|
|
var modifiedAtProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "ModifiedAt");
|
|
if (modifiedAtProperty != null)
|
|
{
|
|
modifiedAtProperty.CurrentValue = DateTime.UtcNow;
|
|
}
|
|
}
|
|
}
|
|
} |