This post demonstrates how write a SQL table out to a local CSV file, the full code for this can be found here.

GetSqlRowsAsync

This returns an IAsyncEnumerable of DataRows, IAsyncEnumerable has been newly introduced with C# 8.0 and it allows results to be streamed back to the caller as they become available meaning we can stream our SQL table rows directly to our destination CSV file without having to hold anything in memory.

public static async IAsyncEnumerable<DataRow> GetSqlRowsAsync(string schema, string tableName)
{
	SqlConnection _sqlConnection = new SqlConnection(_sqlConnectionString);

	using (SqlCommand command = new SqlCommand("select * from " + schema + "." + tableName))
	{
		command.Connection = _sqlConnection;
		await _sqlConnection.OpenAsync();

		DataTable table = new DataTable();

		// Get schema of SQL table
		using (SqlDataAdapter adapter = new SqlDataAdapter(command))
		{
			table.TableName = tableName;
			adapter.FillSchema(table, SchemaType.Source);
		}

		// Return rows as they're read
		using (SqlDataReader reader = await command.ExecuteReaderAsync())
		{
			while (await reader.ReadAsync())
			{
				DataRow row = table.NewRow();

				row.Table.Columns
					.Cast<DataColumn>()
					.ToList()
					.ForEach(x => row[x] = reader.GetValue(x.Ordinal));

				yield return row;
			}
		}

		_sqlConnection.Close();
	}
}

Usage

using (var streamWriter = new StreamWriter(_outputPath))
using (var csvWriter = new CsvWriter(streamWriter))
{
	// Quote all fields
	csvWriter.Configuration.ShouldQuote = (field, context) => true;

	bool writeHeaders = true;

	await foreach (DataRow row in GetSqlRowsAsync(_schema, _table))
	{
		// Write header row
		if (writeHeaders)
		{
			foreach (DataColumn column in row.Table.Columns)
			{
				csvWriter.WriteField(column.ColumnName);
			}

			await csvWriter.NextRecordAsync();
			writeHeaders = false;
		}
		
		// Write data rows
		for (var i = 0; i < row.ItemArray.Length; i++)
		{
			csvWriter.WriteField(row[i]);
		}

		await csvWriter.NextRecordAsync();
	}
}

Usage – GZip

It’s also possible to zip the output stream in transit which is more efficient than outputting the file and then zipping it afterwards.

using (var outputFile = File.Create(_outputPath))
using (var gZipStream = new GZipStream(outputFile, CompressionMode.Compress))
using (var streamWriter = new StreamWriter(gZipStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
	// Quote all fields
	csvWriter.Configuration.ShouldQuote = (field, context) => true;

	bool writeHeaders = true;

	await foreach (DataRow row in GetSqlRowsAsync(_schema, _table))
	{
		// Write header row
		if (writeHeaders)
		{
			foreach (DataColumn column in row.Table.Columns)
			{
				csvWriter.WriteField(column.ColumnName);
			}

			await csvWriter.NextRecordAsync();
			writeHeaders = false;
		}

		// Write data rows
		for (var i = 0; i < row.ItemArray.Length; i++)
		{
			csvWriter.WriteField(row[i]);
		}

		await csvWriter.NextRecordAsync();
	}
}

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *