Avec l'arrivée du Framework 3.5 SP1 béta, de nouvelles classes ont fait leur apparition.

Notamment un ensemble de classes pour gérer le nouveau type SQL SERVER 2008 : FILESTREAM.

Lors de mes différentes présentations sur le sujet, j'ai toujours du utiliser une classe encapsulant un appel à une dll C, que je ne présentais pas d'ailleurs :) en expliquant que "bientôt" nous aurions droit à de vrais classes managed pour gérer tout ça

Et bien, c'est le cas; le framework (3.5 SP1 béta) est là !

Maintenant, tout devient (relativement) simple Happy

D'abord le script de création d'une table contenant un type FILESTREAM :

-- 1. Enable FILESTREAM support
EXEC [sp_filestream_configure] @enable_level = 3;

-- 2. Create a database with a File Group that contains FILESTREAM
CREATE DATABASE FileManagement
ON
PRIMARY (
    NAME = FileManagement_Primary,
    FILENAME = 'c:\temp\data\FileManagement.mdf'),
FILEGROUP FileStreamGroup CONTAINS FILESTREAM (
    NAME = FileManagement_FileGroup,
    FILENAME = 'c:\temp\data\FileManagement')
LOG ON  ( NAME = FileManagement_Log,
    FILENAME = 'c:\temp\data\FileManagementLog.ldf')
GO

-- 3. Create a Table with FILESTREAM
CREATE TABLE [dbo].[Files]
(
    FileID uniqueidentifier NOT NULL ROWGUIDCOL PRIMARY KEY,
    FileContents varbinary(max) FILESTREAM DEFAULT(0x)
)
GO

En préambule, on crée une transaction (BIENSUR !)

SqlTransaction tx = conn.BeginTransaction();

J'insère dans ma table, contenant un filestream, une ligne (tout ça pour récupérer un contexte de transaction par la suite)

SqlCommand insertFileCommand = conn.CreateCommand();

insertFileCommand.Transaction = tx;

insertFileCommand.CommandText = "INSERT INTO Files (FileID) VALUES (@FileID)";

Guid newFileID = Guid.NewGuid();

insertFileCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;

insertFileCommand.ExecuteNonQuery();

Je récupère le contexte de transaction :

SqlCommand getPathAndTokenCommand = conn.CreateCommand();
getPathAndTokenCommand.Transaction = tx;
getPathAndTokenCommand.CommandText = "SELECT FileContents.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Files WHERE FileID = @FileID";
getPathAndTokenCommand.Parameters.Add("@FileID", SqlDbType.UniqueIdentifier).Value = newFileID;
SqlDataReader tokenReader = getPathAndTokenCommand.ExecuteReader(CommandBehavior.SingleRow);
tokenReader.Read();
SqlString filePathName = tokenReader.GetSqlString(0);
SqlBinary fileToken = tokenReader.GetSqlBinary(1);
tokenReader.Close();

Et maintenant j'utilise ce fameux nouveau type, comme un simple type FileStream (j'ouvre un flux, et j'écris dedans !)

FileStream inputFile = File.OpenRead("TextFile1.txt");
SqlFileStream sqlFile = new SqlFileStream(filePathName.Value, fileToken.Value, System.IO.FileAccess.Write);
byte[] buffer = new byte[512 * 1024]; // 512Kb

int bytesRead = inputFile.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
    sqlFile.Write(buffer, 0, bytesRead);
    bytesRead = inputFile.Read(buffer, 0, buffer.Length);
}

J'oublie pas de tout fermer hein ;)

 

sqlFile.Close();
inputFile.Close();
tx.Commit();
conn.Close();

 

Et voilà !

Bon FILESTREAM ! Applause