具有null值的LINQ to SQL

本文关键字:to SQL LINQ 值的 null 具有 | 更新日期: 2023-09-27 18:22:23

有人能帮我弄清楚吗?

以下代码运行良好,并进入if状态

foreach (var m in msg)
{
    if (string.IsNullOrEmpty(m.PhoneNumber))
    {
        m.PhoneNumber = (from c in db.Customers
                            where c.CustomerID == m.CustomerID
                            select c.PhoneNumber).Single();
    }
}

然而,在以下代码中,phoneNumber从未设置

foreach (var m in msg.Where(z => (z.PhoneNumber == null || z.PhoneNumber == "")))
{
     m.PhoneNumber = (from c in db.Customers
                      where c.CustomerID == m.CustomerID
                      select c.PhoneNumber).Single();
}

我认为这是因为上面的代码实际上是对表达式求值的,而下面的代码是不求值的。如果是这种情况,那么如何在未评估的LINQ查询中检查null?

编辑为了避免混淆,以下是在两种情况下如何流行消息

    var msg = from m in db.Messages
              where (m.StatusID == (int)MessageStatus.Submitted && m.MessageBoxTypeID == (int)MessageBoxType.Outbox)
              select m;

具有null值的LINQ to SQL

我对这个有点困惑,但我有一个疯狂的猜测。如果msg序列是转换为SQL查询的IQueryable<T>,那么这两个片段的行为可能会有所不同。假设你有:

var msg = 
    from m in dataContext.MyTable
    select m;

您的第一个代码段将导致枚举整个msg序列,从而向数据库发出未经筛选的SELECT…FROM命令,并获取表中的所有行。

foreach (var m in msg)

另一方面,您的第二个代码段在枚举序列之前对其应用过滤器。因此,向数据库发出的命令是SELECT…FROM…WHERE

foreach (var m in msg.Where(z => (z.PhoneNumber == null || z.PhoneNumber == "")))

在各种情况下,.NET中应用的筛选器的行为与其转换为Transact-SQL的行为不同。首先,区分大小写。在您的情况下,我假设不匹配是由PhoneNumber由空白组成的条目引起的,因为这些条目可能与SQL Server中的空字符串相匹配。

为了测试这种可能性,请检查如果您将第二个代码段更改为:会发生什么

foreach (var m in msg.ToList().Where(z => (z.PhoneNumber == null || z.PhoneNumber == "")))

编辑:您的问题可能是在后续访问期间(当您检查是否设置了PhoneNumber时)再次执行查询。

如果执行:

foreach (var m in msg.Where(z => (z.PhoneNumber == null || z.PhoneNumber == "")))
{
    m.PhoneNumber = …
}
bool stillHasNulls = msg.Any(z => z.PhoneNumber == null || z.PhoneNumber == "");

您会发现stillHasNulls可能仍然计算为true,因为当您重新计算msg序列时(在上面的情况下,当您执行msg.Any时,它会向数据库发出EXISTS命令),您对m.PhoneNumber的分配将丢失。

为了保留m.PhoneNumber分配,您需要将它们持久化到数据库中(如果这是您想要的),或者确保每次访问相同的序列元素。一种方法是使用ToList将序列预填充为集合。

msg = msg.Where(z => (z.PhoneNumber == null || z.PhoneNumber == "")).ToList();
foreach (var m in msg)
{
    m.PhoneNumber = …
}

在上面的代码中,过滤器仍然作为SELECT…FROM…WHERE发布到数据库,但结果被急切地评估,然后作为列表存储在msg中。msg上的任何后续查询都将根据预先填充的内存集合(其中包含您分配给其元素的任何新值)进行评估。