Как это возможно, что мой SQLCommand + ExecuteScalar / ExecuteReader может вернуть результат из другого вызова? - PullRequest
0 голосов
/ 01 августа 2011

У меня есть 2 фрагмента кода, которые, кажется, испытывают то, что можно описать только как проблемы с многопоточностью.

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

Код основан на SQL с использованием ADO.Net.

(Примечание: я ценю потенциал дляАтаки SQL-инъекций, но сейчас давайте предположим, что это решается, потому что SQL для этих команд SQL не может напрямую зависеть от пользовательского ввода)

GetField принимает SQLConnection и некоторый SQL.

  • Предполагается, что SQL структурирован как «Выбрать top 1 SomeField from SomeTable».
  • Создает SQLCommand вокруг этого SQL
  • Выполняет функцию ExecuteScalar команды.
  • Возвращает значение, предоставленное ExecuteScalar

GetRecord аналогичным образом принимает SQLConnection и некоторый SQL.

  • Предполагается, чтоSQL структурирован как «Выберите SomeFields из SomeTable».
  • Выполняет функцию ExecuteReader команды.
  • Он анализирует результат, предоставленный ExecuteReader, в известную структуру
  • Возвращает известную структуру

Проблема У меня естьчто время от времени функция ExecuteReader в GetRecord, похоже, извлекает результат, который можно ожидать от GetField

Затем происходит сбой при попытке анализа данных в известной структуре.

Я не могу воспроизвести это надежно ипоэтому я прошу о помощи.

У кого-нибудь есть идеи, почему это может происходить?

FWIW Мой внутренний SQL-код - SQL2008

Обновление: FWIW, объекты, которым принадлежат эти методы, имеют свое собственное поле подключения, которое инициализируется новым объектом SQLConnection, который сам получает строку подключения из центрального расположения.

Обновление :Вот пример того типа объекта DA, о котором я говорю.


Imports System.Data.SqlClient
Public Module SomeModule
    Public Function GlobalConnection() As SqlConnection
        Return New SqlConnection(GetSQLStringFromConfig())
    End Function
End Module
Public Class ExampleDA
    Protected Con As SqlConnection
    Public Sub New()
        Me.Con = GlobalConnection()
    End Sub
    Public Function GetRecord(ByVal SQL As String) As String()
        Dim Close As Boolean = EnsureConnectionOpen(Con)
        Dim dc As New SqlCommand(SQL, Con)
        Try
            Dim DcExecuteReader As SqlDataReader
            If Close Then
                DcExecuteReader = dc.ExecuteReader(CommandBehavior.CloseConnection)
            Else
                DcExecuteReader = dc.ExecuteReader()
            End If
            ' ToStringArray doesn't exist but it gets the general point across.
            Return DcExecuteReader.ToStringArray() 
        Catch ex As SqlException
            Throw
        End Try
    End Function

    Public Function GetField(ByVal SQL As String, ByVal DefaultValue As Object) As Object
        Dim Close As Boolean = EnsureConnectionOpen(Con)
        Dim dc As SqlCommand = New SqlCommand(SQL, Con)
        Dim Result As Object = GetField(dc, DefaultValue)
        If Close Then Con.Close()
        Return Result
    End Function

    Private Function GetField(ByVal Command As SqlCommand, Optional ByVal DefaultValue As Object = Nothing) As Object
        Try
            Dim ReturnValue As Object
            Dim Close As Boolean = EnsureConnectionOpen(Command.Connection)
            ReturnValue = Command.ExecuteScalar()
            If ReturnValue Is Nothing Then
                ReturnValue = DefaultValue
            End If
            If Close Then
                Command.Connection.Close()
            End If
            If IsDBNull(ReturnValue) Then
                Return DefaultValue
            Else
                Return ReturnValue
            End If
        Catch ex As Exception
            Debug.WriteLine(ex.ToString)
            Throw
        End Try
    End Function
    Private Function EnsureConnectionOpen(ByRef Con As SqlConnection) As Boolean
        If Con.State <> ConnectionState.Open Then
            Con.Open()
            Return True
        End If
        Return False
    End Function
End Class

Ответы [ 4 ]

1 голос
/ 01 августа 2011

Как вы обрабатываете соединения с базой данных?

Одно соединение с базой данных = один процесс для SQL, и они в любом случае не используются совместно.Чтобы получить «смешанные» результаты, вам потребуется перезаписать некоторый контекст соединения, которое является общим.

Другими словами, SQLConnection не является поточно-ориентированным

0 голосов
/ 01 августа 2011

ОК, это напрашивается на неприятности! Перепишите ваши методы GetXXX в этом направлении (извините за синтаксис C #, но я не знаком с VB ;-))

GetField( string sqlCommand )
{
   SqlConnection conn = new SqlConnection( connectionString );
   conn.Open();

   try
   {
       using ( SqlCommand cmd = conn.CreateCommand() )
       {
           cmd.CommandText = cmd;

           cmd.ExecuteScalar();


           //here be your code..

       }

   }
   finally
   { conn.Close();
   }
}

Не храните соединения, не делайте с ними ничего особенного. Открывайте и закрывайте при необходимости, библиотека выполняет пул команд за кулисами!

НТН

Mario

0 голосов
/ 01 августа 2011

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

Вы всегда должны вызывать GetConnection, так как бэкэнд в любом случае выполняет пул соединений. Поэтому с точки зрения производительности это, скорее всего, не будет иметь значения.

НТН

Mario

0 голосов
/ 01 августа 2011

Как соединение сохраняется / распределяется между функциями?

Работаете ли вы под MARS?Если нет, попробуйте включить его в строке подключения (MultipleActiveResultSets = True).

Обратите внимание, что IIS может / будет переключать контекст выполнения из одного потока в другой по собственному желанию во время выполнения.Таким образом, любое совместно используемое соединение в конечном итоге будет разделено между двумя потоками и может выйти из строя.Использование MARS и / или явно новых соединений должно облегчить это.

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