Skip to content

Commit 3ccb3cd

Browse files
author
Cesar Romero Silva
committed
+ Bulk Update
1 parent 4c5921a commit 3ccb3cd

13 files changed

+240
-54
lines changed

App.config

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<configuration>
3-
<startup>
4-
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
5-
</startup>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
5+
</startup>
66
</configuration>

BulkOperation.cs

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,85 @@
55

66
namespace BulkOperations
77
{
8-
/* TODO
9-
* https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy?redirectedfrom=MSDN&view=netframework-4.7.2
10-
* https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy.writetoserver?view=netframework-4.7.2&viewFallbackFrom=netcore-2.1
11-
* https://docs.microsoft.com/pt-br/dotnet/framework/data/adonet/sql/single-bulk-copy-operations
12-
* https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/bulk-copy-example-setup?view=netframework-4.7.2
13-
* https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DEN-US%26k%3Dk(System.Data.SqlClient.SqlBulkCopy);k(SolutionItemsProject);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)%26rd%3Dtrue&view=netframework-4.7.2
14-
* https://github.com/mgravell/fast-member
15-
*
16-
* Update if exist https://stackoverflow.com/questions/12521692/c-sharp-bulk-insert-sqlbulkcopy-update-if-exists/12535726
17-
* https://stackoverflow.com/questions/33027246/insert-object-or-update-if-it-already-exists-using-bulkcopy-c-sql
18-
* https://www.databasejournal.com/features/mssql/article.php/3739131/UPSERT-Functionality-in-SQL-Server-2008.htm
19-
* https://stackoverflow.com/questions/4889123/any-way-to-sqlbulkcopy-insert-or-update-if-exists
20-
*/
21-
228
public static class BulkOperation
239
{
24-
private const string ConnectionString = "Data Source=NT-03087;Initial Catalog=Insumos;Integrated Security=False;User ID=sa;Password=sa;Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=true;";
25-
26-
2710
public static async Task InsertAsync(CancellationToken cancellationToken = default(CancellationToken))
2811
{
2912
using (var connection = new SqlConnection())
3013
{
31-
connection.ConnectionString = ConnectionString;
14+
connection.Configure();
3215
await connection.OpenAsync(cancellationToken).ConfigureAwait(true);
3316

34-
using (var bulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, null))
35-
{
36-
var customers = Customer.Generate(1_000_000);
17+
const int recordsToGenerate = 100_000;
18+
Log.Write($"Gerando {recordsToGenerate:n0} registros clientes");
19+
var customers = Customer.Generate(recordsToGenerate);
3720

21+
Log.Write("Inserindo registros no banco de dados");
22+
using (var insertBulk = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, null))
23+
{
3824
using (var customerReader = new ObjectDataReader<Customer>(customers.GetEnumerator()))
3925
{
40-
bulk.MapEntity();
41-
bulk.ConfigureOptions();
42-
bulk.ConfigureLog();
26+
insertBulk.Configure("Customer");
27+
28+
await insertBulk.WriteToServerAsync(customerReader, cancellationToken).ConfigureAwait(false);
29+
}
30+
}
4331

44-
await bulk.WriteToServerAsync(customerReader, cancellationToken).ConfigureAwait(true);
32+
using (var command = new SqlCommand("", connection))
33+
{
34+
try
35+
{
36+
Log.Write("Criando tabela temporária e copiando registros");
37+
command.CommandText = BulkUpdate.CreateTempTable;
38+
command.ExecuteNonQuery();
39+
try
40+
{
41+
Log.Write("Atualizando registros: Tabela temporária -> Customers");
42+
command.CommandTimeout = 300;
43+
command.CommandText = BulkUpdate.UpdateTable;
44+
command.ExecuteNonQuery();
45+
}
46+
finally
47+
{
48+
Log.Write("Excluindo tabela temporária");
49+
command.CommandText = BulkUpdate.DropTempTable;
50+
command.ExecuteNonQuery();
51+
}
52+
}
53+
catch (Exception e)
54+
{
55+
Log.Write(e.Message);
4556
}
4657
}
4758
}
4859
}
4960

50-
private static void MapEntity(this SqlBulkCopy bulk)
61+
private static void Configure(this SqlBulkCopy bulk, string tableName)
5162
{
52-
// https://stackoverflow.com/questions/40470357/importing-a-csv-using-sqlbulkcopy-with-asp-net-core
53-
bulk.DestinationTableName = "Customer";
54-
bulk.ColumnMappings.Add(nameof(Customer.Id), "Id" );
55-
bulk.ColumnMappings.Add(nameof(Customer.FirstName ), "FirstName");
56-
bulk.ColumnMappings.Add(nameof(Customer.LastName ), "LastName");
63+
bulk.MapEntity(tableName);
64+
bulk.ConfigureOptions();
65+
bulk.ConfigureLog();
66+
}
67+
68+
private static void MapEntity(this SqlBulkCopy bulk, string tableName)
69+
{
70+
bulk.DestinationTableName = tableName;
71+
bulk.ColumnMappings.Add(nameof(Customer.Id), "Id");
72+
bulk.ColumnMappings.Add(nameof(Customer.FirstName), "FirstName");
73+
bulk.ColumnMappings.Add(nameof(Customer.LastName), "LastName");
5774
bulk.ColumnMappings.Add(nameof(Customer.DateOfBirth), "DateOfBirth");
5875
}
5976

6077
private static void ConfigureOptions(this SqlBulkCopy bulk)
6178
{
6279
bulk.EnableStreaming = true;
63-
bulk.BatchSize = 10_000;
64-
bulk.NotifyAfter = 1_000;
80+
bulk.BatchSize = 10_000;
81+
bulk.NotifyAfter = 1_000;
6582
}
6683

6784
private static void ConfigureLog(this SqlBulkCopy bulk)
6885
{
69-
bulk.SqlRowsCopied += (sender, e) => Console.WriteLine($"{DateTime.Now} RowsCopied: {e.RowsCopied:n0}");
86+
bulk.SqlRowsCopied += (sender, e) => Log.Write($"{DateTime.Now} RowsCopied: {e.RowsCopied:n0}");
7087
}
71-
7288
}
73-
}
89+
}

BulkOperations.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
</PropertyGroup>
3535
<ItemGroup>
3636
<Reference Include="System" />
37+
<Reference Include="System.Configuration" />
3738
<Reference Include="System.Core" />
3839
<Reference Include="System.Xml.Linq" />
3940
<Reference Include="System.Data.DataSetExtensions" />
@@ -44,11 +45,16 @@
4445
</ItemGroup>
4546
<ItemGroup>
4647
<Compile Include="BulkOperation.cs" />
48+
<Compile Include="BulkUpdate.cs" />
49+
<Compile Include="Log.cs" />
4750
<Compile Include="Customer.cs" />
51+
<Compile Include="DataTableExtension.cs" />
4852
<Compile Include="ObjectDataReader.cs" />
4953
<Compile Include="Program.cs" />
5054
<Compile Include="Properties\AssemblyInfo.cs" />
55+
<Compile Include="SqlConnectionExtension.cs" />
5156
<Compile Include="StopwatchExtension.cs" />
57+
<Compile Include="Todo.cs" />
5258
</ItemGroup>
5359
<ItemGroup>
5460
<None Include="App.config" />

BulkUpdate.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Configuration;
4+
using System.Data;
5+
using System.Data.SqlClient;
6+
7+
namespace BulkOperations
8+
{
9+
public static class BulkUpdate
10+
{
11+
public const string TempTableName = "#Customer";
12+
public const string TableName = "Customer";
13+
14+
public const string CreateTempTable =
15+
"select * into #Customer from Customer";
16+
17+
public const string UpdateTable =
18+
@"
19+
update Customer
20+
set
21+
Customer.FirstName = #Customer.FirstName,
22+
Customer.LastName = #Customer.LastName,
23+
Customer.DateOfBirth = #Customer.CreatedAt,
24+
Customer.CreatedAt = #Customer.DateOfBirth
25+
from #Customer
26+
";
27+
28+
public const string DropTempTable =
29+
"drop table #Customer;";
30+
31+
public static void UpdateData<T>(List<T> list)
32+
{
33+
var dataTable = new DataTable(TempTableName);
34+
dataTable.FromList(list);
35+
36+
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
37+
38+
using (var connection = new SqlConnection())
39+
{
40+
connection.Configure();
41+
42+
using (var command = new SqlCommand("", connection))
43+
{
44+
try
45+
{
46+
connection.Open();
47+
48+
command.CommandText = CreateTempTable;
49+
command.ExecuteNonQuery();
50+
51+
using (var bulkcopy = new SqlBulkCopy(connection))
52+
{
53+
bulkcopy.BulkCopyTimeout = 660;
54+
bulkcopy.DestinationTableName = TableName;
55+
bulkcopy.WriteToServer(dataTable);
56+
bulkcopy.Close();
57+
}
58+
59+
command.CommandTimeout = 300;
60+
command.CommandText = $"{UpdateTable}{DropTempTable}";
61+
command.ExecuteNonQuery();
62+
}
63+
catch (Exception e)
64+
{
65+
Console.WriteLine(e.Message);
66+
}
67+
finally
68+
{
69+
connection.Close();
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}

Customer.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
use Insumos;
66
77
create table Customer (
8-
Id uniqueidentifier primary key,
9-
FirstName varchar(100),
10-
LastName varchar(100),
11-
DateOfBirth date,
8+
Id uniqueidentifier primary key,
9+
FirstName varchar(100),
10+
LastName varchar(100),
11+
DateOfBirth date,
1212
CreatedAt datetime2 default getdate()
1313
);
1414
@@ -39,4 +39,4 @@ public static IEnumerable<Customer> Generate(int count)
3939
}
4040
}
4141
}
42-
}
42+
}

DataTableExtension.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Data;
5+
6+
namespace BulkOperations
7+
{
8+
public static class DataTableExtension
9+
{
10+
public static DataTable FromList<T>(this DataTable dataTable, IList<T> list)
11+
{
12+
var properties = TypeDescriptor.GetProperties(typeof(T));
13+
14+
foreach (PropertyDescriptor prop in properties)
15+
{
16+
dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
17+
}
18+
19+
foreach (var item in list)
20+
{
21+
var row = dataTable.NewRow();
22+
23+
foreach (PropertyDescriptor prop in properties)
24+
{
25+
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
26+
}
27+
28+
dataTable.Rows.Add(row);
29+
}
30+
31+
return dataTable;
32+
}
33+
}
34+
}

Log.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace BulkOperations
4+
{
5+
public static class Log
6+
{
7+
public static void Write(string value)
8+
{
9+
Console.WriteLine($"{DateTime.Now} {value}");
10+
}
11+
}
12+
}

ObjectDataReader.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private void Initialize()
4141
var parameterExpression = Expression.Parameter(typeof(T), "x");
4242
var func = (Func<T, object>)Expression.Lambda(
4343
Expression.Convert(
44-
Expression.Property(parameterExpression, propertyName),
44+
Expression.Property(parameterExpression, propertyName),
4545
typeof(object)),
4646
parameterExpression)
4747
.Compile();
@@ -74,7 +74,6 @@ public override object GetValue(int ordinal)
7474
return func(_iterator.Current);
7575
}
7676

77-
7877
// optional
7978
public override int GetValues(object[] values)
8079
{

Program.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,25 @@
33

44
namespace BulkOperations
55
{
6-
static class Program
6+
internal static class Program
77
{
8-
static void Main(string[] args)
8+
private static void Main(string[] args)
99
{
1010
var stopwatch = new Stopwatch();
1111
stopwatch.Start();
1212

13-
Console.WriteLine($"{DateTime.Now} Início processamento");
13+
Log.Write("Início processamento");
1414

1515
BulkOperation.InsertAsync().Wait();
1616

1717
stopwatch.Stop();
1818

19-
Console.WriteLine($"{DateTime.Now} Processamento concluído em {stopwatch.ElapsedTimeFmt()}");
19+
20+
Log.Write($"Processamento concluído em {stopwatch.ElapsedTimeFmt()}");
2021

2122
Console.WriteLine("");
2223
Console.WriteLine("Tecle <ENTER> para finalizar...");
2324
Console.ReadLine();
2425
}
2526
}
26-
}
27+
}

Properties/AssemblyInfo.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Reflection;
2-
using System.Runtime.CompilerServices;
32
using System.Runtime.InteropServices;
43

54
// General Information about an assembly is controlled through the following
@@ -33,4 +32,4 @@
3332
// by using the '*' as shown below:
3433
// [assembly: AssemblyVersion("1.0.*")]
3534
[assembly: AssemblyVersion("1.0.0.0")]
36-
[assembly: AssemblyFileVersion("1.0.0.0")]
35+
[assembly: AssemblyFileVersion("1.0.0.0")]

0 commit comments

Comments
 (0)