Unexpected Circular References in Entity Framework Core When Retrieving Data with .Include()

29 views Asked by At

I am working on a repository method in Entity Framework Core to retrieve all applications along with their associated application tokens. My method looks like this:

public async Task<IEnumerable<ApplicationDTO>> GetAllApplications(string? expand = null)
{
    List<Application> applications = await context.Applications.Include(a => a.ApplicationTokens).ToListAsync();

    return mapper.Map<IEnumerable<ApplicationDTO>>(applications);
}

I expect the result to be a list of Application objects, each containing a list of ApplicationToken objects. Importantly, I do not expect the ApplicationToken objects to include back references to Application objects since I did not specify .ThenInclude(t => t.Application) in my query. Here are the entity definitions:

public class Application
{
    public int Id { get; set; }

    public required string Name { get; set; }

    public required string Description { get; set; }

    public DateTime CreatedAt { get; set; }

    public required int CreatedById { get; set; }

    public User? CreatedBy { get; set; }

    public ICollection<ApplicationToken>? ApplicationTokens { get; set; }

    public ICollection<FeatureGate>? FeatureGates { get; set; }
}

public class ApplicationToken
{
    public int Id { get; set; }

    public required int ApplicationId { get; set; }

    public required string Token { get; set; }

    public required bool Enabled { get; set; }

    public required DateTime CreatedAt { get; set; }

    public required int CreatedById { get; set; }

    public required DateTime ExpiresAt { get; set; }

    public Application? Application { get; set; }

    public User? CreatedBy { get; set; }
}

Before mapping to ApplicationDTO, I observed circular references between Application and ApplicationToken entities, which I aimed to avoid. My expectation was that the application tokens would be populated within each application without back references to the application itself, preventing any circular references. I have no lazy loading or virtual collections that could automatically populate these references.

What am I missing here?

1

There are 1 answers

0
Steve Py On

If you are using Automapper, use ProjectTo<TDto>(). Other mapping libraries have similar methods for working with EF's IQueryable rather than fetching entities and relations then calling Map<TDto>(). This will avoid potential cyclic references.

Just make sure that you aren't trying to mix DTOs and entities, for instance creating an ApplicationDTO which contains ApplicationToken entities, because those entities will have references to Application entities, with references to Tokens etc. If you try to serialize the ApplicationDTO it will serialize the contained entities leading to issues with the cyclic references. If you want tokens in the ApplicationDTO, create ApplicationTokenDTOs and configure the mapper to convert those entities to DTOs as well.