首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用SQLFileStream并发读取?

使用SQLFileStream并发读取?
EN

Stack Overflow用户
提问于 2016-05-04 14:43:29
回答 1查看 311关注 0票数 1

在FILESTREAM中使用.Net 4.52和Server 2014

我们有一个webservice在并发读取时失败,"System.InvalidOperationException:进程无法访问指定的文件,因为它是在另一个事务中打开的。“

我隔离了代码,以便在一个测试程序中重现失败,该程序生成10个并发任务,这些任务使用IsolationLevel.ReadCommitted和IO.FileAccess.Read读取相同的数据。我的理解是,在数据库和文件系统中都会有一个共享锁,不应该有“阻塞”。

对于单个任务,代码一致地工作。有时它适用于2-3个任务。对于10个任务,代码几乎总是失败--每隔一段时间它就会正常工作。我认为其他程序员可能正在访问数据,因为数据库位于我们的一个开发服务器上,但这并不能解释10个任务几乎一致的失败。

任何可能导致失败的建议都将不胜感激!

驱动测试的代码:

代码语言:javascript
复制
    Dim profileKey As Guid = New Guid("DC3F1949-37DB-4D47-B204-0170FA4A40CD")
    Dim taskList As List(Of Task) = New List(Of Task)
    For x = 1 To 10
        Dim tsa As New TestSqlFileStream
        taskList.Add(Task.Run(Function() tsa.GetProfiles(profileKey, True)))
    Next

    Task.WaitAll(taskList.ToArray)

被测试的班级:

公共类TestSqlFileStream

代码语言:javascript
复制
Public Function GetProfiles(profileKey As Guid, getSmallVersionOfImage As Boolean) As List(Of Profile)

    Dim retProfiles = New List(Of Profile)

    Using conn As New SqlConnection("server=blah,1435; initial catalog=blah;Trusted_Connection=Yes;")

        conn.Open()

        Dim cmd = conn.CreateCommand()
        Dim iso As IsolationLevel = IsolationLevel.ReadCommitted

        cmd.Transaction = conn.BeginTransaction(iso)

        Try
            cmd.CommandText = "GetProfiles"
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Parameters.Add(New SqlParameter("@profileKey", SqlDbType.UniqueIdentifier)).Value = profileKey

            Using reader As SqlDataReader = cmd.ExecuteReader
                retProfiles = MapGetProfiles(reader, getSmallVersionOfImage)
            End Using

            cmd.Transaction.Commit()

        Catch ex As Exception
            cmd.Transaction.Rollback()
            Throw
        Finally
            conn.Close()
        End Try

    End Using

    Return retProfiles

End Function

Public Function MapGetProfiles(reader As SqlDataReader, getSmallVersionOfImage As Boolean) _
                       As List(Of Profile)

    Dim profiles As New List(Of Profile)
    Dim transactionToken As Byte()
    Try
        While reader.Read()

            Dim profile As New ServiceTypes.SubTypes.Profile

            profile.ParentKey = reader("ParentKey")
            profile.ProfileKey = reader("ProfileKey")
            profile.ProfileType = ConvertToProfileType(reader("ProfileType"))
            If reader("Active") Is Nothing Then profile.Active = False Else profile.Active = reader("Active")
            If IsDBNull(reader("Data")) Then profile.Data = Nothing Else profile.Data = reader("Data")

            Dim imagePath
            If getSmallVersionOfImage Then imagePath = reader("ImageThumbnailPath") Else imagePath = reader("ImagePath")
            transactionToken = DirectCast(reader("transactionContext"), Byte())

            If Not IsDBNull(imagePath) Then

                If Not transactionToken.Equals(DBNull.Value) Then
                    LoadImage(profile, imagePath, transactionToken)
                End If

            End If

            profiles.Add(profile)

        End While

    Catch ex As Exception

        Throw
    Finally
        reader.Close()
    End Try


    Return profiles

End Function

Public Sub LoadImage(ByRef profile As Profile, image As String, transactionContext As Byte())

    Using sqlFileStream = New SqlFileStream(image, transactionContext, IO.FileAccess.Read, FileOptions.SequentialScan, 0)                                    
        Dim retrievedImage = New Byte(sqlFileStream.Length - 1) {}
        sqlFileStream.Read(retrievedImage, 0, sqlFileStream.Length)
        profile.Image = retrievedImage
        sqlFileStream.Close()

    End Using

End Sub

Private Function ConvertToProfileType(profileType As String) As ProfileType
    Dim type = ServiceTypes.SubTypes.ProfileType.None
    Select Case profileType
        Case Nothing
            type = ServiceTypes.SubTypes.ProfileType.None
    End Select
    Return type
End Function

端级

更新:--我已经看过这个问题了,但是问题是不同的,因为它们是在事务中分裂成并行的:在我的示例中,Threading and SqlFileStream. The process cannot access the file specified because it has been opened in another transaction每个任务都启动自己的事务。

Update2当我停在事务内的断点并在查询窗口中运行DBCC时,结果是“没有活动打开的事务”,似乎SqlConnection.BeginTransaction实际上没有在数据库中打开事务。

Update3还从事务日志(在命名事务之后)读取:

代码语言:javascript
复制
Use myDB
GO
select top 1000 [Current LSN],
       [Operation],
       [Transaction Name],
       [Transaction ID],
       [Transaction SID],
       [SPID],
       [Begin Time]
FROM   fn_dblog(null,null)
order by [Begin Time] desc

日志中没有显示我提供的名称的事务。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-05-06 13:32:10

注意:这仅适用于检索到的集合的原子性不重要的解决方案。为了更好的原子性,我建议使用TransactionScope。

当在事务下检索多个文件时,事务队列/锁定机制似乎变得混乱( when Reader.Read循环)。我突破了文件检索来使用一个新的事务或每个文件检索,并可以运行100个并行任务,针对同一层次的配置文件集为一个单亲。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37031070

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档