在Entity Framework中左外连接一对一

时间:2016-09-09 16:02:04

标签: c# sql-server entity-framework linq

我遇到与Entity Framework 6.1.3中的一对一关系相关的问题。

我有两节课。

public class Package
{
    public Guid Id { get; set; }
    public string PackageNo { get; set; }
    public double SendingCost{ get; set; }
    public virtual PackageAppointment PackageAppointment { get; set; }
}

public class PackageAppointment 
{
    public DateTime Date { get; set; }
    public virtual Package Package { get; set; }
}

PackageAppointment的地图类是:

public class PackageAppointmentMap
{
    public PackageAppointmentMap()
    {
        Property(x => x.Date).IsRequired();

        HasRequired(x => x.Package).WithOptional(x => x.PackageAppointment).Map(x => x.MapKey("PackageId"));

    }
}

一切正常。我对这段关系没有任何问题。问题是查询数据库。每次我使用上下文查询数据库中的Package实体时,SQL Server探查器都会生成类似这样的内容。

LINQ查询:

var data = await Task.Run(() => GenericService.GetAll().Select(x => x.PackageNo));

SQL Server探查器:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[PackageNo] AS [PackageNo], 
    [Extent1].[SendingCost] AS [SendingCost],    
    [Extent2].[Id] AS [Id1]   
FROM    
    [dbo].[Package] AS [Extent1]
LEFT OUTER JOIN 
    [dbo].[PackageAppointment] AS [Extent2] ON [Extent1].[Id] = [Extent2].[PackageId]

我使用context.Package...执行的每个查询都没有从PackageAppointment(或任何其他一对一关系)检索数据,实体框架生成左外连接。

我的项目中的实际情况是Package实体有5对一关系。由于这个原因,性能非常低。

也许我应该改变关系或其他什么。我真的不知道如何解决这个性能问题。

请告知。

1 个答案:

答案 0 :(得分:3)

您可以将Package和PackageAppointment之间的关系更改为:

public PackageAppointmentMap()
{
    Property(x => x.Date).IsRequired();

    HasOptional(x => x.Package)
        .WithOptionalPrincipal(x => x.PackageAppointment)
        .Map(x => x.MapKey("PackageAppointmentId"));
}

这将使您的Package表包含FK PackageAppontmentId,并且您的查询将不具有此连接。同时更改与Package.HasRequired(x => x.PackageAppointment)的关系也适用(可能原因见下文)

之后访问包:

var query = dbContext.Packages.ToString();

返回:

SELECT
    1 AS [C1],
    [Extent1].[Id] AS [Id],
    [Extent1].[PackageNo] AS [PackageNo],
    [Extent1].[SendingCost] AS [SendingCost],
    [Extent1].[PackageAppointmentId] AS [PackageAppointmentId]
    FROM [dbo].[Packages] AS [Extent1]

另一方面,访问PackageAppointmentss:

var anotherQuery = dbContext.PackageAppointments.ToString();

将导致查询:

SELECT
    1 AS [C1],
    [Extent1].[Id] AS [Id],
    [Extent1].[Date] AS [Date],
    [Extent2].[Id] AS [Id1]
    FROM  [dbo].[PackageAppointments] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Packages] AS [Extent2] ON ([Extent2].[PackageAppointmentId] IS NOT NULL) AND ([Extent1].[Id] = [Extent2].[PackageAppointmentId])

[编辑]

为什么会这样?

看起来这似乎是为了改变跟踪。

情景:

您的PackageAppointment具有必需的Package。这意味着,您的PackageAppointment表格具有外键PackageId,因此当您加载PackageAppointment时,更改跟踪器会获得有关PackageAppointment的所有信息,以跟踪对实体的所有可能更改,并且可以轻松跟踪更改PackageAppointment.Package引用 - 即使您没有真正加载PackageAppointment.PackageId表中的记录,也会在您加载的实体中对Package进行比较,这与...相关联PackageAppointment(延迟加载=关闭)。

另一方面,如果您加载Package,则可以更改属性PackageAppointment以引用另一个PackageAppointment。但要跟踪此更改,您必须以某种方式获得与此程序包关联的PackageAppointment.Id。由于Package表没有从其自身到PackageAppointment表的外键,因此您必须进行联接以获取与此PackageAppointment.Id相关联的Package。换句话说,如果您不进行此加入,则此实体的更改跟踪将无效,并且如果您对其进行任何更改,则会使您加载引用的PackageAppointment,这可能无效。

要证明这一点,请尝试为此特定查询添加.AsNoTracking()并检查返回的查询。

所以代码

var trackingQuery = dbContext.PackageAppointments.ToString();

Console.WriteLine("==========================");
Console.WriteLine("Query with Change Tracker enabled");
Console.WriteLine(trackingQuery);
Console.WriteLine("==========================");
Console.WriteLine("\n\n");

var noTrackingQuery = dbContext.PackageAppointments.AsNoTracking().ToString();

Console.WriteLine("==========================");
Console.WriteLine("Query with Change Tracker disabled");
Console.WriteLine(noTrackingQuery);
Console.WriteLine("==========================");

结果如下:

==========================
Query with Change Tracker enabled
SELECT
    1 AS [C1],
    [Extent1].[Id] AS [Id],
    [Extent1].[Date] AS [Date],
    [Extent2].[Id] AS [Id1]
    FROM  [dbo].[PackageAppointments] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Packages] AS [Extent2] ON ([Extent2].[PackageAppointmentId] IS NOT NULL) AND ([Extent1].[Id] = [Extent2].[PackageAppointmentId])
==========================



==========================
Query with Change Tracker disabled
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Date] AS [Date]
    FROM [dbo].[PackageAppointments] AS [Extent1]
==========================

正如你所看到的,LEFT OUTER JOIN在这里神奇地消失了,这证明这与变更跟踪系统有关。