.NET Linq 查询一对多关系
.NET Linq query for one-to-many relationsiops
下面的示例显示了客户和发票之间的简单一对多关系。 LINQ 查询检索所有发票和相关客户。我只是想对查询的效率发表意见,因为有一个递归,一张发票有一个客户,客户有很多发票,每个发票都有一个客户,等等。
另外,在只读场景中我可以使用 AsNoTracking() 吗?
public class Customer
{
public int CustomerID { get; set; }
public string CustomerName { get; set; }
[ForeignKey("CustomerID")]
[NotMapped]
public virtual IList<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int InvoiceID { get; set; }
public int CustomerID { get; set; }
[ForeignKey("CustomerID")]
public Customer Customer { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//........
builder.Entity<Invoice>().ToTable("Invoices")
.HasOne(s => s.Customer).WithMany(c => c.Invoices)
.OnDelete(DeleteBehavior.Restrict);
}
public class SomeClass
{
//....
List<Invoice> invList = await context.Invoices.Include(x => x.Customer).toListAsync();
}
我不认为存在递归 本身 但当你查看它时可能存在,只是因为 EF 将 link 相关对象放在两个方向
查询将转换为简单的内容,例如
SELECT * FROM Invoices JOIN Customers ON ...
对于拥有多个发票的客户,这会导致在结果集中重复客户信息,但由于 EF 正在将它们映射到对象(在枚举结果时创建 Invoice 和 Customer 的实例),如果遇到它之前看到的客户数据将使用它已经知道的客户实体。
这意味着如果您查看调试器并开始在本地 window 中展开 invList
,您将得到例如发票 ID 1,它是内存地址 0x01 的对象,它有一个 客户 ID 1,它是内存地址 0x82 的对象,你'你会看到这个客户有 20 张发票。如果您在该 20 列表中找到 发票 ID 1,它将是内存地址 0x01 处的对象。如果你展开它并查看它的 Customer,它将是内存地址 0x82 处的 Customer,它有一个包含 20 个 inoices 的列表......等等......你可以永远扩展它们,因为你只是循环回去在相同的两个对象实例之间来回,这 20 张发票只有一个客户。它不会递归地生成更多数据。
在数据加载方面您必须更加小心的地方是 AsNoTracking
;如果您完全关闭跟踪,那么数据库客户数据中的重复将导致始终生成新的 Customer 实例,即使它们与之前看到的实例具有相同的键。在这种情况下,在本地 window 中扩展对象图会看到 Invoice 1,mem 地址 0x03 具有 Customer 1,mem 地址 0x85谁有一张发票,即发票1,内存地址0x03。然后您可以展开 Invoice 2,mem 地址 0x05 并查看 Customer 1,mem 地址 0x89 - 相同的客户详细信息但不同的对象实例(0x85 vx 0x89) 之前
实际上,人类买家确实有 20 张与之关联的发票,但在您的对象图中,同一客户数据有 20 个不同的 Customer 实例,它们都有一个发票集合,每个发票带有一张发票
如果您想避免这种实例爆炸但不跟踪只读方案的数据更改,您可以查看 AsNoTrackingWithIdentityResolution
以获得“一个客户有 20 张发票的一张发票...”形状图表,但不跟踪 old/new 用于更新查询目的的值
下面的示例显示了客户和发票之间的简单一对多关系。 LINQ 查询检索所有发票和相关客户。我只是想对查询的效率发表意见,因为有一个递归,一张发票有一个客户,客户有很多发票,每个发票都有一个客户,等等。 另外,在只读场景中我可以使用 AsNoTracking() 吗?
public class Customer
{
public int CustomerID { get; set; }
public string CustomerName { get; set; }
[ForeignKey("CustomerID")]
[NotMapped]
public virtual IList<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int InvoiceID { get; set; }
public int CustomerID { get; set; }
[ForeignKey("CustomerID")]
public Customer Customer { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//........
builder.Entity<Invoice>().ToTable("Invoices")
.HasOne(s => s.Customer).WithMany(c => c.Invoices)
.OnDelete(DeleteBehavior.Restrict);
}
public class SomeClass
{
//....
List<Invoice> invList = await context.Invoices.Include(x => x.Customer).toListAsync();
}
我不认为存在递归 本身 但当你查看它时可能存在,只是因为 EF 将 link 相关对象放在两个方向
查询将转换为简单的内容,例如
SELECT * FROM Invoices JOIN Customers ON ...
对于拥有多个发票的客户,这会导致在结果集中重复客户信息,但由于 EF 正在将它们映射到对象(在枚举结果时创建 Invoice 和 Customer 的实例),如果遇到它之前看到的客户数据将使用它已经知道的客户实体。
这意味着如果您查看调试器并开始在本地 window 中展开 invList
,您将得到例如发票 ID 1,它是内存地址 0x01 的对象,它有一个 客户 ID 1,它是内存地址 0x82 的对象,你'你会看到这个客户有 20 张发票。如果您在该 20 列表中找到 发票 ID 1,它将是内存地址 0x01 处的对象。如果你展开它并查看它的 Customer,它将是内存地址 0x82 处的 Customer,它有一个包含 20 个 inoices 的列表......等等......你可以永远扩展它们,因为你只是循环回去在相同的两个对象实例之间来回,这 20 张发票只有一个客户。它不会递归地生成更多数据。
在数据加载方面您必须更加小心的地方是 AsNoTracking
;如果您完全关闭跟踪,那么数据库客户数据中的重复将导致始终生成新的 Customer 实例,即使它们与之前看到的实例具有相同的键。在这种情况下,在本地 window 中扩展对象图会看到 Invoice 1,mem 地址 0x03 具有 Customer 1,mem 地址 0x85谁有一张发票,即发票1,内存地址0x03。然后您可以展开 Invoice 2,mem 地址 0x05 并查看 Customer 1,mem 地址 0x89 - 相同的客户详细信息但不同的对象实例(0x85 vx 0x89) 之前
实际上,人类买家确实有 20 张与之关联的发票,但在您的对象图中,同一客户数据有 20 个不同的 Customer 实例,它们都有一个发票集合,每个发票带有一张发票
如果您想避免这种实例爆炸但不跟踪只读方案的数据更改,您可以查看 AsNoTrackingWithIdentityResolution
以获得“一个客户有 20 张发票的一张发票...”形状图表,但不跟踪 old/new 用于更新查询目的的值