У меня есть сайт, созданный с использованием Asp.net и LinqToSql для доступа к данным. В определенном разделе сайта LinqToSql создает запрос, который выглядит следующим образом (с моего компьютера разработчика):
select ...
from table1
left outer join table2 on table1 where ...
left outer join table3 on table2 where ...
Поскольку связь между таблицей 2 и таблицей 1 не всегда существует, в этой ситуации уместно левое внешнее соединение. А поскольку связь между таблицей 3 и таблицей 1 проходит через таблицу 2, ей также необходимо левое внешнее соединение. Этот sql возвращает правильный набор записей.
Я просто поместил код на сервер. При выполнении идентичного кода в том же сценарии LinqToSql создает следующий запрос:
select ...
from table1
left outer join table2 on table1 where ...
join table3 on table2 where ...
По какой-то причине он выполняет соединение между table2 и table3 как внутреннее, а не внешнее соединение. В результате в запросе возвращается ноль записей.
Как на компьютере разработчика, так и на сервере используется .Net 3.5 SP1. Компьютер разработчика - Vista64, Сервер - Windows Server 2003 SP2. Мой коллега, использующий Windows XP PRO, также подтвердил то же правильное поведение на своем компьютере разработчика.
Может кто-нибудь придумать причину, по которой сервер будет создавать разные sql? Как я могу это исправить? Кажется, что-то связано с тем, как Linq и .Net работают на сервере. Однако я не могу придумать способ подтвердить и исправить это.
Код Linq (я включаю только те части, которые относятся к разделу, где изменен sql):
from Import_Table t in db.Import_Tables
select new {
CheckedOutUser = (!t.IsCheckedOut) ? "--" : t.Import_CheckoutHistory.System_User.FirstName + " " + t.Import_CheckoutHistory.System_User.LastName,
CheckedOutUserID = (!t.IsCheckedOut) ? 0 : t.Import_CheckoutHistory.System_UserID};
В контексте приведенного выше описания table1 = Import_Table, table2 = Import_CheckoutHistory, table3 = System_User. Если я закомментирую строку, начинающуюся с «CheckedOutUser = ...», то она будет работать на сервере - так что это определенно виновник.
Фактическое возвращение sql:
SELECT
(CASE WHEN NOT ([t0].[IsCheckedOut] = 1) THEN CONVERT(NVarChar(401),'--') ELSE ([t2].[FirstName] + ' ') + [t2].[LastName] END) AS [CheckedOutUser],
(CASE WHEN NOT ([t0].[IsCheckedOut] = 1) THEN 0 ELSE [t1].[system_UserID] END) AS [CheckedOutUserID]
FROM [dbo].[import_Table] AS [t0]
LEFT OUTER JOIN [dbo].[import_CheckoutHistory] AS [t1] ON [t1].[import_CheckoutHistoryID] = [t0].[import_CheckoutHistoryID]
LEFT OUTER/INNER JOIN [dbo].[system_User] AS [t2] ON [t2].[system_UserID] = [t1].[system_UserID]
На машинах разработчиков последняя строка начинается с "Left external". На сервере последняя строка начинается с "Inner"
Обновление: Мое решение ниже