Created
May 12, 2015 15:58
-
-
Save nerdpad/6d9b399f2f5f5e5c6519 to your computer and use it in GitHub Desktop.
This is a sample to recreate the issue posted on stackoverflow: http://goo.gl/U83sfn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
USE master | |
GO | |
-- Enable FileStream at the Instance Level | |
EXEC sp_configure filestream_access_level, 2 | |
RECONFIGURE | |
GO | |
-- Provide a FileStream Filegroup | |
-- Create Client DB Database | |
-- Enable Non-Transactional Access at the Database Level | |
IF NOT EXISTS (SELECT [name] FROM sys.databases WHERE [name] = 'ClientDB') | |
CREATE DATABASE ClientDB ON PRIMARY (NAME = ClientDB_data, FILENAME = 'C:\DB\ClientDB.mdf'), | |
FILEGROUP ClientDBFSGroup CONTAINS FILESTREAM (NAME = ClientDB_FS, FILENAME = 'C:\DB\ClientDBFileStream') | |
LOG ON (NAME = 'ClientDB_log', FILENAME = 'C:\DB\ClientDB_log.ldf') | |
WITH FILESTREAM (NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = N'ClientDBFS') | |
GO | |
USE ClientDB | |
GO | |
-- Create documents table | |
IF OBJECT_ID('Documents') IS NULL | |
CREATE TABLE Documents AS FileTable | |
GO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Sample.FileDownloader | |
{ | |
using System.Collections.Generic; | |
using System.Configuration; | |
using System.Data.SqlClient; | |
using System.Data.SqlTypes; | |
using System.IO; | |
using System.Threading.Tasks; | |
using System.Transactions; | |
/// <summary> | |
/// A sample document | |
/// </summary> | |
public class Document | |
{ | |
/// <summary> | |
/// Gets or sets the name. | |
/// </summary> | |
/// <value> | |
/// The name. | |
/// </value> | |
public string Name { get; set; } | |
/// <summary> | |
/// Gets or sets the path. | |
/// </summary> | |
/// <value> | |
/// The path. | |
/// </value> | |
public string UNCPath { get; set; } | |
/// <summary> | |
/// Gets or sets the stream. | |
/// </summary> | |
/// <value> | |
/// The stream. | |
/// </value> | |
public byte[] Stream { get; set; } | |
} | |
/// <summary> | |
/// Sample file downloader | |
/// </summary> | |
public class FileDownloader | |
{ | |
/// <summary> | |
/// The directory to dump all the files | |
/// </summary> | |
private const string DumpDirectory = "c:\\repo\\filedump"; | |
/// <summary> | |
/// The connection string | |
/// </summary> | |
private static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["ContentService"].ConnectionString; | |
/// <summary> | |
/// Starts this instance. | |
/// </summary> | |
/// <returns>Returns nothing</returns> | |
public async Task Start() | |
{ | |
using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) | |
{ | |
// Fetch all the files from table | |
var documents = await this.GetAllDocuments(); | |
// download all the files to dump directory simultaneously | |
// Parallel.ForEach(documents, this.DumpFile); | |
var transaction = Transaction.Current; | |
Parallel.ForEach( | |
documents, | |
d => | |
{ | |
using (var t = transaction.DependentClone(DependentCloneOption.RollbackIfNotComplete)) | |
{ | |
this.DumpFile(d); | |
t.Complete(); | |
} | |
}); | |
ts.Complete(); | |
} | |
} | |
/// <summary> | |
/// Gets all documents. | |
/// </summary> | |
/// <returns>return all documents</returns> | |
private async Task<List<Document>> GetAllDocuments() | |
{ | |
const string CmdStr = "Select name, file_stream.PathName(1) as unc_path, GET_FILESTREAM_TRANSACTION_CONTEXT() as stream from Documents"; | |
var result = new List<Document>(); | |
using (var con = new SqlConnection(ConnectionString)) | |
using (var cmd = new SqlCommand(CmdStr, con)) | |
{ | |
con.Open(); | |
var reader = await cmd.ExecuteReaderAsync(); | |
if (!reader.HasRows) | |
{ | |
return result; | |
} | |
while (reader.Read()) | |
{ | |
result.Add( | |
new Document | |
{ | |
Name = reader.GetString(reader.GetOrdinal("name")), | |
UNCPath = reader.GetString(reader.GetOrdinal("unc_path")), | |
Stream = reader.GetFieldValue<byte[]>(reader.GetOrdinal("stream")) | |
}); | |
} | |
return result; | |
} | |
} | |
/// <summary> | |
/// Dumps the file. | |
/// </summary> | |
/// <param name="document">The document.</param> | |
private void DumpFile(Document document) | |
{ | |
// open the stream and dump the file to the file system | |
using (var sqlStream = new SqlFileStream(document.UNCPath, document.Stream, FileAccess.Read, FileOptions.SequentialScan, 4096)) | |
using (var fileStream = new FileStream(Path.Combine(DumpDirectory, document.Name), FileMode.CreateNew)) | |
{ | |
sqlStream.CopyTo(fileStream); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment