- Regenerated InitialCreate migration to reflect new entity configurations - Configured many-to-many relationship between CostCycle and Unit with NoAction on Unit side to prevent multiple cascade paths - Updated UnitCost relationships: Cascade from CostCycle, NoAction from Unit - Removed commented-out global filter code and replaced with explicit relationship configurations
192 lines
8.1 KiB
C#
192 lines
8.1 KiB
C#
using Complex.Application;
|
|
using Complex.Domain.Commons;
|
|
using Complex.Domain.Entities;
|
|
using Complex.Infrastructure.Extensions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
using Microsoft.EntityFrameworkCore.Metadata;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection.Emit;
|
|
using System.Reflection.Metadata;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Complex.Infrastructure
|
|
{
|
|
public partial class ComplexDBContext : DbContext, IComplexDBContext
|
|
{
|
|
public ComplexDBContext()
|
|
{
|
|
|
|
}
|
|
public object? Find(Type entityType, object keyId)
|
|
{
|
|
return base.Find(entityType, keyId);
|
|
}
|
|
public override EntityEntry Add(object entity)
|
|
{
|
|
return base.Add(entity);
|
|
}
|
|
public ComplexDBContext(DbContextOptions<ComplexDBContext> options)
|
|
: base(options)
|
|
{
|
|
}
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
foreach (var entityType in builder.Model.GetEntityTypes())
|
|
{
|
|
if (entityType.ClrType.GetCustomAttributes(typeof(AuditableAttribute), true).Length > 0)
|
|
{
|
|
builder.Entity(entityType.Name).Property<DateTime>("InsertTime").HasDefaultValue(DateTime.Now);
|
|
builder.Entity(entityType.Name).Property<DateTime?>("UpdateTime");
|
|
builder.Entity(entityType.Name).Property<DateTime?>("RemoveTime");
|
|
builder.Entity(entityType.Name).Property<bool>("IsRemoved").HasDefaultValue(false);
|
|
}
|
|
}
|
|
builder.Entity<ComplexEntity>().HasQueryFilter(b => EF.Property<bool>(b, "IsRemoved") == false);
|
|
builder.Entity<Unit>().HasQueryFilter(b => EF.Property<bool>(b, "IsRemoved") == false);
|
|
builder.Entity<UnitCost>().HasQueryFilter(b => EF.Property<bool>(b, "IsRemoved") == false);
|
|
builder.Entity<CostCycle>().HasQueryFilter(b => EF.Property<bool>(b, "IsRemoved") == false);
|
|
|
|
// Configure many-to-many between CostCycle and Unit
|
|
// SQL Server does not allow multiple cascade paths, so one side must use NoAction
|
|
builder.Entity<CostCycle>()
|
|
.HasMany(cc => cc.Units)
|
|
.WithMany(u => u.CostCycles)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"CostCycleUnit",
|
|
j => j.HasOne<Unit>().WithMany().HasForeignKey("UnitsId").OnDelete(DeleteBehavior.NoAction),
|
|
j => j.HasOne<CostCycle>().WithMany().HasForeignKey("CostCyclesId").OnDelete(DeleteBehavior.Cascade));
|
|
|
|
// Configure UnitCost relationships to avoid multiple cascade paths
|
|
// UnitCost -> CostCycle (Cascade) and UnitCost -> Unit (NoAction)
|
|
builder.Entity<UnitCost>()
|
|
.HasOne(uc => uc.CostCycle)
|
|
.WithMany()
|
|
.HasForeignKey(uc => uc.CostCycleId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<UnitCost>()
|
|
.HasOne(uc => uc.Unit)
|
|
.WithMany()
|
|
.HasForeignKey(uc => uc.UnitId)
|
|
.OnDelete(DeleteBehavior.NoAction);
|
|
}
|
|
|
|
|
|
public override int SaveChanges()
|
|
{
|
|
var modifiedEntries = ChangeTracker.Entries()
|
|
.Where(p => p.State == EntityState.Modified ||
|
|
p.State == EntityState.Added ||
|
|
p.State == EntityState.Deleted
|
|
);
|
|
foreach (var item in modifiedEntries)
|
|
{
|
|
var entityType = item.Context.Model.FindEntityType(item.Entity.GetType());
|
|
if (entityType != null)
|
|
{
|
|
if (entityType.ClrType.GetCustomAttributes(typeof(AuditableAttribute), true).Length == 0)
|
|
continue;
|
|
var inserted = entityType.FindProperty("InsertTime");
|
|
var updated = entityType.FindProperty("UpdateTime");
|
|
var RemoveTime = entityType.FindProperty("RemoveTime");
|
|
var IsRemoved = entityType.FindProperty("IsRemoved");
|
|
if (item.State == EntityState.Added && inserted != null)
|
|
{
|
|
item.Property("InsertTime").CurrentValue = DateTime.Now;
|
|
}
|
|
if (item.State == EntityState.Modified && updated != null)
|
|
{
|
|
item.Property("UpdateTime").CurrentValue = DateTime.Now;
|
|
}
|
|
|
|
if (item.State == EntityState.Deleted && RemoveTime != null && IsRemoved != null)
|
|
{
|
|
item.Property("RemoveTime").CurrentValue = DateTime.Now;
|
|
item.Property("IsRemoved").CurrentValue = true;
|
|
item.State = EntityState.Modified;
|
|
}
|
|
}
|
|
|
|
}
|
|
return base.SaveChanges();
|
|
}
|
|
|
|
//{
|
|
// var entries = ChangeTracker
|
|
// .Entries()
|
|
// .Where(e =>
|
|
// e.State == EntityState.Added
|
|
// || e.State == EntityState.Modified);
|
|
|
|
// foreach (var entityEntry in entries)
|
|
// {
|
|
// entityEntry.Property("UpdateTime").CurrentValue = DateTime.Now;
|
|
|
|
// if (entityEntry.State == EntityState.Added)
|
|
// {
|
|
// entityEntry.Property("InsertTime").CurrentValue = DateTime.Now;
|
|
// }
|
|
// }
|
|
// entries = ChangeTracker
|
|
// .Entries()
|
|
// .Where(e =>
|
|
// e.State == EntityState.Deleted);
|
|
|
|
// foreach (var entityEntry in entries)
|
|
// {
|
|
// entityEntry.State = EntityState.Modified;
|
|
// entityEntry.Property("RemoveTime").CurrentValue = DateTime.Now;
|
|
// entityEntry.Property("IsRemoved").CurrentValue = true;
|
|
// }
|
|
|
|
// return base.SaveChanges();
|
|
//}
|
|
public virtual DbSet<UnitState> UnitStates { get; set; }
|
|
public virtual DbSet<Person> Persons { get; set; }
|
|
public virtual DbSet<ComplexPerson> ComplexPersons { get; set; }
|
|
public virtual DbSet<BasicTable> BasicTables { get; set; }
|
|
public virtual DbSet<Chat> Chats { get; set; }
|
|
public virtual DbSet<ComplexEntity> ComplexEntities { get; set; }
|
|
public virtual DbSet<ComplexManager> ComplexManagers { get; set; }
|
|
public virtual DbSet<ComplexUnitEvent> ComplexUnitEvents { get; set; }
|
|
public virtual DbSet<Poster> Posters { get; set; }
|
|
public virtual DbSet<Transaction> Transactions { get; set; }
|
|
public virtual DbSet<IncomeCostTitle> IncomeCostTitles { get; set; }
|
|
public virtual DbSet<TransactionType> TransactionTypes { get; set; }
|
|
public virtual DbSet<Unit> Units { get; set; }
|
|
public virtual DbSet<CostCycle> CostCycles { get; set; }
|
|
public virtual DbSet<UnitCost> UnitCosts { get; set; }
|
|
public virtual DbSet<UserToken> UserTokens { get; set; }
|
|
public virtual DbSet<SmsCode> SmsCodes { get; set; }
|
|
public virtual DbSet<Province> Provinces { get; set; }
|
|
public virtual DbSet<City> Cities { get; set; }
|
|
|
|
protected override void ConfigureConventions(ModelConfigurationBuilder builder)
|
|
{
|
|
|
|
builder.Properties<DateOnly>()
|
|
.HaveConversion<DateOnlyConverter>()
|
|
.HaveColumnType("date");
|
|
|
|
base.ConfigureConventions(builder);
|
|
|
|
}
|
|
}
|
|
public class DateOnlyConverter : ValueConverter<DateOnly, DateTime>
|
|
{
|
|
public DateOnlyConverter()
|
|
: base(dateOnly =>
|
|
dateOnly.ToDateTime(TimeOnly.MinValue),
|
|
dateTime => DateOnly.FromDateTime(dateTime))
|
|
{ }
|
|
}
|
|
}
|