using Microsoft.Data.SqlClient; namespace VanDerHeijden.Logging.Sql; /// /// Writes log entries to a SQL Server table in bulk using SqlBulkCopy. /// Expected table schema: /// CREATE TABLE Logs ( /// Id BIGINT IDENTITY PRIMARY KEY, /// Timestamp DATETIME2 NOT NULL, /// Level NVARCHAR(20) NOT NULL, /// Category NVARCHAR(256) NOT NULL, /// Message NVARCHAR(MAX) NOT NULL, /// Exception NVARCHAR(MAX) NULL, /// Path NVARCHAR(1024) NULL, /// Method NVARCHAR(10) NULL, /// ClientIp NVARCHAR(45) NULL, /// Referer NVARCHAR(2048) NULL, /// UserAgent NVARCHAR(512) NULL /// ); /// public sealed class SqlLogWriter(string connectionString, string tableName = "Logs") : IBatchedLogWriter { /// /// Bulk-inserts all entries into the configured SQL Server table using . /// /// The log entries to insert. /// A token that can cancel the operation. public async Task WriteBatchAsync(List entries, CancellationToken ct) { await using var connection = new SqlConnection(connectionString); await connection.OpenAsync(ct); using var bulkCopy = new SqlBulkCopy(connection) { DestinationTableName = tableName, BulkCopyTimeout = 30 }; bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Timestamp), "Timestamp"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Level), "Level"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Category), "Category"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Message), "Message"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Exception), "Exception"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Path), "Path"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Method), "Method"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.ClientIp), "ClientIp"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.Referer), "Referer"); bulkCopy.ColumnMappings.Add(nameof(SqlLogEntry.UserAgent), "UserAgent"); var table = ToDataTable(entries); await bulkCopy.WriteToServerAsync(table, ct); } /// public ValueTask DisposeAsync() => ValueTask.CompletedTask; private static System.Data.DataTable ToDataTable(List entries) { var table = new System.Data.DataTable(); table.Columns.Add("Timestamp", typeof(DateTime)); table.Columns.Add("Level", typeof(string)); table.Columns.Add("Category", typeof(string)); table.Columns.Add("Message", typeof(string)); table.Columns.Add("Exception", typeof(string)); table.Columns.Add("Path", typeof(string)); table.Columns.Add("Method", typeof(string)); table.Columns.Add("ClientIp", typeof(string)); table.Columns.Add("Referer", typeof(string)); table.Columns.Add("UserAgent", typeof(string)); foreach (var e in entries) table.Rows.Add( e.Timestamp, e.Level, e.Category, e.Message, (object?)e.Exception ?? DBNull.Value, (object?)e.Path ?? DBNull.Value, (object?)e.Method ?? DBNull.Value, (object?)e.ClientIp ?? DBNull.Value, (object?)e.Referer ?? DBNull.Value, (object?)e.UserAgent ?? DBNull.Value); return table; } }