Flux de données dans la database du server SQL sans mettre en memory tampon l'set des données

J'ai une table Blob, qui a un varbinary (max) comme une colonne. Maintenant, je veux stocker des données dans la database en utilisant un Filestream. Les données peuvent être très grandes (dans mon cas 1,5 Go), donc je ne veux pas charger toutes datatables dans un tampon.

Ce que j'ai essayé:

using (FileStream fs = File.Open(@"BigData.iso", FileMode.Open)) { using (SqlConnection conn = new SqlConnection()) { conn.ConnectionSsortingng = @"..."; conn.Open(); SqlCommand command = new SqlCommand("INSERT INTO Blob Values (@0, @1)", conn); command.Parameters.Add(new SqlParameter("0", Guid.NewGuid())); var sqlb = new SqlBytes(fs); command.Parameters.Add(new SqlParameter("1", SqlDbType.VarBinary, -1)).Value = sqlb; command.ExecuteNonQuery(); } } 

Mais j'ai une exception OutOfMemoryException , car SqlBytes initialise son tampon à la taille entière des données.

Je sais qu'il existe une fonctionnalité FILESTREAM de Microsoft, mais je ne veux pas l'utiliser.

Y a-t-il un moyen d'y parvenir?

Vous pouvez lire le file en petits morceaux et les append à la colonne de données.

Vous aurez besoin d'une colonne IDENTITY ou d'une autre colonne qui peut être utilisée comme key pour exécuter des instructions UPDATE . Voici un exemple utilisant une colonne IDENTITY :

Créer une table pour stocker datatables

 CREATE TABLE [dbo].[table1]( [ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL, [Data] [varbinary](max) NULL, ) 

Implémenter C # pour insert / mettre à jour datatables en morceaux

 private const ssortingng C_SqlConnectionSsortingng = @"Server=SERVERNAME;Database=DBNAME;Trusted_Connection=yes;"; private const int C_FileChunkSizeBytes = 1024 * 1024; // 1 MB private static void storeFile(ssortingng filepath) { using (FileStream fs = File.Open(filepath, FileMode.Open)) { using (SqlConnection conn = new SqlConnection()) { conn.ConnectionSsortingng = C_SqlConnectionSsortingng; conn.Open(); // Use a transaction to ensure that all parts of the file get stored to DB SqlCommand command = new SqlCommand("BEGIN TRAN", conn); command.ExecuteNonQuery(); var pos = 0; byte[] fileBytes = null; int sqlRowId = 0; // Read the file in chunks while (pos < fs.Length) { // Read file bytes var bytesToRead = pos + C_FileChunkSizeBytes < fs.Length ? C_FileChunkSizeBytes : (int)(fs.Length - pos); fileBytes = new byte[bytesToRead]; fs.Read(fileBytes, 0, bytesToRead); // Store bytes to a parameter var varbinary = new SqlParameter("0", System.Data.SqlDbType.VarBinary, -1); varbinary.Value = fileBytes; if (pos == 0) { // If this is the first chunk, then we need to INSERT // The HOLDLOCK hint will hold a lock on the table until transaction completes (or is rolled back) command = new SqlCommand("INSERT INTO [dbo].[table1] WITH(HOLDLOCK) VALUES(@0)", conn); command.Parameters.Add(varbinary); command.ExecuteNonQuery(); // Get the row ID for the inserted row command = new SqlCommand("SELECT @@IDENTITY", conn); sqlRowId = Convert.ToInt32(command.ExecuteScalar()); } else { // Update existing row and append data command = new SqlCommand("UPDATE [dbo].[table1] SET [Data] = [Data] + @0 WHERE [ID] = @1", conn); command.Parameters.Add(varbinary); command.Parameters.Add(new SqlParameter("1", System.Data.SqlDbType.Int)).Value = sqlRowId; command.ExecuteNonQuery(); } // ** Good place for a breakpoint pos += bytesToRead; } // Commit transaction command = new SqlCommand("COMMIT TRAN", conn); command.ExecuteNonQuery(); conn.Close(); } } } 

Essai

Placez un point d'arrêt dans le code C # au bas de la boucle while, par exemple à pos += bytesToRead; .

Lors de l'exécution du code, lorsque l'exécution du code s'arrête au point d'arrêt, vérifiez datatables dans SQL:

 SELECT * ,LEN([Data]) AS [Length] FROM [dbo].[table1] WITH(NOLOCK) 

L'indice NOLOCK nous permet de voir datatables dans les transactions non validées. LEN([Data]) montrera comment la longueur du champ augmente après chaque itération de la boucle while.