Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Only allow for opting out of aggregate side effects in unit of work. #1389

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Digdir.Domain.Dialogporten.Application.Externals;

public interface IUnitOfWork
{
IUnitOfWork WithoutAuditableSideEffects();
IUnitOfWork WithoutAggregateSideEffects();
Task<SaveChangesResult> SaveChangesAsync(CancellationToken cancellationToken = default);

IUnitOfWork EnableConcurrencyCheck<TEntity>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public async Task<GetDialogResult> Handle(GetDialogQuery request, CancellationTo
currentUserInformation.Name);

var saveResult = await _unitOfWork
.WithoutAuditableSideEffects()
.WithoutAggregateSideEffects()
.SaveChangesAsync(cancellationToken);

saveResult.Switch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public async Task<GetDialogResult> Handle(GetDialogQuery request, CancellationTo
currentUserInformation.Name);

var saveResult = await _unitOfWork
.WithoutAuditableSideEffects()
.WithoutAggregateSideEffects()
.SaveChangesAsync(cancellationToken);

saveResult.Switch(
Expand Down
12 changes: 7 additions & 5 deletions src/Digdir.Domain.Dialogporten.Infrastructure/UnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal sealed class UnitOfWork : IUnitOfWork, IAsyncDisposable, IDisposable

private IDbContextTransaction? _transaction;

private bool _auditableSideEffects = true;
private bool _aggregateSideEffects = true;
private bool _enableConcurrencyCheck;

public UnitOfWork(DialogDbContext dialogDbContext, ITransactionTime transactionTime, IDomainContext domainContext)
Expand All @@ -51,9 +51,9 @@ public IUnitOfWork EnableConcurrencyCheck<TEntity>(
return this;
}

public IUnitOfWork WithoutAuditableSideEffects()
public IUnitOfWork WithoutAggregateSideEffects()
{
_auditableSideEffects = false;
_aggregateSideEffects = false;
return this;
}

Expand Down Expand Up @@ -94,9 +94,11 @@ private async Task<SaveChangesResult> SaveChangesAsync_Internal(CancellationToke
return new Success();
}

if (_auditableSideEffects)
_dialogDbContext.ChangeTracker.HandleAuditableEntities(_transactionTime.Value);

if (_aggregateSideEffects)
{
await _dialogDbContext.ChangeTracker.HandleAuditableEntities(_transactionTime.Value, cancellationToken);
await _dialogDbContext.ChangeTracker.HandleAggregateEntities(_transactionTime.Value, cancellationToken);
}

if (!_enableConcurrencyCheck)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Digdir.Library.Entity.EntityFrameworkCore.Features.Creatable;
using Digdir.Library.Entity.Abstractions.Features.Aggregate;
using Digdir.Library.Entity.EntityFrameworkCore.Features.Creatable;
using Digdir.Library.Entity.EntityFrameworkCore.Features.Identifiable;
using Digdir.Library.Entity.EntityFrameworkCore.Features.Lookup;
using Digdir.Library.Entity.EntityFrameworkCore.Features.SoftDeletable;
Expand All @@ -8,6 +9,7 @@
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Digdir.Library.Entity.Abstractions.Features.Creatable;
using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Library.Entity.Abstractions.Features.Immutable;
using Digdir.Library.Entity.Abstractions.Features.Lookup;
using Digdir.Library.Entity.Abstractions.Features.SoftDeletable;
using Digdir.Library.Entity.Abstractions.Features.Updatable;
Expand All @@ -23,36 +25,54 @@ namespace Digdir.Library.Entity.EntityFrameworkCore;
/// </summary>
public static class EntityLibraryEfCoreExtensions
{
/// <summary>
/// Updates the properties and sets the correct <see cref="EntityState"/> on the <see cref="ChangeTracker"/> for the entities implementing the following abstractions in context of aggregates.
/// <list type="bullet">
/// <item><see cref="IAggregateCreatedHandler"/></item>
/// <item><see cref="IAggregateUpdatedHandler"/></item>
/// <item><see cref="IAggregateDeletedHandler"/></item>
/// <item><see cref="IAggregateRestoredHandler"/></item>
/// <item><see cref="IUpdateableEntity"/></item>
/// <item><see cref="IVersionableEntity"/></item>
/// </list>
/// </summary>
/// <remarks>
/// Should be called right before saving the entities.
/// </remarks>
/// <param name="changeTracker">The change tracker.</param>
/// <param name="utcNow">The time in UTC in which the changes tok place.</param>
/// <param name="cancellationToken">A token for requesting cancellation of the operation.</param>
/// <returns>The same <see cref="ChangeTracker"/> instance so that multiple calls can be chained.</returns>
public static Task<ChangeTracker> HandleAggregateEntities(
this ChangeTracker changeTracker,
DateTimeOffset utcNow,
CancellationToken cancellationToken = default)
=> AggregateExtensions.HandleAggregateEntities(changeTracker, utcNow, cancellationToken);

/// <summary>
/// Updates the properties and sets the correct <see cref="EntityState"/> on the <see cref="ChangeTracker"/> for the entities implementing the following abstractions.
/// <list type="bullet">
/// <item><see cref="ILookupEntity"/></item>
/// <item><see cref="IIdentifiableEntity"/></item>
/// <item><see cref="ICreatableEntity"/></item>
/// <item><see cref="IUpdateableEntity"/></item>
/// <item><see cref="ISoftDeletableEntity"/></item>
/// <item><see cref="IIdentifiableEntity"/></item>
/// <item><see cref="IVersionableEntity"/></item>
/// <item><see cref="IImmutableEntity"/></item>
/// <item><see cref="ILookupEntity"/></item>
/// </list>
/// </summary>
/// <remarks>
/// Should be called right before saving the entities.
/// </remarks>
/// <param name="changeTracker">The change tracker.</param>
/// <param name="utcNow">The time in UTC in which the changes tok place.</param>
/// <param name="cancellationToken">A token for requesting cancellation of the operation.</param>
/// <returns>The same <see cref="ChangeTracker"/> instance so that multiple calls can be chained.</returns>
public static async Task<ChangeTracker> HandleAuditableEntities(this ChangeTracker changeTracker, DateTimeOffset utcNow, CancellationToken cancellationToken = default)
{
changeTracker.HandleLookupEntities()
public static ChangeTracker HandleAuditableEntities(this ChangeTracker changeTracker, DateTimeOffset utcNow)
=> changeTracker.HandleLookupEntities()
.HandleIdentifiableEntities()
.HandleImmutableEntities()
//.HandleVersionableEntities()
.HandleCreatableEntities(utcNow);
//.HandleUpdatableEntities(utcNow);
await changeTracker.HandleAggregateEntities(utcNow, cancellationToken);
return changeTracker.HandleSoftDeletableEntities(utcNow);
}
.HandleCreatableEntities(utcNow)
.HandleUpdatableEntities(utcNow)
.HandleSoftDeletableEntities(utcNow);

/// <summary>
/// Configures the shape of, and how the entities implementing the following abstractions are mapped to the database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal static class AggregateExtensions
{
private static readonly EntityEntryComparer _entityEntryComparer = new();

internal static async Task HandleAggregateEntities(this ChangeTracker changeTracker,
internal static async Task<ChangeTracker> HandleAggregateEntities(this ChangeTracker changeTracker,
DateTimeOffset utcNow, CancellationToken cancellationToken)
{
var aggregateNodeByEntry = await changeTracker
Expand Down Expand Up @@ -57,6 +57,8 @@ internal static async Task HandleAggregateEntities(this ChangeTracker changeTrac
versionable.NewVersion();
}
}

return changeTracker;
}

internal static ModelBuilder AddAggregateEntities(this ModelBuilder modelBuilder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public static ChangeTracker HandleUpdatableEntities(this ChangeTracker changeTra
{
var updatableEntities = changeTracker
.Entries<IUpdateableEntity>()
.Where(x => x.State is EntityState.Added or EntityState.Modified && (x.Entity is not ISoftDeletableEntity deletable || !deletable.Deleted));
.Where(IsNotSoftDeleted)
.Where(x => AddedWithoutExplicitUpdatedAt(x) || Modified(x));

foreach (var entity in updatableEntities)
{
Expand All @@ -26,4 +27,8 @@ public static ChangeTracker HandleUpdatableEntities(this ChangeTracker changeTra

return changeTracker;
}

private static bool IsNotSoftDeleted(EntityEntry<IUpdateableEntity> x) => x.Entity is not ISoftDeletableEntity { Deleted: true };
private static bool AddedWithoutExplicitUpdatedAt(EntityEntry<IUpdateableEntity> x) => x.State is EntityState.Added && x.Entity.UpdatedAt == default;
private static bool Modified(EntityEntry<IUpdateableEntity> x) => x.State is EntityState.Modified;
}
Loading