feat: add BaseDbContext, non-generic IEntity, fix Entity constructor

- add non-generic IEntity with Created/Updated only
- IEntity<TId> now extends IEntity
- remove Created = DateTimeOffset.UtcNow from Entity constructor
- add BaseDbContext with ApplyTimestamps, UtcValueConverter, DeleteBehavior.Restrict
- add UnexpectedEntityStateException
- add HrynCo.Common dependency to HrynCo.DAL.EF

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Anatolii Grynchuk
2026-05-05 22:02:30 +03:00
parent b7aec10ee0
commit 6be7a0ceed
7 changed files with 100 additions and 4 deletions
+74
View File
@@ -0,0 +1,74 @@
namespace HrynCo.DAL.EF.Core;
using HrynCo.Common;
using HrynCo.DAL.Abstract.Entities;
using HrynCo.DAL.EF.Converters;
using HrynCo.DAL.EF.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
public abstract class BaseDbContext : DbContext
{
private readonly IClock _clock;
protected BaseDbContext(DbContextOptions options, IClock clock)
: base(options)
{
_clock = clock;
}
public override int SaveChanges()
{
ApplyTimestamps();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ApplyTimestamps();
return await base.SaveChangesAsync(cancellationToken);
}
private void ApplyTimestamps()
{
DateTimeOffset now = _clock.UtcNow;
foreach (EntityEntry<IEntity> entry in ChangeTracker.Entries<IEntity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.Created = now;
break;
case EntityState.Modified:
entry.Entity.Updated = now;
break;
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Deleted:
break;
default:
throw new UnexpectedEntityStateException(entry.State);
}
}
}
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<DateTime>().HaveConversion<UtcValueConverter>();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (IMutableForeignKey relationship in modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly);
base.OnModelCreating(modelBuilder);
}
}