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);
}
}
}
}
}
}