如何修复因从 EF Core 3 迁移到 EF Core 6 而中断的循环查询
How To Fix Query With Cycles Broken By Migration from EF Core 3 To EF Core 6
从 EF Core 3 迁移到 EF Core 6 后,此查询:
private async Task<Variation[]> GetPizzasInOrder(Uuid[] productsInOrder, CancellationToken ct)
{
return await _clientCheckupsGatewayContext.MetaProducts
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.Include(v => v.MetaProduct)
.ToArrayAsync(ct);
}
开始抛出错误:
System.InvalidOperationException: A tracking query is attempting to project an owned entity without a corresponding owner in its result, but owned entities cannot be tracked without their owner. Either include the owner entity in the result or make the query non-tracking using 'AsNoTracking'.
更改为 'AsNoTracking()' 会出现另一个错误:
private async Task<Variation[]> GetPizzasInOrder(Uuid[] productsInOrder, CancellationToken ct)
{
return await _clientCheckupsGatewayContext.MetaProducts
.AsNoTracking()
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.Include(v => v.MetaProduct)
.ToArrayAsync(ct);
}
System.InvalidOperationException: The Include path
'MetaProduct->Variations' results in a cycle. Cycles are not allowed
in no-tracking queries; either use a tracking query or remove the
cycle.
public class MetaProduct
{
public Uuid Id { get; }
public IReadOnlyList<Variation> Variations => _variations.ToArray();
private List<Variation> _variations = null!;
}
public class Variation
{
public Uuid Id { get; }
public MetaProduct? MetaProduct { get; }
}
关系配置:
private static void MapMetaProducts(ModelBuilder modelBuilder)
{
var tagsConverter = new ValueConverter<string[], string>(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<string[]>(v)
);
var builder = modelBuilder.Entity<MetaProduct>().ToTable("metaproducts");
builder.HasKey(p => p.Id);
builder.Property(p => p.Id).HasColumnName("Id");
builder.OwnsMany(mp => mp.Variations,
vBuilder =>
{
vBuilder.ToTable("metaproducts_variations");
vBuilder.WithOwner(v => v.MetaProduct!);
vBuilder.Property(v => v.Id).HasColumnName("Id");
vBuilder.HasKey("Id");
});
}
如何解决?
在我看来,您要做的只是 return 变体数组,变体 ID 在列表中。
另外,当您的变体被拥有时,它不应该有返回到 MetaProduct 的导航 属性。它实际上应该只在其所有者的上下文中检索。
如果您真的想从 Variation 导航到 MetaProduct,那么您应该重新考虑 Variation 是否真的是一个 'owned' 实体或只是一个相关实体。
问题是您正在从 MetaProducts 输入查询,然后尝试再次包含它。如果您可以取消从 Variation 到 MetaProduct 的导航,那么以下将起作用:
return await _clientCheckupsGatewayContext.MetaProducts
.AsNoTracking()
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.ToArrayAsync(ct);
如果您真的想以其他方式导航,那么您需要将 Variation 提升到相关实体(HasMany 而不是 OwnsMany),然后在您的上下文中将 Variations 公开为 DbSet。
从 EF Core 3 迁移到 EF Core 6 后,此查询:
private async Task<Variation[]> GetPizzasInOrder(Uuid[] productsInOrder, CancellationToken ct)
{
return await _clientCheckupsGatewayContext.MetaProducts
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.Include(v => v.MetaProduct)
.ToArrayAsync(ct);
}
开始抛出错误:
System.InvalidOperationException: A tracking query is attempting to project an owned entity without a corresponding owner in its result, but owned entities cannot be tracked without their owner. Either include the owner entity in the result or make the query non-tracking using 'AsNoTracking'.
更改为 'AsNoTracking()' 会出现另一个错误:
private async Task<Variation[]> GetPizzasInOrder(Uuid[] productsInOrder, CancellationToken ct)
{
return await _clientCheckupsGatewayContext.MetaProducts
.AsNoTracking()
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.Include(v => v.MetaProduct)
.ToArrayAsync(ct);
}
System.InvalidOperationException: The Include path 'MetaProduct->Variations' results in a cycle. Cycles are not allowed in no-tracking queries; either use a tracking query or remove the cycle.
public class MetaProduct
{
public Uuid Id { get; }
public IReadOnlyList<Variation> Variations => _variations.ToArray();
private List<Variation> _variations = null!;
}
public class Variation
{
public Uuid Id { get; }
public MetaProduct? MetaProduct { get; }
}
关系配置:
private static void MapMetaProducts(ModelBuilder modelBuilder)
{
var tagsConverter = new ValueConverter<string[], string>(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<string[]>(v)
);
var builder = modelBuilder.Entity<MetaProduct>().ToTable("metaproducts");
builder.HasKey(p => p.Id);
builder.Property(p => p.Id).HasColumnName("Id");
builder.OwnsMany(mp => mp.Variations,
vBuilder =>
{
vBuilder.ToTable("metaproducts_variations");
vBuilder.WithOwner(v => v.MetaProduct!);
vBuilder.Property(v => v.Id).HasColumnName("Id");
vBuilder.HasKey("Id");
});
}
如何解决?
在我看来,您要做的只是 return 变体数组,变体 ID 在列表中。
另外,当您的变体被拥有时,它不应该有返回到 MetaProduct 的导航 属性。它实际上应该只在其所有者的上下文中检索。
如果您真的想从 Variation 导航到 MetaProduct,那么您应该重新考虑 Variation 是否真的是一个 'owned' 实体或只是一个相关实体。
问题是您正在从 MetaProducts 输入查询,然后尝试再次包含它。如果您可以取消从 Variation 到 MetaProduct 的导航,那么以下将起作用:
return await _clientCheckupsGatewayContext.MetaProducts
.AsNoTracking()
.SelectMany(mp => mp.Variations)
.Where(v => productsInOrder.Contains(v.Id))
.ToArrayAsync(ct);
如果您真的想以其他方式导航,那么您需要将 Variation 提升到相关实体(HasMany 而不是 OwnsMany),然后在您的上下文中将 Variations 公开为 DbSet。