Please support domain event publishing of 'EntityBase<TId>'
A new entity abstract class 'EntityBase<TId>' has been added in the latest version, but in the SaveChangesAsync method under DbContext, the entity of 'EntityBase' cannot be obtained through ChangeTracker.
Because 'EntityBase<TId>' does not inherit from 'EntityBase'
I originally planned to get the entities that need to publish domain events through their common inheritance, that is, 'HasDomainEventsBase', but I failed when I tried to ClearDomainEvents because it has an internal access level.
So I hope to add support for 'EntityBase<TId>' in the future
Yeah, this behavior should be supported. I thought I'd verified it was working. Can you provide some sample code that isn't working?
Yeah, this behavior should be supported. I thought I'd verified it was working. Can you provide some sample code that isn't working?
@ardalis Yes,
In Core
public class Category : EntityBase<uint>, IAggregateRoot
{
public uint ParentId { get; private set; }
public string Name { get; private set; } = default!;
protected Category() { }
public Category(uint parentId, string name)
{
ParentId = parentId;
Name = Guard.Against.NullOrWhiteSpace(name, nameof(name));
RegisterDomainEvent(new CategoryCreatedEvent(parentId, name));
}
}
In UseCases
public record CreateCategoryCommand(uint ParentId, string Name) : ICommand<Result<uint>>;
public class CreateCategoryHandler(IRepository<Category> repository) : ICommandHandler<CreateCategoryCommand, Result<uint>>
{
public async Task<Result<uint>> Handle(CreateCategoryCommand request, CancellationToken cancellationToken)
{
var result = await repository.AddAsync(new Category(request.ParentId, request.Name), cancellationToken);
return result.Id;
}
}
In Infrastructure
public class AppDbContext(DbContextOptions<AppDbContext> options, IDomainEventDispatcher? dispatcher) : DbContext(options)
{
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
if (dispatcher == null) return await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
// bug here
// ChangeTracker.Entries<EntityBase>() cannot return instances of 'EntityBase<TId>'
// because there is no inheritance relationship between EntityBase<TId> and EntityBase
var entitiesWithEvents = ChangeTracker.Entries<EntityBase>()
.Select(e => e.Entity)
.Where(e => e.DomainEvents.Any())
.ToArray();
// Task DispatchAndClearEvents(IEnumerable<EntityBase> entitiesWithEvents);
// DispatchAndClearEvents method only accepts IEnumerable<EntityBase>
await dispatcher.DispatchAndClearEvents(entitiesWithEvents);
return await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);;
}
public override int SaveChanges()
{
return SaveChangesAsync().GetAwaiter().GetResult();
}
}
Maybe make DispatchAndClearEvents accept IEnumerable<HasDomainEventsBase>
@ardalis just wanted to check in if you're planning to knock this off. This one is bugging me as well. Thanks...