Bulk Operations - BulkMerge - Performance Comparison (DataTable) (NET Core)

CSharp · Console

/Program.cs

// Bulk Operations
// Doc: https://bulk-operations.net/bulk-merge

// @nuget: Z.BulkOperations
// @nuget: System.Data.SqlClient

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Z.BulkOperations;

public class Program
{
	public static List<BenchmarkResult> BenchmarkResults = new List<BenchmarkResult>();
	
	public static void Main()
	{
		GenerateDatabase();
		JustInTime_Compile();
		
		var customerCount = 1000;
		
		var clockBulkMerge = new Stopwatch();
		
		// Generate X customers (half for update, half for insert)
		var dtCustomers = GenerateCustomers(customerCount);
		dtCustomers.AsEnumerable().ToList().ForEach(x => x["FirstName"] += "_z");
		
		using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServer()))
		{
			connection.Open();
			
			// BulkMerge 
			using (var bulk = new BulkOperation(connection))
			{
				bulk.DestinationTableName = "Customers";
				bulk.AutoMapOutputDirection = false; // performance can be improved with options
				
				clockBulkMerge.Start();
				bulk.BulkMerge(dtCustomers); 
				clockBulkMerge.Stop();
				
				BenchmarkResults.Add(new BenchmarkResult() { Action = "BulkMerge (Bulk Operations)", Entities = dtCustomers.Rows.Count, Performance = clockBulkMerge.ElapsedMilliseconds + " ms" });
			}
		}
		
		FiddleHelper.WriteTable(BenchmarkResults);
	}
		 
	public static void JustInTime_Compile()
	{		
		using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServer()))
		{
			connection.Open();
			
			DataTable dtCustomers = new DataTable("Customers");

			dtCustomers.Columns.Add("CustomerID", typeof(int));
			dtCustomers.Columns.Add("Code", typeof(string)); 
			dtCustomers.Columns.Add("FirstName", typeof(string)); 
			dtCustomers.Columns.Add("LastName", typeof(string)); 

			for(int i = 0; i < 20; i++)
			{
				var drCustomer = dtCustomers.NewRow();  
				drCustomer["Code"] = "Update_Code_" + i;  
				drCustomer["FirstName"] = "Update_FirstName_" + i;  
				drCustomer["LastName"] ="Update_LastName_" + i;  

				dtCustomers.Rows.Add(drCustomer);
			}
			
			// BulkInsert
			using (var bulk = new BulkOperation(connection))
			{
				bulk.DestinationTableName = "Customers";
				bulk.AutoMapOutputDirection = false;
				
				bulk.BulkInsert(dtCustomers);
				
				// Clean
				bulk.BulkDelete(dtCustomers);
			}
		}
	}
	
	public static DataTable GenerateCustomers(int count) 
	{
		var updateCount = count / 2;
		var insertCount = count - updateCount;
		
		DataTable dtTable = new DataTable("Customers");
		
		dtTable.Columns.Add("CustomerID", typeof(int));
		dtTable.Columns.Add("Code", typeof(string)); 
		dtTable.Columns.Add("FirstName", typeof(string)); 
		dtTable.Columns.Add("LastName", typeof(string)); 

		for(int i = 0; i < updateCount; i++)
		{
			var drCustomer = dtTable.NewRow();  
			drCustomer["Code"] = "Update_Code_" + i;  
			drCustomer["FirstName"] = "Update_FirstName_" + i;  
			drCustomer["LastName"] ="Update_LastName_" + i;  

			dtTable.Rows.Add(drCustomer);
		}
		
		using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServer()))
		{
			connection.Open();
			
			// BulkInsert
			using (var bulk = new BulkOperation(connection))
			{
				bulk.DestinationTableName = "Customers";
				
				bulk.BulkInsert(dtTable);
			}
		}
		
		for(int i = 0; i < insertCount; i++)
		{
			var drCustomer = dtTable.NewRow();  
			drCustomer["Code"] = "Insert_Code_" + i;  
			drCustomer["FirstName"] = "Insert_FirstName_" + i;  
			drCustomer["LastName"] ="Insert_LastName_" + i;  

			dtTable.Rows.Add(drCustomer);
		}
		
		return dtTable;
	}

public static void GenerateDatabase()
	{
		using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServer()))
		{
			connection.Open();
			using (var command = connection.CreateCommand())
			{
				command.CommandText = @"   
CREATE TABLE [Customers]
(
	[CustomerID] [INT] IDENTITY(1,1) NOT NULL,
	[Code] [NVARCHAR](MAX) NULL,
	[FirstName] [NVARCHAR](MAX) NULL,
	[LastName] [NVARCHAR](MAX) NULL,
	CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
	(
		[CustomerID] ASC
	)
)
 ";
				command.ExecuteNonQuery();
			}
		}
	}
	
	public class BenchmarkResult
	{
		public string Action { get; set; }
		public int Entities { get; set; }
		public string Performance { get; set; }
	}
}
;