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:
@@ -0,0 +1,6 @@
|
||||
namespace HrynCo.DAL.EF.Converters;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
internal class UtcValueConverter()
|
||||
: ValueConverter<DateTime, DateTime>(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace HrynCo.DAL.EF.Exceptions;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public sealed class UnexpectedEntityStateException : Exception
|
||||
{
|
||||
public UnexpectedEntityStateException(EntityState state)
|
||||
: base($"Unexpected entity state: {state}")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HrynCo.Common" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
Reference in New Issue
Block a user