using Infrastructure.Domain; using Infrastructure.Events; using Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; namespace Infrastructure.Data { public class EfDbContext : DbContext { public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); protected readonly ILogger _logger; protected readonly IHostEnvironment _env; protected readonly IConfiguration _cfg; protected readonly IEventPublisher _publisher; public EfDbContext(DbContextOptions options, ILogger logger, IHostEnvironment env, IConfiguration cfg, IEventPublisher publisher) : base(options) { this._logger = logger; this._env = env; this._cfg = cfg; this._publisher = publisher; this.ChangeTracker.AutoDetectChangesEnabled = false; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder?.UseLoggerFactory(MyLoggerFactory); optionsBuilder?.EnableSensitiveDataLogging(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { if (modelBuilder is null) { throw new ArgumentNullException(nameof(modelBuilder)); } var entityTypes = modelBuilder.Model.GetEntityTypes().ToList(); var tablePrefix = this._cfg.GetAppSetting("TablePrefix"); foreach (var entity in entityTypes) { if (!string.IsNullOrEmpty(tablePrefix)) { entity.SetTableName($"{tablePrefix}_{entity.GetTableName()}"); } if (entity.GetProperties().Any(o => o.Name == "Id")) { modelBuilder.Entity(entity.Name).HasKey("Id"); modelBuilder.Entity(entity.Name).Property("Id").ValueGeneratedNever(); } if (entity.GetProperties().Any(o => o.Name == "RowVersion")) { modelBuilder.Entity(entity.Name).Property("RowVersion").IsConcurrencyToken().ValueGeneratedNever(); } if (entity.GetProperties().Any(o => o.Name == "Created")) { modelBuilder.Entity(entity.Name).Property("Created").ValueGeneratedOnAdd(); } if (entity.GetProperties().Any(o => o.Name == "Modified")) { modelBuilder.Entity(entity.Name).Property("Modified").ValueGeneratedOnUpdate(); } if (entity.GetProperties().Any(o => o.Name == "Parent") && entity.GetProperties().Any(o => o.Name == "Children")) { modelBuilder.Entity(entity.Name).HasOne("Parent").WithMany("Children").HasForeignKey(new string[] { "ParentId" }).OnDelete(DeleteBehavior.SetNull); if (entity.GetProperties().Any(o => o.Name == "Name")) { modelBuilder.Entity(entity.Name).Property("Name").IsRequired(); } if (entity.GetProperties().Any(o => o.Name == "Number")) { modelBuilder.Entity(entity.Name).Property("Number").IsRequired(); } } modelBuilder.Entity(entity.Name).HasComment(entity.ClrType.GetDisplayName()); foreach (var prop in entity.GetProperties()) { modelBuilder.Entity(entity.ClrType).Property(prop.Name).HasComment(prop.PropertyInfo.GetDisplayName()); } } } public override int SaveChanges() { this.ChangeTracker.DetectChanges(); var entries = this.ChangeTracker.Entries().Where(o => o.State == EntityState.Added || o.State == EntityState.Modified || o.State == EntityState.Deleted).ToList(); var events = new List(); foreach (var entry in entries) { var entity = entry.Entity as BaseEntity; if (entity is IValidatableObject) { var validationResults = new List(); if (!Validator.TryValidateObject(entry.Entity, new ValidationContext(entity, null, null), validationResults, true)) { throw new EntityInvalidException(validationResults, $"{entity.GetType().FullName} valid error"); } } if (entity is IVersionEntity) { (entity as IVersionEntity).RowVersion = Guid.NewGuid().ToString(); } var now = DateTimeOffset.UtcNow; if (entry.State == EntityState.Added) { entity.Created = now; events.Add(Activator.CreateInstance(typeof(EntityInserted<>).MakeGenericType(entry.Entity.GetType()), entry.Entity)); } else if (entry.State == EntityState.Modified) { entity.Modified = now; events.Add(Activator.CreateInstance(typeof(EntityUpdated<>).MakeGenericType(entry.Entity.GetType()), entry.Entity, entry.OriginalValues.ToObject())); } else if (entry.State == EntityState.Deleted) { events.Add(Activator.CreateInstance(typeof(EntityDeleted<>).MakeGenericType(entry.Entity.GetType()), entry.Entity)); } this._logger.LogDebug($"{nameof(EfDbContext)}>{entity.GetType().Name}:{entry.State}"); } try { var result = base.SaveChanges(); if (events.Count > 0) { events.ForEach(o => this._publisher.Publish(o)); } return result; } catch (Exception ex) { ChangeTracker.Clear(); ex.PrintStack(); this._logger.LogError(ex.ToString()); throw; } } } }