13

Tôi đang trích xuất nội dung của tệp trong tệp tệp SQL. Mã sau đây hoạt động nếu tôi không sử dụng song song.Luồng và SqlFileStream. Quá trình không thể truy cập tệp được chỉ định bởi vì nó đã được mở trong một giao dịch khác

Tôi nhận được ngoại lệ sau, khi đọc tệp luồng sql đồng thời (Song song).

Quy trình không thể truy cập tệp được chỉ định vì tệp đã được mở trong giao dịch khác.

TL; DR:

Khi đọc một tập tin từ FileTable (sử dụng GET_FILESTREAM_TRANSACTION_CONTEXT) trong một Parallel.ForEach tôi nhận được ngoại lệ trên.

mẫu mã cho bạn thử:

https://gist.github.com/NerdPad/6d9b399f2f5f5e5c6519

Còn Version:

Fetch đính kèm, và trích xuất nội dung:

var documents = new List<ExtractedContent>(); 
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
{ 
    var attachments = await dao.GetAttachmentsAsync(); 

    // Extract the content simultaneously 
    // documents = attachments.ToDbDocuments().ToList(); // This works 
    Parallel.ForEach(attachments, a => documents.Add(a.ToDbDocument())); // this doesn't 

    ts.Complete(); 
} 

DAO đọc File Table:

public async Task<IEnumerable<SearchAttachment>> GetAttachmentsAsync() 
{ 
    try 
    { 
     var commandStr = "...."; 

     IEnumerable<SearchAttachment> attachments = null; 
     using (var connection = new SqlConnection(this.DatabaseContext.Database.Connection.ConnectionString)) 
     using (var command = new SqlCommand(commandStr, connection)) 
     { 
      connection.Open(); 

      using (var reader = await command.ExecuteReaderAsync()) 
      { 
       attachments = reader.ToSearchAttachments().ToList(); 
      } 
     } 

     return attachments; 
    } 
    catch (System.Exception) 
    { 
     throw; 
    } 
} 

Tạo đối tượng cho mỗi tập tin: Đối tượng chứa một tham chiếu đến GET_FILESTREAM_TRANSACTION_CONTEXT

public static IEnumerable<SearchAttachment> ToSearchAttachments(this SqlDataReader reader) 
{ 
    if (!reader.HasRows) 
    { 
     yield break; 
    } 

    // Convert each row to SearchAttachment 
    while (reader.Read()) 
    { 
     yield return new SearchAttachment 
     { 
      ... 
      ... 
      UNCPath = reader.To<string>(Constants.UNCPath), 
      ContentStream = reader.To<byte[]>(Constants.Stream) // GET_FILESTREAM_TRANSACTION_CONTEXT() 
      ... 
      ... 
     }; 
    } 
} 

đọc các tập tin sử dụng SqlFileStream: ngoại lệ là ném ở đây

public static ExtractedContent ToDbDocument(this SearchAttachment attachment) 
{ 
    // Read the file 
    // Exception is thrown here 
    using (var stream = new SqlFileStream(attachment.UNCPath, attachment.ContentStream, FileAccess.Read, FileOptions.SequentialScan, 4096)) 
    { 
     ... 
     // extract content from the file 
    } 

    .... 
} 

Cập nhật 1:

Theo this bài viết nó có vẻ như nó có thể là một vấn đề cấp độ Isolation. Có ai từng phải đối mặt với vấn đề tương tự không?

+0

Thử mở tệp trên cùng một chuỗi đã làm phần còn lại của SQL. Có lẽ điều này chỉ đơn giản là không được phép. – usr

+0

Bạn đang viết thư cho 'tài liệu' trên nhiều chủ đề, 'Danh sách ' không phải là chủ đề an toàn và bạn không thể làm điều này (Nó không có khả năng là nguồn của vấn đề của bạn nhưng nó là một vấn đề) –

Trả lời

4

Giao dịch không chảy vào Parallel.ForEach, bạn phải tự mang lại giao dịch trong.

//Switched to a thread safe collection. 
var documents = new ConcurrentQueue<ExtractedContent>(); 
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) 
{ 
    var attachments = await dao.GetAttachmentsAsync(); 
    //Grab a reference to the current transaction. 
    var transaction = Transaction.Current; 
    Parallel.ForEach(attachments, a => 
    { 
     //Spawn a dependant clone of the transaction 
     using (var depTs = transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete)) 
     { 
      documents.Enqueue(a.ToDbDocument()); 
      depTs.Complete(); 
     } 
    }); 

    ts.Complete(); 
} 

Tôi cũng chuyển từ List<ExtractedContent> để ConcurrentQueue<ExtractedContent> vì bạn không được phép gọi .Add( trên một danh sách từ nhiều luồng ở cùng lúc.

+0

Giả sử giao dịch được sao chép, sao truy cập luồng phim SQL đồng thời có an toàn không? – usr

+0

@usr Vâng, đó là một trong những lợi ích của việc sử dụng [dòng phim SQL] (https://technet.microsoft.com/en-us/library/bb933993 (v = sql.105) .aspx), họ có thể tham gia trong giao dịch. Việc truy cập tệp thực được thực hiện thông qua chia sẻ mạng UNC với đường dẫn tạm thời tồn tại trong suốt thời gian của giao dịch. Nó sẽ không khác với việc mở hai đối tượng Read Only 'FileStream' vào cùng một đường dẫn mạng. –

+2

@usr Hết sức tò mò, tôi đã kiểm tra nguồn tham chiếu. SqlFileStream chỉ là một trình bao bọc xung quanh một FileStream bình thường và một trình bao bọc được quản lý xung quanh [FILE_FULL_EA_INFORMATION] (https://msdn.microsoft.com/en-us/library/windows/hardware/ff545793 (v = vs.85) .aspx) ('Byte []' bạn truyền vào hàm khởi tạo là dữ liệu cho cấu trúc đó) –

Các vấn đề liên quan