Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Handling Generic Entities in Entity Framework's Change Tracker

I’m implementing a Domain-Driven Design (DDD) architecture and want to leverage strongly typed IDs for my entities. My base AggregateRoot class looks like this:

public abstract class AggregateRoot<TId>
    : Entity<TId>
    where TId : struct
{
    private readonly List<IDomainEvent> _domainEvents = [];

    // ...
}

Here’s an example entity:

public readonly record struct AuctionId(Guid Value);

public class Auction
    : AggregateRoot<AuctionId>
{
    // ...
}

I have a domain event dispatcher interceptor (PublishDomainEventsInterceptor) which I want to work generically with all aggregates:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

internal sealed class PublishDomainEventsInterceptor 
    : SaveChangesInterceptor 
{
    // ...

    public async Task PublishDomainEvents(
        DbContext? context)
    {
        // ...

        // Problem: Need a generic way to handle strongly-typed IDs here
        var entities = context.ChangeTracker
            .Entries<AggregateRoot>() // Issue: Requires type argument
            .Where(e => e.Entity.DomainEvents.Any())
            .Select(e => e.Entity); 

        // ...
    }
}

How can I modify my PublishDomainEvents() method or related approach to make it work with AggregateRoot<TId> without knowing the specific TId type in advance? I want to maintain type safety and the benefits of strongly typed IDs.

Thank you in advance for your help, cheers! 🙂

>Solution :

Since your interceptor code does not actually need to use the strongly-typed Id the easiest option would be to introduce a non-generic interface for AggregateRoot<TId> to implement and use it:

public interface IAggregateRoot
{
   public IEnumerable<IDomainEvent> DomainEvents { get; }
}

public abstract class AggregateRoot<TId>
    : Entity<TId>, IAggregateRoot
    where TId : struct
{
    // ...
}

And usage:

var entities = context.ChangeTracker
    .Entries<IAggregateRoot>() 
    .Where(e => e.Entity.DomainEvents.Any())
    .Select(e => e.Entity); 
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading