c# - linq select 语句与直接访问

标签 c# linq entity-framework

我有两个类

class Supervisor
{
    public int SupervisorID { get; set; }
    public string Name { get; set; }

    public virtual List<Trunk> Trunks { get; set; }

    //constructor + other code etc...//
}

class Trunk
{
    public int TrunkID { get; set; }
    public string Name { get; set; }

    public int SupervisorID { get; set; }

    //constructor + other code etc...//
}

这些类所连接的数据库充满了数据,每个 Supervisor 记录至少有一个 Trunk 记录。

我的问题是这样的: 为什么此代码可以正确工作并打印出每个主管的中继名称:

//Works
var s = from x in db.Supervisors
        select x.Trunks.FirstOrDefault().Name;
foreach (var name in s)
    Console.WriteLine(name);

但是这段代码没有并抛出 ArgumentNullException?

//Doesn't Work: ArgumentNullException
foreach (var supervisor in db.Supervisors)
    Console.WriteLine(supervisor.Trunks.FirstOrDefault().Name);

此外,此代码运行良好:

foreach (var supervisor in db.Supervisors)
    Console.WriteLine(supervisor.Name);

因此,只有在访问supervisor.Trunks时,我才会在第二个代码块中得到null。

主管表的屏幕截图:/image/H8Ha6.jpg

这两个代码块做的事情不完全相同吗?

最佳答案

以下代码:

var s = from x in db.Supervisors
    select x.Trunks.FirstOrDefault().Name;

实际上并不作为 C# 代码执行。这是编译一系列 Expression 对象,这些对象定义了源代码的样子。它不被编译成可执行字节。然后,这些Expression对象被传递到查询提供程序,该查询提供程序将C#源代码(或至少是等效的)转换为SQL代码,并针对数据库运行它。该查询提供程序可以看到您正在访问对象的 Trunks 属性,它知道将其转换为 Join。它发现您正在访问该表的 Name 属性,因此这是它选择的列,等等。

当你写下以下内容时:

foreach (var supervisor in db.Supervisors)

除了拉回整个 Supervisors 表之外,不会向数据库发送任何内容。它不会构建任何Expression对象来定义查询可能是什么。没有针对 Trunks 表的 Join。中继名称不会在选择器中提取。

此相关表未立即加载。如果您稍后可能想使用它,它不会使用所有 Trunks 信息填充 supervisor 对象。当对象有很多关系时,这样做的成本太高了。由于它根本没有填充,因此即使数据库中实际上存在 Trunk 对象,它也将为 null

告诉查询提供者,“嘿,我需要这些主管的 Trunks 表中的信息。”您使用 Inlude 方法:

foreach (var supervisor in db.Supervisors.Include(s => s.Trunks))

当然,这仍然不如您的第一个解决方案,因为现在您要从两个表中提取所有字段,而不仅仅是中继名称。这会浪费大量的网络流量。

还有一个选择是enable lazy initialization的相关实体。这意味着,当您尝试访问查询中未填充的相关实体时,它会再次往返数据库以在您需要时获取该信息。在某些情况下这可能没问题,但在这里您知道您将需要该信息。您真正想要避免的是为每个主管执行额外的数据库往返。因此,这将导致您的程序正常运行,而不是崩溃,但代价是比任何其他替代方案慢得多

关于c# - linq select 语句与直接访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23500121/

相关文章:

c# - UWP - 获取 IMSI 和 IMEI

c# - Linq:获取丢失的记录

c# - EF Core 7 - 仅包含未删除的项目

c# - 从 DbConnection 获取 SqlConnection

c# - 我如何从文本文件中获取值并选择具有该值的复选框?

c# - 没有堆栈跟踪信息的 Mono SIGSEGV 崩溃

c# - 我如何在 C# 中引用另一个类的方法?

linq - 为什么选择linq to EF对输出列表进行排序的方法?

entity-framework - Entity Framework 6 ToString()、格式化(DateTime 格式)、查询拦截

c# - 是否有可能在dapper中执行代码优先方法