Search code examples
entity-frameworkentity-framework-coreeager-loading

EF Core loading relationships when I don't want it to


I have a service that pulls back entities using EagerLoading. For the most part this works, but I have recently found an issue. I have a field:

public class Field
{
    [Required] public int Id { get; set; }
    [Required, MaxLength(100)] public string CategoryId { get; set; }
    [Required, MaxLength(100)] public string Name { get; set; }
    public FieldDataType DataType { get; set; }
    public bool IsSpecification { get; set; }

    public IList<FieldMap> FieldMaps { get; set; }
}

As you can see, it has a collection of FieldMaps associated with it. The FieldMap looks like this:

public class FieldMap
{
    public int Id { get; set; }
    public int FeedId { get; set; }
    public int FieldId { get; set; }
    [Required, MaxLength(100)] public string Path { get; set; }

    public Field Field { get; set; }
}

Which is mapped up in my DbContext like this:

modelBuilder.Entity<Field>().HasMany(m => m.FieldMaps).WithOne().HasForeignKey(m => m.FieldId);

// Unique constraints
modelBuilder.Entity<Field>().HasIndex(m => new { m.CategoryId, m.Name, m.IsSpecification }).IsUnique();
modelBuilder.Entity<FieldMap>().HasIndex(m => new { m.FeedId, m.Path }).IsUnique();

// Disable cascade delete
modelBuilder.Entity<FieldMap>().HasOne(m => m.Field).WithMany(m => m.FieldMaps).OnDelete(DeleteBehavior.Restrict);

The problem is, when I call the List method, it pulls back all items forever:

enter image description here

My list method is like this:

public IQueryable<T> List(params string[] includes)
{
    IQueryable<T> query = _dbEntitySet;

    return includes == null ? 
        query : 
        includes.Aggregate(query, (current, include) => current.Include(include));
}

And I invoke it like this:

[HttpGet("{id:int}/fields")]
[ProducesResponseType(typeof(List<Field>), StatusCodes.Status200OK)]
public IActionResult ListFieldMaps(int id)
{
    var feeds = _feedService.List();
    var feed = feeds.SingleOrDefault(m => m.Id.Equals(id));
    if (feed == null) return NotFound();

    var fieldMaps = _fieldMapService.List("Field");

    if (fieldMaps.Any(m => m.Field == null)) return BadRequest(Resources.NullFieldError);

    return Ok(fieldMaps.Where(m =>
        m.FeedId.Equals(id) &&
        m.Field.IsSpecification == (feed.Type == FeedType.Specification)).ToList());
}

As you can see, it should only load a list of FieldMaps with one Field, not the rest of the fieldmaps after that.

Can someone help me with this issue?


Solution

  • I found this article:

    https://learn.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading

    and this issue:

    https://github.com/aspnet/EntityFrameworkCore/issues/11564

    and from both of them was able to add this:

    services.AddMvc()
        .AddJsonOptions(options =>
            options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
    

    Which caused my issue to be resolved. Now when I do a request, I get this response:

    [
        {
            "Id": 1,
            "FeedId": 1,
            "FieldId": 10,
            "Path": "aw_product_id",
            "Field": {
                "Id": 10,
                "CategoryId": "cameras",
                "Name": "gtin",
                "DataType": 0,
                "IsSpecification": false,
                "FieldMaps": []
            }
        }
    ]