Linq Query возвращает неверный набор результатов - PullRequest
6 голосов
/ 18 ноября 2009

У меня очень сложный запрос Linq to SQL, который возвращает набор результатов из базы данных Microsoft SQL Server. Запрос создается с использованием синтаксиса, похожего на:

Dim db as MyDataContext = MyGetDataContextHelper()
Dim qry = From rslt in db.MyView Select ColumnList

If userParam1 IsNot Nothing Then
    qry = qry.Where(lambda for the filter)
End If

etc....

Return qry.ToList()

В запросе есть несколько пользовательских фильтров, в том числе фильтр, выполняющий поиск по географическому радиусу.

Вот проблема. У меня установлен перерыв на вызов ToList в самом конце. Когда наступает разрыв, я использую визуализатор отладки Linq to SQL, чтобы увидеть сгенерированный оператор SQL. Я копирую этот сложный оператор SQL в окно запросов SQL Server Management Studio и выполняю его для своей базы данных, чтобы получить именно тот набор результатов, который мне нужен. Таким образом, сгенерированный SQL, похоже, дает желаемый результат. Однако, когда я выполняю метод «ToList» объекта запроса, возвращаемый список имеет меньше строк и несколько разных строк. Я также пробовал это с использованием записи свойства журнала DataContext в файл, с тем же результатом. Запрос генерирует правильный набор результатов в SQL Management Studio, но неверные результаты из метода ToList.

Как это может быть? Если сгенерированный SQL просто передается по соединению с SQL Server, не должен ли он генерировать именно тот набор результатов, который я вижу в SQL Server Management Studio? Я предполагаю, что я неправильно понимаю механизм Linq to SQL, то есть это не просто переход на SQL Server. Это правильно?

EDIT: В соответствии с приведенным ниже запросом, здесь приведена очень сжатая версия SQL, сгенерированная Linq, для краткости большинство столбцов результатов удалено. Он дает правильный результат в SQL Management Studio, но результат, возвращаемый моему приложению, отличается.

SELECT [t3].[Id]
FROM (
    SELECT DISTINCT [t1].[Id]
    FROM (
        SELECT [t0].[Id], [t0].[ItemDate]
        FROM [dbo].[MySearchView] AS [t0]
        ) AS [t1]
    WHERE (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[ZipCoverage] AS [t2]
        WHERE ([t2].[Id] = [t1].[Id]) 
        AND ([t2].[Latitude] >= (41.09046 - (0.5))) 
        AND ([t2].[Latitude] <= (41.09046 + (0.5))) 
        AND ([t2].[Longitude] >= (-73.43106 - (0.5))) 
        AND ([t2].[Longitude] <= (-73.43106 + (0.5))) 
        AND (ABS(3956.08833132861 * 2 * ATN2(SQRT(POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) - 0.717163818159029) / (CONVERT(Float,2))), 2) + (COS(0.717163818159029) * COS((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) * POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Longitude]) - -1.28161377022951) / (CONVERT(Float,2))), 2))), SQRT((1 - POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) - 0.717163818159029) / (CONVERT(Float,2))), 2)) + (COS(0.717163818159029) * COS((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) * POWER(SIN(((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Longitude]) / (CONVERT(Float,2))), 2))))) <= 5))) 
        AND ([t1].[ItemDate] <= '11/17/2009 8:12:42 PM')
    ) AS [t3]

ОБНОВЛЕНИЕ 2009-11-17 Не удалось связаться с MS по этому вопросу. Создал образец приложения, которое я отправил в службу поддержки. Они продублировали проблему и проводят исследования. Будет опубликован ответ, когда я получу ответ.

ОБНОВЛЕНИЕ 2009-12-21 Наконец, с помощью Microsoft пришли к правильному ответу. Пожалуйста, смотрите мой принятый ответ ниже для объяснения.

Ответы [ 7 ]

2 голосов
/ 21 декабря 2009

Что ж, после некоторой переписки с очень полезным представителем службы поддержки от Microsoft мы наконец пришли к источнику проблемы. И, к сожалению, я не предоставил достаточно информации в своем первоначальном посте, чтобы кто-то здесь, на SO, мог принять решение, поэтому я приношу свои извинения в этом отношении.

Вот в чем проблема - как часть кода, который создает запрос LINQ, я объявил переменную .Net следующим образом:

Dim RadCvtFactor As Decimal = Math.PI / 180

Оказывается, что когда это передается в SQL, объявлением параметра, как видно из файла журнала LINQ, является DECIMAL (29, 4). Из-за значения масштаба в объявлении недопустимое значение передается в СУБД, что приводит к странной разнице в результатах запроса.

Объявление переменной .Net как единственного значения, например:

Dim RadCvtFactor As Single = Math.PI / 180

полностью исправляет проблему.

Представитель Microsoft признал, что это преобразование параметров может быть "потенциальной проблемой", и проконсультируется с командой продукта.

Спасибо всем, кто прислал ответы.

1 голос
/ 15 декабря 2009

Это может звучать глупо, но всегда полезно проверить, подключаетесь ли вы к той же среде базы данных в SSMS, что и из вашего приложения? :)

1 голос
/ 18 ноября 2009
qry.ToList()

Этот оператор создает и возвращает требуемый список. Вам нужно присвоить результат чему-либо (например, локальной переменной), если вы хотите использовать список позже.

Редактировать: спасибо за обновление.

Я подозреваю, что должно быть что-то, о чем вы нам не говорите, что также может быть проблемой, и это может жить здесь:

Dim db as MyDataContext = MyGetDataContextHelper()

Подключается ли этот метод к той же базе данных, к которой вы подключались при использовании sql studio?

  • Проверьте свойство соединения для текстового текста.
  • Убедитесь, что запрос отправлен в базу данных, следя за ним с помощью SQL Server Profiler.
  • Выполните очень простой запрос и подтвердите, что он возвращает правильные результаты.
1 голос
/ 18 ноября 2009

Вы можете использовать DebuggerWriter для проверки фактического SQL, отправленного на сервер.

1 голос
/ 18 ноября 2009

Другая возможность - уровень изоляции и характер данных. Вы используете REPEATABLE READ или READ UNCOMMITTED или SNAPSHOT под Linq? А как насчет использования SSMS? Очевидно, что если данные перемещаются, то слабый уровень изоляции позволит вам пропустить строки, прочитать несколько строк дважды, увидеть старую версию строки и т. Д.

Кроме того, можете ли вы дать нам немного лучшее представление о том, как выглядит "очень сложный запрос"? Вам не нужно использовать настоящие имена таблиц.

1 голос
/ 18 ноября 2009

Я бы начал с просмотра вашего DataContext. Если ваш DataContext не обновляется с SQL Server, возможно, вы возвращаете более старую версию таблицы.

DataContext поддерживает состояние базы данных, когда она была создана. Вы хотите использовать новый контекст для каждого набора операций.

1 голос
/ 18 ноября 2009

Одна вещь, которая сразу приходит на ум, - это проблема с разрешениями. Возможно ли, чтобы программа и выполняемый вручную запрос выполнялись под разными учетными данными и, следовательно, имели разные уровни доступа к базе данных? Это может повлиять на результаты запроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...