【问题标题】:EF Lambda Expression How to Access Data from Multiple TablesEF Lambda 表达式如何访问多个表中的数据
【发布时间】:2020-10-29 13:33:06
【问题描述】:

我需要一些帮助来编写和理解 EF Lambda 表达式。 如果我收到一个 productID 作为参数 (selectedID),我将如何访问与该 productID 相关的客户名称、invoiceID 和 PriceTotal?

这是我尝试获取 invoiceID 的方法:

db.InvoiceLines.Where(z => z.ProductID == selectedID).Select(x => x.InvoiceID).ToList();

但我不知道如何访问其余数据。

任何帮助和解释将不胜感激!

【问题讨论】:

    标签: entity-framework linq lambda


    【解决方案1】:

    所以每个Customer 有零个或多个Invoices,每个Invoice 是恰好一个客户的发票,即外键CustomerId 所指的客户:一个简单的一对多关系。

    Invoices 和 InvoiceLines 之间同样存在一对多的关系:每个 Invoice 都有零个或多个 InvoiceLines,每个 InvoiceLine 都属于一个 Invoice,使用外键。

    产品 - InvoiceLines:也是使用外键的一对多关系。

    如果您关注entity-framework coding conventions,,您将获得类似于以下的课程:

    class Customer
    {
        public int Id {get; set;}
        public string Name {get; set;}
        ...
    
        // Every Customer has zero or more Invoices (one-to-many)
        public virtual ICollection<Invoice> Invoices {get; set;}
    }
    
    class Invoice
    {
        public int Id {get; set;}
        ...
    
        // Every invoice belongs to exactly one Customer, using foreign key:
        public int CustomerId {get; set;}
        public virtual Customer Customer {get; set;}
    
        // Every Invoice has zero or more InvoiceLiness (one-to-many)
        public virtual ICollection<InvoiceLine> InvoiceLiness {get; set;}
    }
    

    在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系。

    外键 CustomerId 是 Invoices 表中的一个真实列。因此它是非虚拟的。可以在虚拟属性 Customer 上看到 Invoice 与 Customer 有关系。

    在实体框架中,表之间的关系类型(一对一、一对多、多对多)可以通过virtual ICollection&lt;...&gt;virtual ... 看到

    表和集合使用复数名词来标识;表的单行或集合中的一项由单数名词标识。稍后当我们讨论 lambda 时,您会发现这使得解释 lambda 变得更容易。

    class InvoiceLIne
    {
        public int Id {get; set;}
        ...
    
        // Every InvoiceLine belongs to exactly one Invoice, using foreign key:
        public int InvoiceId{get; set;}
        public virtual Invoice Invoice {get; set;}
    
        // Every InvoiceLine has exacaly one Product, using foreign key:
        public int ProductId {get; set;}
        public virtual Product Product {get; set;}
    }
    
    class Product
    {
        public int Id {get; set;}
        public string Name {get; set;}
        ...
    
        // Every Product is use in has zero or more InvoiceLines (one-to-many)
        public virtual ICollection<InvoiceLine> InvoiceLInes {get; set;}
    }
    

    最后是 DbContext

    class OrderingDbContext : DbContext
    {
        public DbSet<Customer> Customers {get; set;}
        public DbSet<Invoice> Invoices {get; set;}
        public DbSet<InvoiceLIne> InvoiceLines {get; set;}
        public DbSet<Product> Products {get; set;}
    }
    

    实际上,这就是实体框架检测表、表中的列以及表之间的关系所需要知道的全部内容。不需要属性或流利的 api。只有当你想要不同于默认标识符的标识符、表名或列名,或者你想在关系中定义一些特殊的东西时,你才需要流畅的 API。

    回到你的问题

    如果我收到一个 productID 作为参数 (selectedID),我将如何访问与该 productID 相关的客户名称、invoiceID 和 PriceTotal?

    首先,如果您有一个 productId,可能有多个客户使用此 productId 购买了该产品,因此有多个发票和几个 priceTotals。因此,您不能说出客户名称、发票 ID。

    要求 如果我有 productId,请提供购买此产品的所有客户的姓名,以及声明他们购买此产品的 InvoiceId,以及他们购买此产品的发票总价。

    请注意,客户可能在不同的订单中多次购买了该产品,因此他们可以有多个发票。

    如果您遵循我上面写的约定,则解决方案相当简单:

    int productId = ...
    var customersWhoBoughThisProduct = dbContext.Customers
    
        // I only want Customers who have at least one Invoice that has at least one
        // invoiceLine for this product
        .Where(customer => customer.Invoices
            .SelectMany(invoice => invoiceLines, invoice => invoice.ProductId)
            .Contains(productId))
    
        // from the customers who bough this product, select several properties:
        .Select(customer => new
        {
            // Select only the Customer properties that you plan to use
            Id = customer.Id,
            Name = customer.Name,
            ...
    
            // You want only the invoices that have this product as one of the InvoiceLines
            Invoices = customer.Invoices
                .Where(invoice => invoice.InvoiceLines
                    .Any(invoiceLine => invoiceLine.ProductId == productId)
                .Select(invoice => new
                {
                    // select the invoice properties that you plan to use, for example:
                    Id = invoice.Id,
                    OrderDate = invoice.OrderDate,
                    PriceTotal = invoice.InvoiceLines
                                        .Select(invoiceLine => invoiceLine.Price)
                                        .Sum(),
                })
                .ToList(),
            });
    

    你在这里看到了几个 Lambda 表达式。因为我对标识符的使用非常严格,所以它们应该不会太难:

    dbContext.Customers.Where(...)
        .Select(customer => new
        {
            // Select only the Customer properties that you plan to use
            Id = customer.Id,
            Name = customer.Name,
            ...
            
    

    在 Where 之后,我得到了一个客户序列,并且从这个序列中的每个客户,我都创建了一个新对象,其属性 Id 具有 customerId 的值,属性 Name 具有值客户姓名。

    这个客户对象,也有一个属性 Invoices:

    Invoices = customer.Invoices
                .Where(...)
                .Select(invoice => new{...}
    

    您不想要该客户的所有发票,您只想要发票,其中至少一个发票行具有 ProductId 产品的外键:

    .Where(invoice => invoice.InvoiceLines.Any(invoiceLine => invoiceLine.ProductId == productId)
    

    这表示:只保留那些至少有一个 InvoiceLIne 属性 ProductId 等于 productId 的发票。我们想要哪种发票。

     PriceTotal = invoice.InvoiceLines
                         .Select(invoiceLine => invoiceLine.Price)
                         .Sum(),
    

    为了计算包含该产品的发票的 PriceTotal,我们取该发票中每个 InvoiceLine 的价格,并将所有这些价格相加。

    最困难的一个:客户在哪里:

    .Where(customer => customer.Invoices
        .SelectMany(invoice => invoiceLines)
        .Any(invoiceLine => invoiceLine.ProductId == productId))
    

    所以输入是一个客户序列。我们只保留那些在该客户的 Invoices 序列中至少有一个 invoiceLine 且 productId 等于 productId 的客户。

    如果您有一个序列,其中序列中的每个元素都有一个子序列,并且您想将所有这些子序列作为一个序列进行检查,请使用 SelectMany。

    因此,如果您有学校,并且每个学校都有学生,并且您希望按顺序查看所有学校的所有学生,用户:schools.SelectMany(school =&gt; school.Students) 我对 Invoices 和 InvoiceLines 做了同样的事情,以获取所有 Invoices 的所有 InvoiceLines该客户的:invoices.SelectMany(invoice =&gt; invoice.InvoiceLines)

    对了,你有没有看到,因为我对单复数名词都非常严格,所以 lambda 表达式中的标识符很容易理解它们代表什么?

    希望这对理解 lambda 表达式有所帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 2016-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多