Reputation: 13377
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:
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?
Upvotes: 1
Views: 692
Reputation: 13377
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": []
}
}
]
Upvotes: 2
Reputation: 255
My guess is that you're seeing the same instances over and over again thanks to EF navigation property fix-up.
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
Try setting Object Id in Visual Studio (add the variable to Watch, right click on it and select "Make Object ID"). This will create a unique id for each instance and will tell you if you're loading different entities.
Upvotes: 2