EF如何强制内部联接?

时间:2018-07-23 05:14:50

标签: entity-framework

我有这种ef方法样式

            List<Customer> customers = db.Customers.Where( s => s.IsActive == true )
                .Include( s => s.Communications.Select( a => a.Page ) )
                .Include( s => s.Communications.Select( a => a.Dealership ) )
                .Include( s => s.Communications.Select( a => a.Lead ) )
                .ToList();

它将输出

SELECT 
    ...
    FROM ( SELECT 
        ... 
        CASE WHEN ([Join3].[CommunicationId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[Customer] AS [Extent1]
        LEFT OUTER JOIN  (SELECT ...
            FROM    [dbo].[Communication] AS [Extent2]
            INNER JOIN [dbo].[Page] AS [Extent3] ON [Extent2].[PageId] = [Extent3].[PageId]
            INNER JOIN [dbo].[Dealership] AS [Extent4] ON [Extent2].[DealershipId] = [Extent4].[DealershipId]
            LEFT OUTER JOIN [dbo].[Lead] AS [Extent5] ON [Extent2].[LeadId] = [Extent5].[LeadId] ) AS [Join3] ON [Extent1].[CustomerId] = [Join3].[CustomerId1]
        WHERE 1 = [Extent1].[IsActive]
    )  AS [Project1]
    ORDER BY [Project1].[CustomerId] ASC, [Project1].[C1] ASC

问题是左外连接,因此即使通讯为空,它也会返回客户。在实体类中,我还将Communication和Customer类标记为[Required]。如何强制使用内部联接?

2 个答案:

答案 0 :(得分:1)

这是.Include的正常行为,因为首先,它会请求主数据,即Customers,而与相关/相关项目的存在无关。因此,您应该手动使用inner join来编写查询:

var data = (from customer in db.Customers.Where(x => x.IsActive)
            join comm in db.Communications on customer.CustomerId equals comm.CustomerId1
            join page in db.Pages on comm.PageId equals page.PageId
            join dealer in db.Dealerships on comm.DealershipId equals dealer.DealershipId
            join lead in db.Leads on comm.LeadId equals lead.LeadId
            select new 
            {
                customer.CustomerId,
                comm.PageId,
                comm.DealershipId,
                comm.LeadId,

                //another required fields from any table
            }).ToList();

答案 1 :(得分:0)

执行DBMS查询的较慢部分之一是将结果数据从DBMS传输到本地进程。

您的Communications具有零个或多个Communication。每个Customer都使用一个外键恰好属于一个Customers:一个简单的一对多关系。可能是您设计了多对多关系,结果将相似。

显然,您想获取一些Communications,每个具有其Communications的多个属性。

如果您使用include,则将提取完整的Communications.CustomerId对象并将其转移到本地流程(包括其所有属性),而您只能使用Page / Dealership和Lead。您不会使用Customer.Id,因为您知道它等于var result = myDbcontext.Customers .Where(customer => customer.IsActive) .Select(customer => new { // select only the customer properties you plan to use, for instance: Id = customer.Id, Name = customer.Name, Communications = customer.Communications .Select(communication => new { // again: select only the properties you plan to use: Page = communication.Page, DealerShip = communication.DealerShip, Lead = communication.Lead, }), });

一般规则

  

仅当计划更改/更新获取的项目时才使用“包含”。如果只想读取值,请使用“选择”。仅选择您真正打算使用的属性。

Page = communications.Page.Select(page => new
{
     Header = page.Header,
     Footer = page.Footer,
     Lines = page.Lines,
     ...
},

如果“页面/经销商/潜在客户”是类,请再次使用“选择”仅获取所需的属性。

virtual ICollection<Communication>

通常,实体框架中的一对多关系是使用var result = myDbcontext.Customers .Where(customer => customer.IsActive) // Take only Active customers .GroupJoin(myDbContext.Communications, // GroupJoin with Communications customer => customer.Id, // from every Customer take the Id communication => communication.CustomerId, // from every Communication take the CustomerId (customer, communications) => new // from every Customer with matching communications .Select(customer => new // make one new object { Id = customer.Id, Name = customer.Name, Communications = communications.Select(communication => new { Page = communication.Page, ... } 设计的。如果使用此属性,则实体框架将知道需要内部联接并为您执行该联接。这导致了相当简单的代码

如果由于某种原因您决定不使用ICollection,则必须自己进行加入。代码类似于:

{{1}}