当我不希望 EF Core 加载关系时
EF Core loading relationships when I don't want it to
我有一项使用 EagerLoading 拉回实体的服务。
在大多数情况下,这是可行的,但我最近发现了一个问题。
我有一个字段:
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; }
}
如您所见,它有一个与之关联的 FieldMaps 集合。
FieldMap 看起来像这样:
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; }
}
在我的 DbContext 中是这样映射的:
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);
问题是,当我调用 List 方法时,它永远拉回所有项目:
我的列表方法是这样的:
public IQueryable<T> List(params string[] includes)
{
IQueryable<T> query = _dbEntitySet;
return includes == null ?
query :
includes.Aggregate(query, (current, include) => current.Include(include));
}
我这样调用它:
[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());
}
如您所见,它应该只加载一个 FieldMaps 列表和一个 Field,而不是之后的其余 fieldmaps .
有人可以帮我解决这个问题吗?
我的猜测是,由于 EF 导航 属性 修复,您会一遍又一遍地看到相同的实例。
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.
尝试在 Visual Studio 中设置对象 ID(将变量添加到 Watch,右键单击它并 select "Make Object ID")。这将为每个实例创建一个唯一的 ID,并会告诉您是否正在加载不同的实体。
我找到了这篇文章:
https://docs.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading
和这个问题:
https://github.com/aspnet/EntityFrameworkCore/issues/11564
并且他们都能够添加这个:
services.AddMvc()
.AddJsonOptions(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
这导致我的问题得以解决。
现在,当我发出请求时,我得到了这样的回应:
[
{
"Id": 1,
"FeedId": 1,
"FieldId": 10,
"Path": "aw_product_id",
"Field": {
"Id": 10,
"CategoryId": "cameras",
"Name": "gtin",
"DataType": 0,
"IsSpecification": false,
"FieldMaps": []
}
}
]
我有一项使用 EagerLoading 拉回实体的服务。 在大多数情况下,这是可行的,但我最近发现了一个问题。 我有一个字段:
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; }
}
如您所见,它有一个与之关联的 FieldMaps 集合。 FieldMap 看起来像这样:
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; }
}
在我的 DbContext 中是这样映射的:
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);
问题是,当我调用 List 方法时,它永远拉回所有项目:
我的列表方法是这样的:
public IQueryable<T> List(params string[] includes)
{
IQueryable<T> query = _dbEntitySet;
return includes == null ?
query :
includes.Aggregate(query, (current, include) => current.Include(include));
}
我这样调用它:
[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());
}
如您所见,它应该只加载一个 FieldMaps 列表和一个 Field,而不是之后的其余 fieldmaps .
有人可以帮我解决这个问题吗?
我的猜测是,由于 EF 导航 属性 修复,您会一遍又一遍地看到相同的实例。
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.
尝试在 Visual Studio 中设置对象 ID(将变量添加到 Watch,右键单击它并 select "Make Object ID")。这将为每个实例创建一个唯一的 ID,并会告诉您是否正在加载不同的实体。
我找到了这篇文章:
https://docs.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading
和这个问题:
https://github.com/aspnet/EntityFrameworkCore/issues/11564
并且他们都能够添加这个:
services.AddMvc()
.AddJsonOptions(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
这导致我的问题得以解决。 现在,当我发出请求时,我得到了这样的回应:
[
{
"Id": 1,
"FeedId": 1,
"FieldId": 10,
"Path": "aw_product_id",
"Field": {
"Id": 10,
"CategoryId": "cameras",
"Name": "gtin",
"DataType": 0,
"IsSpecification": false,
"FieldMaps": []
}
}
]