Reputation: 933
I have a simple application, that registers a collection
of money from batches of sold tickets from
drivers (which domain expert calls settlements
). I try to use DDD approach and EF Core to handle this small app (call it a playground for using EF Core in DDD). I have basically 3 tables in SQL Server (simplified to the absolute minimum):
Table: Collection
-------------+---------
Column | Type
-------------+---------
CollectionId | int (PK)
IsCancelled | bit
Table: Settlement
-------------+---------
Column | Type
-------------+---------
SettlementId | int (PK)
CollectionId | int (FK)
Number | int
Table: CollectionSettlementJoin
-------------+---------
Column | Type
-------------+---------
SettlementId | int (PK)(FK)
CollectionId | int (PK)(FK)
I know there seems to be a redundant join table (since I have the CollectionId
on the Settlement
table), but it seems to be a design requirement, that I will explain in a moment. So each collection
has at least 1 or more settlements
. I have 2 domain entities which actually correspond with the tables - my aggregate root Collection
and attached to it Settlements
property that contains list of Settlement
entities.
The extra table is used for auditing purposes as actually does not take real part in the domain. It is populated by a trigger on Settlement.CollectionId
update (for non-nulls). Each collection
can be cancelled within 5 minutes of its creation by the creator or anytime by a superuser. When a collection
is cancelled, I want to reset Settlement.CollectionId
to null
(when that happens data in CollectionSettlementJoin
stays and I can always get back what settlements were cancelled).
My current setup is working fine when comes to creating a collection
. The selected settlements
are added, saved and successfully persisted in my database. The problem starts when I want to cancel a collection. I get from the database the collection
with attached settlements
. But when I remove the settlements
from my aggregate root, dbContext.SaveChanges()
does not persist the changes (does not set Settlement.CollectionId
to null
).
Here is my setup:
public class Collection : EntityBase, IAggregateRoot
{
private Collection() { }
public Collection(List<Settlement> settlements)
{
_settlements = settlements;
}
private bool _isCancelled;
public bool IsCancelled => _isCancelled;
private List<Settlement> _settlements;
public IReadOnlyCollection<Settlement> Settlements => _settlements.AsReadOnly();
public void CancelCollection()
{
if (_isCancelled != true)
{
_isCancelled = true;
_settlements.Clear();
}
}
}
public class Settlement : EntityBase
{
private Settlement() { }
public Collection? Collection { get;set; }
public int? CollectionId { get; internal set; }
}
public class CollectionEntityTypeConfiguration : IEntityTypeConfiguration<Collection>
{
public void Configure(EntityTypeBuilder<Collection> builder)
{
builder.ToTable("Collection");
builder.HasKey(s => s.Id);
builder.Property(s => s.Id).HasColumnName("CollectionId").UseIdentityColumn();
builder.Property(s => s.IsCancelled).HasDefaultValueSql("(0)");
builder.HasMany(s => s.Settlements)
.WithOne(c => c.Collection)
.HasForeignKey(s => s.CollectionId);
}
}
public class SettlementEntityTypeConfiguration : IEntityTypeConfiguration<Settlement>
{
public void Configure(EntityTypeBuilder<Settlement> builder)
{
builder.ToTable("Settlement");
builder.HasKey(s => s.Id);
builder.Property(s => s.Id).HasColumnName("SettlementId").ValueGeneratedNever();
builder.Property(s => s.CollectionId);
builder.HasOne(s => s.Collection)
.WithMany(c => c.Settlements)
.HasForeignKey(s => s.CollectionId);
}
}
Context (not much here)
public class SettlementCollectionContext: DbContext
{
public SettlementCollectionContext(
DbContextOptions<SettlementCollectionContext> options) : base(options)
{
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
}
private void ChangeTracker_StateChanged(object sender, EntityStateChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"Entity {e.Entry.Entity.GetType().Name} has changed.");
}
public DbSet<Collection> Collections { get; set; }
public DbSet<Settlement> Settlements { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new SettlementEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new CollectionEntityTypeConfiguration());
}
}
And finally my command:
var collection = await _dbContext.Collections
.Include(c => c.Settlements)
.Where(c => c.Id == collectionId)
.SingleOrDefaultAsync();
if (!collection!.IsCancelled)
{
collection.CancelCollection();
}
_dbContext.Update(collection); //without this the change tracker does not register the change
_dbContext.SaveChanges();
I know that the ChangeTracker
registeres this change, becuase I added in my context an even handler to ChangeTracker.StateChanged
and during debugging I noticed it register that collection
has changed (although not the settlement
). I also tried to reset Settlement.CollectionId
property to null
in Collection.CancelCollection()
method, but this did not help either. I must be missing something.
Upvotes: 1
Views: 86
Reputation: 933
So in the end nothing is wrong with the setup. Actually my unit of work was a problem, because it was saving incorrect context, so obviously the changes were never persisted.
Upvotes: 1