.NET Linq 查询一对多关系

Posted

技术标签:

【中文标题】.NET Linq 查询一对多关系【英文标题】:.NET Linq query for one-to-many relationsiops 【发布时间】:2021-10-07 08:47:48 【问题描述】:

下面的示例显示了客户和发票之间的简单一对多关系。 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();


【问题讨论】:

如果代码足够快,则无需尝试替代方案。所以你需要测量。尽可能首先编写代码以确保清晰和易于维护。 是的,用于只读方案AsNoTracking 可以使用,因为您不希望 EF 跟踪它。 【参考方案1】:

我不认为存在递归本身,但当您查看它时可能存在,因为 EF 会在两个方向上链接相关对象

查询将转换为类似的简单内容

SELECT * FROM Invoices JOIN Customers ON ...

如果遇到客户数据,这会导致结果集中的客户信息重复,因为 EF 将它们映射到对象(在枚举结果时创建 Invoice 和 Customer 的实例)的客户它将使用它已经知道的客户实体。

这意味着如果您查看调试器并开始在 Locals 窗口中展开 invList,您将获得例如发票 ID 1,它是内存地址 0x01 的对象,客户 ID 1 是内存地址 0x82 的对象,你会看到这个客户有20 张发票。如果您在 20 个列表中找到 Invoice ID 1,它将是内存地址 0x01 处的对象。如果你扩展它并查看它的客户,它将是内存地址 0x82 的客户,它有一个包含 20 个 inoice 的列表......等等......你可以永远扩展它们,因为你只是循环回来在相同的两个对象实例之间来回移动,而这 20 张发票只有一个客户。它不会递归地生成更多数据。

在数据加载方面需要更加小心的地方是AsNoTracking;如果您完全关闭跟踪,那么数据库客户数据中的重复将导致始终生成新的客户实例,即使它们与以前看到的实例具有相同的键。在这种情况下,在本地窗口中展开对象图会看到 Invoice 1,mem address 0x03Customer 1,mem address 0x85,他有一张发票,即 发票 1,mem 地址 0x03。然后,您可以展开 Invoice 2,mem address 0x05 并查看 Customer 1,mem address 0x89 - 与之前相同的客户详细信息但不同的对象实例 (0x85 vx 0x89) p>

实际上,人类买家确实有 20 张与之关联的发票,但在您的对象图中,有 20 个相同客户数据的不同客户实例,它们都有一个发票集合,每个都有一张发票

如果您想避免这种实例爆炸但不跟踪只读方案的数据更改,您可以查看AsNoTrackingWithIdentityResolution 以获取“一张发票与一位客户和 20 张发票......”形状图,但不是为 UPDATE 查询目的跟踪旧/新值

【讨论】:

非常感谢您的详细回复!有道理。

以上是关于.NET Linq 查询一对多关系的主要内容,如果未能解决你的问题,请参考以下文章

Linq 语法

LINQ学习之旅

linq语法大全(转集)

LINQ to SQL语句之Join

.NET深入解析LINQ框架(五:IQueryableIQueryProvider接口详解)

LINQ 如何在一对多连接中指定选择某些列