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

Please support domain event publishing of 'EntityBase<TId>' #644

Closed
SystematicChaos012 opened this issue Nov 28, 2023 · 3 comments
Closed

Comments

@SystematicChaos012
Copy link

SystematicChaos012 commented Nov 28, 2023

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

@ardalis ardalis added the bug label Nov 28, 2023
@ardalis
Copy link
Owner

ardalis commented Nov 28, 2023

Yeah, this behavior should be supported. I thought I'd verified it was working. Can you provide some sample code that isn't working?

@SystematicChaos012
Copy link
Author

SystematicChaos012 commented Nov 28, 2023

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>

@rameshdabhi
Copy link

@ardalis just wanted to check in if you're planning to knock this off. This one is bugging me as well. Thanks...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants