The SqlBulkCopy class is able to bulk insert data from anything that comes from a DataTable or can be read using a SqlDataReader.
So what do you do if you have an array of objects?
You have to convert the array into a DataTable, then call SqlBulkCopy.
First of all, this is my table:
CREATE TABLE [dbo].[Favorites](
[rowId] [int] IDENTITY(1,1) NOT NULL,
[userKey] [int] NOT NULL,
[favoriteId] [nvarchar](255) NULL,
[lastModified] [datetime] NULL,
[isDeleted] [bit] NULL,
[created] [datetime] NULL,
CONSTRAINT [PK_Favorites] PRIMARY KEY CLUSTERED
(
[rowID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I have mapped the table into a DTO. Please notice that names AND THE ORDER of the fields in the DTO matches the names order of the fields in the SQL table:
public class FavoriteDTO
{
public int RowId { get; set; }
public int UserKey { get; set; }
public string FavoriteId { get; set; }
public DateTime? LastModified { get; set; }
public bool? IsDeleted { get; set; }
public DateTime? Created { get; set; }
}
STEP 1: CONVERT THE DTO INTO A DATATABLE
First we must convert the array of FavoriteDTO into a DataTable. This extension method will take care of that:
using System.ComponentModel;
using System.Data;
namespace MyCode
{
public static class IEnumerableExtensions
{
public static DataTable ToDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
{
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
}
}
STEP 2: THE BULKCOPY METHOD
This BulkInsertAsync method will call the ToDataTable() extension method before calling WriteToServerAsync, allowing us to insert the objects in bulk.
using Microsoft.Data.SqlClient;
public async Task BulkInsertAsync(IEnumerable<FavoriteDTO> favorites)
{
try
{
using var connection = new SqlConnection("your connection string");
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
connection.Open();
bulkCopy.DestinationTableName = "Favorites";
await bulkCopy.WriteToServerAsync(favorites.ToDataTable());
}
}
catch (SqlException e)
{
throw new Exception($"Failed to insert favorites: {e.Message}", e);
}
}
That’s it. Happy coding.
MORE TO READ:
- SqlBulkCopy Class from Microsoft
- C# Convert array of objects into a DataTable from briancaos
- C# Dapper Async Bulk Inserts from briancaos