Как правильно вернуть SQLFileStream из службы WCF? - PullRequest
1 голос
/ 02 августа 2011

Мы загружаем документы, хранящиеся на SQL Server, используя FileStream из службы WCF, передавая объект SqlFileStream клиенту.Чтобы это работало, мы оставляем транзакцию и соединение с БД в сервисе открытым.Мы закрываем SqlFileStream в клиенте.

'Service
Public Function GetDokumentStream(dokumentId As Integer) As System.IO.Stream Implements IDataService.GetDokumentStream
  Dim cnx = New SqlConnection(...)
  cnx.Open()
  Dim tran = cnx.BeginTransaction()
  Dim cmd As New SqlCommand("Select Dokument.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() from Dokument where ID= @ID", cnx, tran)
  cmd.Parameters.AddWithValue("ID", dokumentId)
  Using rdr = cmd.ExecuteReader()
    If rdr.Read() Then
        Dim pathName = rdr(0).ToString()
        Dim context = CType(rdr(1), Byte())
        Dim sqlFileStream As New SqlFileStream(pathName, context, IO.FileAccess.Read)
        Return sqlFileStream
    Else
        '...
    End If
  End Using

'Client
Dim sqlFileStream = _satDataService.GetDokumentStream(dokumentInfo.DokumentID)
Using fileStream As New IO.FileStream(fileName, IO.FileMode.OpenOrCreate)
    sqlFileStream.CopyTo(fileStream)
    sqlFileStream.Close()
End Using

Должны ли мы реализовать что-либо для ручного закрытия соединения в службе или инфраструктура WCF очищается автоматически?Можно ли закрыть возвращаемый поток в клиенте или лучше создать сложный тип для потока, реализующего IDisposable?

В качестве альтернативы мы можем скопировать SQLFileStream в MemoryStrean, закрыть соединение и вернуть поток памяти:

Using cnx = New SqlConnection(...)
    cnx.Open()
    Using tran = cnx.BeginTransaction()
        Dim cmd As New SqlCommand("Select Dokument.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() from Dokument where ID= @ID", cnx, 

tran)
        cmd.Parameters.AddWithValue("ID", dokumentId)
        Using rdr = cmd.ExecuteReader()
            If rdr.Read() Then
                Dim pathName = rdr(0).ToString()
                Dim context = CType(rdr(1), Byte())
                Dim context1 = rdr(1)
                Dim sqlFileStream As New SqlFileStream(pathName, context, IO.FileAccess.Read)
                sqlFileStream.CopyTo(memoryStream)
                _trace.InfoFormat("Returning file {0} size {1}bytes", pathName, memoryStream.Length)
                memoryStream.Position = 0
                Return memoryStream
            Else
                Throw New ApplicationException("Dokument File not found")
            End If
        End Using
    End Using
End Using

Использует ли это решение больше памяти на сервере, чем напрямую, возвращая SqlFileStream?Или WCF внутренне копирует SqlFileStream в память в любом случае?

Ответы [ 2 ]

0 голосов
/ 05 марта 2016

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

Вам не нужно писать какие-либо операторы SQL, все они генерируются внутри с использованием информации о сопоставлении.

Он также оборачивает соединение и транзакцию таким образом, что после удаления потока транзакция фиксируется, а соединение удаляется, что делает безопасным возврат из службы WCF.

Доступно на https://github.com/RupertAvery/WrappedSqlFileStream

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

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

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

...