@manhng

Welcome to my blog!

Bulk Insert

July 9, 2018 15:39

Quickly insert millions of rows in SQL Server in .NET (edit)

https://www.softfluent.com/blog/dev/Quickly-insert-millions-of-rows-in-SQL-Server-in-NET

Ghi chú

Sổ chi tiết công nợ của một khách hàng
Sổ cái của một tài khoản
Bảng cân đối phát sinh công nợ của một tài khoản

https://docs.microsoft.com/en-us/aspnet/core/tutorials/index?view=aspnetcore-2.1#how-to-download-a-sample
https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/app-state/samples
https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/app-state/samples/2.x/SessionSample

Bulk Insert
https://chsakell.com/2014/07/13/insert-millions-of-records-in-sql-server-table-at-once/
https://blogs.msdn.microsoft.com/nikhilsi/2008/06/11/bulk-insert-into-sql-from-c-app/
https://www.codeproject.com/Articles/439843/Handling-BULK-Data-insert-from-CSV-to-SQL-Server
https://programmingwithmosh.com/csharp/using-sqlbulkcopy-for-fast-inserts/
https://www.codemag.com/article/1701101/Processing-Large-Datasets-Using-C

ObjectDataReader (BulkInsert)

https://gist.github.com/meziantou/174e2791dec966be837746750b87d069

1) SQL SCRIPT

CREATE TABLE [dbo].[Customer](
	[Id] [uniqueidentifier] NOT NULL,
	[FirstName] [nvarchar](50) NULL,
	[LastName] [nvarchar](50) NULL,
	[DateOfBirth] [datetime2](7) NULL
) ON [PRIMARY]
GO

2) Class Library: ObjectDataReader.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq.Expressions;

namespace DOTNETCoreClassLibrary1
{
    public class ObjectDataReader : DbDataReader
    {
        private IEnumerator _iterator;
        private IDictionary<string, int> _propertyNameToOrdinal = new Dictionary<string, int>();
        private IDictionary<int, string> _ordinalToPropertyName = new Dictionary<int, string>();
        private Func<T, object>[] _getPropertyValueFuncs;

        public ObjectDataReader(IEnumerator items)
        {
            _iterator = items ?? throw new ArgumentNullException(nameof(items));

            Initialize();
        }

        private void Initialize()
        {
            int ordinal = 0;
            var properties = typeof(T).GetProperties();
            _getPropertyValueFuncs = new Func<T, object>[properties.Length];
            foreach (var property in properties)
            {
                string propertyName = property.Name;
                _propertyNameToOrdinal.Add(propertyName, ordinal);
                _ordinalToPropertyName.Add(ordinal, propertyName);

                var parameterExpression = Expression.Parameter(typeof(T), "x");
                var func = (Func<T, object>)Expression.Lambda(Expression.Convert(Expression.Property(parameterExpression, propertyName), typeof(object)), parameterExpression).Compile();
                _getPropertyValueFuncs[ordinal] = func;

                ordinal++;
            }
        }

        public override object this[int ordinal] => GetValue(ordinal);

        public override object this[string name] => GetValue(GetOrdinal(name));

        public override int Depth => 0;

        public override int FieldCount => _ordinalToPropertyName.Count;

        public override bool HasRows => true;

        public override bool IsClosed => _iterator != null;

        public override int RecordsAffected => throw new NotImplementedException();

        public override bool GetBoolean(int ordinal)
        {
            return (bool)GetValue(ordinal);
        }

        public override byte GetByte(int ordinal)
        {
            return (byte)GetValue(ordinal);
        }

        public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
        {
            throw new NotImplementedException();
        }

        public override char GetChar(int ordinal)
        {
            return (char)GetValue(ordinal);
        }

        public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
        {
            throw new NotImplementedException();
        }

        public override string GetDataTypeName(int ordinal)
        {
            throw new NotImplementedException();
        }

        public override DateTime GetDateTime(int ordinal)
        {
            return (DateTime)GetValue(ordinal);
        }

        public override decimal GetDecimal(int ordinal)
        {
            return (decimal)GetValue(ordinal);
        }

        public override double GetDouble(int ordinal)
        {
            return (double)GetValue(ordinal);
        }

        public override IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }

        public override Type GetFieldType(int ordinal)
        {
            var value = GetValue(ordinal);
            if (value == null)
                return typeof(object);

            return value.GetType();
        }

        public override float GetFloat(int ordinal)
        {
            return (float)GetValue(ordinal);
        }

        public override Guid GetGuid(int ordinal)
        {
            return (Guid)GetValue(ordinal);
        }

        public override short GetInt16(int ordinal)
        {
            return (short)GetValue(ordinal);
        }

        public override int GetInt32(int ordinal)
        {
            return (int)GetValue(ordinal);
        }

        public override long GetInt64(int ordinal)
        {
            return (long)GetValue(ordinal);
        }

        public override string GetName(int ordinal)
        {
            if (_ordinalToPropertyName.TryGetValue(ordinal, out var name))
                return name;

            return null;
        }

        public override int GetOrdinal(string name)
        {
            if (_propertyNameToOrdinal.TryGetValue(name, out var ordinal))
                return ordinal;

            return -1;
        }

        public override string GetString(int ordinal)
        {
            return (string)GetValue(ordinal);
        }

        public override object GetValue(int ordinal)
        {
            var func = _getPropertyValueFuncs[ordinal];
            return func(_iterator.Current);
        }

        public override int GetValues(object[] values)
        {
            int max = Math.Min(values.Length, FieldCount);
            for (var i = 0; i < max; i++)
            {
                values[i] = IsDBNull(i) ? DBNull.Value : GetValue(i);
            }

            return max;
        }

        public override bool IsDBNull(int ordinal)
        {
            return GetValue(ordinal) == null;
        }

        public override bool NextResult()
        {
            return false;
        }

        public override bool Read()
        {
            return _iterator.MoveNext();
        }
    }
}

3) Models: Customer.cs

using System;
using System.Collections.Generic;

namespace DOTNETCoreClassLibrary1
{
    public class Customer
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; }

        public static IEnumerable Generate(int count)
        {
            for (int i = 0; i < count; i++)
            {
                yield return new Customer
                {
                    Id = Guid.NewGuid(),
                    FirstName = "FirstName" + i,
                    LastName = "LastName" + i,
                    DateOfBirth = DateTime.UtcNow
                };
            }
        }

        public static IEnumerable Generate(int start, int count)
        {
            for (int i = start; i < count; i++)
            {
                yield return new Customer
                {
                    Id = Guid.NewGuid(),
                    FirstName = "FirstName" + i,
                    LastName = "LastName" + i,
                    DateOfBirth = DateTime.UtcNow
                };
            }
        }
    }
}

4) Controllers: HomeController.cs

using ASPNETCoreWebApplication1.Models;
using DOTNETCoreClassLibrary1;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace ASPNETCoreWebApplication1.Controllers
{
    public class HomeController : Controller
    {
        private readonly string _connectionString = "";

        public HomeController(IConfiguration configuration)
        {
            _connectionString = configuration.GetConnectionString("DefaultConnection");
        }

        public async Task Index()
        {
            await InsertAsync();

            return View();
        }

        private async Task InsertAsync(CancellationToken ct = default(CancellationToken))
        {
            using (var connection = new SqlConnection(this._connectionString))
            {
                await connection.OpenAsync(ct);
                using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, null))
                {
                    var customers = Customer.Generate(1000000);
                    using (var enumerator = customers.GetEnumerator())
                    using (var customerReader = new ObjectDataReader(enumerator))
                    {
                        bulk.DestinationTableName = "Customer";
                        bulk.ColumnMappings.Add(nameof(Customer.Id), "Id");
                        bulk.ColumnMappings.Add(nameof(Customer.FirstName), "FirstName");
                        bulk.ColumnMappings.Add(nameof(Customer.LastName), "LastName");
                        bulk.ColumnMappings.Add(nameof(Customer.DateOfBirth), "DateOfBirth");

                        bulk.EnableStreaming = true;
                        bulk.BatchSize = 10000;
                        bulk.NotifyAfter = 1000;
                        bulk.SqlRowsCopied += (sender, e) => Console.WriteLine("RowsCopied: " + e.RowsCopied);

                        await bulk.WriteToServerAsync(customerReader, ct);
                    }
                }
            }
        }
    }
}

Categories

Recent posts