2

我有一个应用程序成功地利用 LINQ 执行 LEFT OUTER JOINs 几个实例;但是,在一种情况下,它无法按预期工作。

在 LINQPad 中进行测试(使用 LINQ-to_SQL)产生了正确的结果;但是,为了确保我更改为 LINQPad beta 版本 4.42.05 并使用我的应用程序的 DLL 和其 web.config 文件中的 connectionString 成功连接(根据添加连接对话框)。同样,LINQPad 成功返回了正确的结果,并在 TSQL 中清楚地生成了预期的左外连接,但应用程序中的相同代码失败了。

在调试函数时,我得到“对象引用未设置为对象的实例”。错误。请参阅以下代码和相关 TSQL 后的附加说明。请注意,这种关系涉及拥有一家或多家商店的客户以及拥有零个或多个部门的商店。因此,一些返回的记录不会有部门(因此需要左外连接)。

以下代码在 LINQPad 中完美运行:

var model = (from h in SalesOrderHeaders
        join c in Customers on h.CustomerId equals c.CustomerId
        join s in Stores on h.StoreId equals s.StoreId
        join d in Departments on h.DepartmentId equals d.DepartmentId into outer
        from o in outer.DefaultIfEmpty()
        select new 
        {
            OrderId = h.SalesOrderHeaderId,
            OrderDetailId = 1,
            SalesOrderDate = h.SalesOrderDate,
            DeliveryDateTime = h.DeliveryDateTime,
            Customer = c.Customer,
            Store = s.Store,
            Department = (o.Department == null) ? "None" : o.Department,
            FullDescription = "None",
            Qty = 0,
            UoM = "None",
        }).OrderBy (m => m.OrderId);

在应用程序中使用以下代码时,它会失败:

var model = from h in headers
        join c in customers on h.CustomerId equals c.CustomerId
        join s in stores on h.StoreId equals s.StoreId
        join d in departments on h.DepartmentId equals d.DepartmentId into outer
        from o in outer.DefaultIfEmpty()
        select new  SalesOrderGridViewModel
        {
            OrderId = h.SalesOrderHeaderId,
            OrderDetailId = 1,
            SalesOrderDate = h.SalesOrderDate,
            DeliveryDateTime = h.DeliveryDateTime,
            Customer = c.Name,
            Store = s.Name,
            Department = (o.Name == null) ? "None" : o.Name,
            FullDescription = "None",
            Qty = 0,
            UoM = "None",
        };

但是,当我更改应用程序中的代码时,结果的部门字段分配中的布尔值引用 headers 变量 (h.DepartmentId == null) 中的连接元素,如下面的代码所示:

var model = from h in headers
        join c in customers on h.CustomerId equals c.CustomerId
        join s in stores on h.StoreId equals s.StoreId
        join d in departments on h.DepartmentId equals d.DepartmentId into outer
        from o in outer.DefaultIfEmpty()
        select new  SalesOrderGridViewModel
        {
            OrderId = h.SalesOrderHeaderId,
            OrderDetailId = 1,
            SalesOrderDate = h.SalesOrderDate,
            DeliveryDateTime = h.DeliveryDateTime,
            Customer = c.Name,
            Store = s.Name,
            Department = (h.DepartmentId == null) ? "None" : o.Name,
            FullDescription = "None",
            Qty = 0,
            UoM = "None",
        };

返回预期的结果。

有趣的是,最初从原始代码生成的 TSQL 的细微差别:

SELECT [t4].[SalesOrderHeaderId] AS [OrderId], [t4].[SalesOrderDate], 
   [t4].[DeliveryDateTime], [t4].[Customer], [t4].[Store], 
   [t4].[value] AS [Department]
FROM (
    SELECT [t0].[SalesOrderHeaderId], [t0].[SalesOrderDate], 
       [t0].[DeliveryDateTime], [t1].[Customer], [t2].[Store], 
        (CASE 
            WHEN [t3].[Department] IS NOT NULL THEN [t3].[Department]
            ELSE CONVERT(NVarChar(50),@p0)
         END) AS [value]
    FROM [SalesOrderHeaders] AS [t0]
    INNER JOIN [Customers] AS [t1] ON [t0].[CustomerId] = [t1].[CustomerId]
    INNER JOIN [Stores] AS [t2] ON [t0].[StoreId] = ([t2].[StoreId])
    LEFT OUTER JOIN [Departments] AS [t3] 
      ON [t0].[DepartmentId] = ([t3].[DepartmentId])) AS [t4]
ORDER BY [t4].[SalesOrderHeaderId]

从修改后的代码来看,布尔值已更改以测试原始标题表中 DepartmentId 的值([t3].[Department] 与 [t0].[DepartmentId] ),似乎是解决方案:

SELECT [t4].[SalesOrderHeaderId] AS [OrderId], [t4].[SalesOrderDate], 
   [t4].[DeliveryDateTime], [t4].[Customer], [t4].[Store], 
   [t4].[value] AS [Department]
FROM (
    SELECT [t0].[SalesOrderHeaderId], [t0].[SalesOrderDate], 
       [t0].[DeliveryDateTime], [t1].[Customer], [t2].[Store], 
        (CASE 
            WHEN [t0].[DepartmentId] IS NOT NULL THEN [t3].[Department]
            ELSE CONVERT(NVarChar(50),@p0)
         END) AS [value]
    FROM [SalesOrderHeaders] AS [t0]
    INNER JOIN [Customers] AS [t1] ON [t0].[CustomerId] = [t1].[CustomerId]
    INNER JOIN [Stores] AS [t2] ON [t0].[StoreId] = ([t2].[StoreId])
    LEFT OUTER JOIN [Departments] AS [t3] 
      ON [t0].[DepartmentId] = ([t3].[DepartmentId])) AS [t4]
ORDER BY [t4].[SalesOrderHeaderId]

虽然我找到了一种方法来完成这项工作;因为它在 LINQPad 中以两种方式工作,并在分散在我的应用程序中的许多其他 LINQ 查询中成功,所以它在这个位置的原始形式失败让我担心。

最终,当我测试左外连接的返回值时,它似乎在应用程序中失败了。然而,这是许多书籍和文章中记录的做法。所以我的最后一个问题是,有没有人知道为什么会发生这种情况和/或它如何在 LINQPad 中工作(使用应用程序 DLL 并针对同一个数据库)?

4

2 回答 2

2

这是一个如何不编写 LINQ 查询的经典示例 -在 SQL 中思考,然后音译为 LINQ

使用 LINQ,您可以完全避免连接并将查询公式化如下:

from h in SalesOrderHeaders
orderby h.OrderId
select new
{
    OrderId = h.SalesOrderHeaderId,
    OrderDetailId = 1,
    SalesOrderDate = h.SalesOrderDate,
    DeliveryDateTime = h.DeliveryDateTime,
    c.Customer.Customer,
    s.Store.Store,
    Department = (h.Department == null) ? "None" : h.Department.Department,
    FullDescription = "None",
    Qty = 0,
    UoM = "None"
}
于 2012-07-07T00:43:35.767 回答
0

看来 [t0].DepartmentID(来自 SalesOrderHeaders)可以为空。您的 LEFT OUTER JOIN 依赖于这个值。这最终将 [t3].DepartmentID 与 null 进行比较(在某些情况下)。

您可能还需要在 JOIN 中使用 select 中的 CASE 语句。

于 2012-07-06T16:58:28.877 回答