Skip to content

Commit dbb8aa4

Browse files
author
fabien.menager
committed
Merge branch 'master' into feature/provider-options
# Conflicts: # src/PhenX.EntityFrameworkCore.BulkInsert.MySql/MySqlBulkInsertProvider.cs # src/PhenX.EntityFrameworkCore.BulkInsert.SqlServer/SqlServerBulkInsertProvider.cs # src/PhenX.EntityFrameworkCore.BulkInsert.Sqlite/SqliteBulkInsertProvider.cs # src/PhenX.EntityFrameworkCore.BulkInsert/Abstractions/IBulkInsertProvider.cs # src/PhenX.EntityFrameworkCore.BulkInsert/BulkInsertProviderBase.cs # src/PhenX.EntityFrameworkCore.BulkInsert/PhenX.EntityFrameworkCore.BulkInsert.csproj
2 parents f037a4c + 1926334 commit dbb8aa4

25 files changed

Lines changed: 497 additions & 278 deletions

src/PhenX.EntityFrameworkCore.BulkInsert.MySql/MySqlBulkInsertProvider.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using MySqlConnector;
66

7+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
78
using PhenX.EntityFrameworkCore.BulkInsert.Options;
89

910
namespace PhenX.EntityFrameworkCore.BulkInsert.MySql;
@@ -32,6 +33,7 @@ public MySqlBulkInsertProvider(ILogger<MySqlBulkInsertProvider>? logger = null)
3233
public override Task<List<T>> BulkInsertReturnEntities<T>(
3334
bool sync,
3435
DbContext context,
36+
TableMetadata tableInfo,
3537
IEnumerable<T> entities,
3638
BulkInsertOptions options,
3739
OnConflictOptions? onConflict = null,
@@ -44,9 +46,10 @@ public override Task<List<T>> BulkInsertReturnEntities<T>(
4446
protected override async Task BulkInsert<T>(
4547
bool sync,
4648
DbContext context,
49+
TableMetadata tableInfo,
4750
IEnumerable<T> entities,
4851
string tableName,
49-
PropertyAccessor[] properties,
52+
IReadOnlyList<PropertyMetadata> properties,
5053
MySqlBulkInsertOptions options,
5154
CancellationToken ctk
5255
)
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.EntityFrameworkCore;
2-
using Microsoft.EntityFrameworkCore.Infrastructure;
2+
3+
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
34

45
namespace PhenX.EntityFrameworkCore.BulkInsert.MySql;
56

@@ -13,10 +14,6 @@ public static class MySqlDbContextOptionsExtensions
1314
/// </summary>
1415
public static DbContextOptionsBuilder UseBulkInsertMySql(this DbContextOptionsBuilder optionsBuilder)
1516
{
16-
var extension = optionsBuilder.Options.FindExtension<BulkInsertOptionsExtension<MySqlBulkInsertProvider>>() ?? new BulkInsertOptionsExtension<MySqlBulkInsertProvider>();
17-
18-
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
19-
20-
return optionsBuilder;
17+
return optionsBuilder.UseProvider<MySqlBulkInsertProvider>();
2118
}
2219
}

src/PhenX.EntityFrameworkCore.BulkInsert.MySql/MySqlDialectBuilder.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using System.Text;
22

3-
using Microsoft.EntityFrameworkCore;
4-
using Microsoft.EntityFrameworkCore.Metadata;
5-
63
using PhenX.EntityFrameworkCore.BulkInsert.Dialect;
4+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
75
using PhenX.EntityFrameworkCore.BulkInsert.Options;
86

97
namespace PhenX.EntityFrameworkCore.BulkInsert.MySql;
@@ -43,9 +41,9 @@ protected override void AppendOnConflictStatement(StringBuilder sql)
4341
sql.Append("ON DUPLICATE KEY");
4442
}
4543

46-
protected override void AppendDoNothing(StringBuilder sql, IProperty[] insertedProperties)
44+
protected override void AppendDoNothing(StringBuilder sql, IEnumerable<PropertyMetadata> insertedProperties)
4745
{
48-
var columnName = insertedProperties[0].GetColumnName();
46+
var columnName = insertedProperties.First().ColumnName;
4947

5048
sql.Append($"UPDATE {Quote(columnName)} = {GetExcludedColumnName(columnName)}");
5149
}

src/PhenX.EntityFrameworkCore.BulkInsert.PostgreSql/PostgreSqlBulkInsertProvider.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using Npgsql;
77

8+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
89
using PhenX.EntityFrameworkCore.BulkInsert.Options;
910

1011
namespace PhenX.EntityFrameworkCore.BulkInsert.PostgreSql;
@@ -24,9 +25,9 @@ public PostgreSqlBulkInsertProvider(ILogger<PostgreSqlBulkInsertProvider>? logge
2425
/// <inheritdoc />
2526
protected override string AddTableCopyBulkInsertId => $"ALTER TABLE {{0}} ADD COLUMN {BulkInsertId} SERIAL PRIMARY KEY;";
2627

27-
private string GetBinaryImportCommand(DbContext context, Type entityType, string tableName)
28+
private static string GetBinaryImportCommand(TableMetadata tableInfo, string tableName)
2829
{
29-
var columns = GetQuotedColumns(context, entityType, false);
30+
var columns = tableInfo.GetProperties(false).Select(X => X.QuotedColumName);
3031

3132
return $"COPY {tableName} ({string.Join(", ", columns)}) FROM STDIN (FORMAT BINARY)";
3233
}
@@ -41,15 +42,16 @@ private string GetBinaryImportCommand(DbContext context, Type entityType, string
4142
protected override async Task BulkInsert<T>(
4243
bool sync,
4344
DbContext context,
45+
TableMetadata tableInfo,
4446
IEnumerable<T> entities,
4547
string tableName,
46-
PropertyAccessor[] properties,
48+
IReadOnlyList<PropertyMetadata> properties,
4749
BulkInsertOptions options,
48-
CancellationToken ctk) where T : class
50+
CancellationToken ctk)
4951
{
5052
var connection = (NpgsqlConnection)context.Database.GetDbConnection();
5153

52-
var importCommand = GetBinaryImportCommand(context, typeof(T), tableName);
54+
var importCommand = GetBinaryImportCommand(tableInfo, tableName);
5355

5456
var writer = sync
5557
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using Microsoft.EntityFrameworkCore;
2-
using Microsoft.EntityFrameworkCore.Infrastructure;
1+
using Microsoft.EntityFrameworkCore;
2+
3+
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
34

45
namespace PhenX.EntityFrameworkCore.BulkInsert.PostgreSql;
56

@@ -13,10 +14,6 @@ public static class PostgreSqlDbContextOptionsExtensions
1314
/// </summary>
1415
public static DbContextOptionsBuilder UseBulkInsertPostgreSql(this DbContextOptionsBuilder optionsBuilder)
1516
{
16-
var extension = optionsBuilder.Options.FindExtension<BulkInsertOptionsExtension<PostgreSqlBulkInsertProvider>>() ?? new BulkInsertOptionsExtension<PostgreSqlBulkInsertProvider>();
17-
18-
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
19-
20-
return optionsBuilder;
17+
return optionsBuilder.UseProvider<PostgreSqlBulkInsertProvider>();
2118
}
2219
}

src/PhenX.EntityFrameworkCore.BulkInsert.SqlServer/SqlServerBulkInsertProvider.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.EntityFrameworkCore.Storage;
66
using Microsoft.Extensions.Logging;
77

8+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
89
using PhenX.EntityFrameworkCore.BulkInsert.Options;
910

1011
namespace PhenX.EntityFrameworkCore.BulkInsert.SqlServer;
@@ -36,12 +37,12 @@ public SqlServerBulkInsertProvider(ILogger<SqlServerBulkInsertProvider>? logger
3637
protected override async Task BulkInsert<T>(
3738
bool sync,
3839
DbContext context,
40+
TableMetadata tableInfo,
3941
IEnumerable<T> entities,
4042
string tableName,
41-
PropertyAccessor[] properties,
43+
IReadOnlyList<PropertyMetadata> properties,
4244
SqlServerBulkInsertOptions options,
43-
CancellationToken ctk
44-
)
45+
CancellationToken ctk)
4546
{
4647
var connection = (SqlConnection) context.Database.GetDbConnection();
4748
var sqlTransaction = context.Database.CurrentTransaction!.GetDbTransaction() as SqlTransaction;
Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using Microsoft.EntityFrameworkCore;
2-
using Microsoft.EntityFrameworkCore.Infrastructure;
1+
using Microsoft.EntityFrameworkCore;
2+
3+
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
34

45
namespace PhenX.EntityFrameworkCore.BulkInsert.SqlServer;
56

@@ -13,10 +14,6 @@ public static class SqlServerDbContextOptionsExtensions
1314
/// </summary>
1415
public static DbContextOptionsBuilder UseBulkInsertSqlServer(this DbContextOptionsBuilder optionsBuilder)
1516
{
16-
var extension = optionsBuilder.Options.FindExtension<BulkInsertOptionsExtension<SqlServerBulkInsertProvider>>() ?? new BulkInsertOptionsExtension<SqlServerBulkInsertProvider>();
17-
18-
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
19-
20-
return optionsBuilder;
17+
return optionsBuilder.UseProvider<SqlServerBulkInsertProvider>();
2118
}
2219
}

src/PhenX.EntityFrameworkCore.BulkInsert.SqlServer/SqlServerDialectBuilder.cs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
using System.Linq.Expressions;
21
using System.Text;
32

4-
using Microsoft.EntityFrameworkCore;
5-
using Microsoft.EntityFrameworkCore.Metadata;
6-
73
using PhenX.EntityFrameworkCore.BulkInsert.Dialect;
4+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
85
using PhenX.EntityFrameworkCore.BulkInsert.Options;
96

107
namespace PhenX.EntityFrameworkCore.BulkInsert.SqlServer;
@@ -17,37 +14,39 @@ internal class SqlServerDialectBuilder : SqlDialectBuilder
1714

1815
protected override bool SupportsMoveRows => false;
1916

20-
public override string BuildMoveDataSql<T>(DbContext context, string source,
21-
string target,
22-
IProperty[] insertedProperties,
23-
IProperty[] properties,
24-
BulkInsertOptions options, OnConflictOptions? onConflict = null)
17+
public override string BuildMoveDataSql<T>(
18+
TableMetadata target,
19+
string source,
20+
IReadOnlyList<PropertyMetadata> insertedProperties,
21+
IReadOnlyList<PropertyMetadata> properties,
22+
BulkInsertOptions options,
23+
OnConflictOptions? onConflict = null)
2524
{
26-
var insertedColumns = insertedProperties.Select(p => Quote(p.GetColumnName())).ToArray();
25+
var insertedColumns = insertedProperties.Select(x => x.QuotedColumName);
2726
var insertedColumnList = string.Join(", ", insertedColumns);
2827

29-
var returnedColumns = properties.Select(p => $"INSERTED.{p.GetColumnName()} AS {p.GetColumnName()}");
28+
var returnedColumns = properties.Select(p => $"INSERTED.{p.ColumnName} AS {p.ColumnName}");
3029
var columnList = string.Join(", ", returnedColumns);
3130

3231
var q = new StringBuilder();
3332

3433
if (options.CopyGeneratedColumns)
3534
{
36-
q.AppendLine($"SET IDENTITY_INSERT {target} ON;");
35+
q.AppendLine($"SET IDENTITY_INSERT {target.QuotedTableName} ON;");
3736
}
3837

3938
// Merge handling
4039
if (onConflict is OnConflictOptions<T> onConflictTyped && onConflictTyped.Match != null)
4140
{
42-
var matchColumns = GetColumns(context, onConflictTyped.Match);
41+
var matchColumns = GetColumns(target, onConflictTyped.Match);
4342
var matchOn = string.Join(" AND ",
4443
matchColumns.Select(col => $"TARGET.{col} = SOURCE.{col}"));
4544

4645
var updateSet = onConflictTyped.Update != null
47-
? string.Join(", ", GetUpdates(context, insertedProperties, onConflictTyped.Update))
46+
? string.Join(", ", GetUpdates(target, insertedProperties, onConflictTyped.Update))
4847
: null;
4948

50-
q.AppendLine($"MERGE INTO {target} AS TARGET");
49+
q.AppendLine($"MERGE INTO {target.QuotedTableName} AS TARGET");
5150
q.AppendLine(
5251
$"USING (SELECT {string.Join(", ", insertedColumns)} FROM {source}) AS SOURCE ({insertedColumnList})");
5352
q.AppendLine($"ON {matchOn}");
@@ -69,7 +68,7 @@ public override string BuildMoveDataSql<T>(DbContext context, string source,
6968
// No conflict handling
7069
else
7170
{
72-
q.AppendLine($"INSERT INTO {target} ({insertedColumnList})");
71+
q.AppendLine($"INSERT INTO {target.QuotedTableName} ({insertedColumnList})");
7372

7473
if (columnList.Length != 0)
7574
{
@@ -86,7 +85,7 @@ public override string BuildMoveDataSql<T>(DbContext context, string source,
8685

8786
if (options.CopyGeneratedColumns)
8887
{
89-
q.AppendLine($"SET IDENTITY_INSERT {target} OFF;");
88+
q.AppendLine($"SET IDENTITY_INSERT {target.QuotedTableName} OFF;");
9089
}
9190

9291
return q.ToString();

src/PhenX.EntityFrameworkCore.BulkInsert.Sqlite/SqliteBulkInsertProvider.cs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Microsoft.EntityFrameworkCore;
77
using Microsoft.Extensions.Logging;
88

9-
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
9+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
1010
using PhenX.EntityFrameworkCore.BulkInsert.Options;
1111

1212
namespace PhenX.EntityFrameworkCore.BulkInsert.Sqlite;
@@ -87,18 +87,18 @@ private static SqliteType GetSqliteType(Type clrType)
8787
throw new InvalidOperationException("Unknown Sqlite type for " + clrType);
8888
}
8989

90-
private DbCommand GetInsertCommand(DbContext context, Type entityType, string tableName,
90+
private DbCommand GetInsertCommand(DbContext context, TableMetadata tableInfo, string tableName,
9191
BulkInsertOptions options,
9292
int batchSize)
9393
{
94-
var columns = context.GetProperties(entityType, options.CopyGeneratedColumns);
94+
var columns = tableInfo.GetProperties(options.CopyGeneratedColumns);
9595
var cmd = context.Database.GetDbConnection().CreateCommand();
9696

9797
var sqliteColumns = columns
9898
.Select(c => new
9999
{
100-
Name = c.GetColumnName(),
101-
Type = GetSqliteType(c.GetProviderClrType() ?? c.ClrType)
100+
Name = c.ColumnName,
101+
Type = GetSqliteType(c.ProviderClrType ?? c.ClrType)
102102
})
103103
.ToArray();
104104

@@ -132,18 +132,19 @@ private DbCommand GetInsertCommand(DbContext context, Type entityType, string ta
132132
protected override async Task BulkInsert<T>(
133133
bool sync,
134134
DbContext context,
135+
TableMetadata tableInfo,
135136
IEnumerable<T> entities,
136137
string tableName,
137-
PropertyAccessor[] properties,
138+
IReadOnlyList<PropertyMetadata> properties,
138139
BulkInsertOptions options,
139140
CancellationToken ctk
140141
) where T : class
141142
{
142143
const int maxParams = 1000;
143144
var batchSize = options.BatchSize;
144-
batchSize = Math.Min(batchSize, maxParams / properties.Length);
145+
batchSize = Math.Min(batchSize, maxParams / properties.Count);
145146

146-
await using var insertCommand = GetInsertCommand(context, typeof(T), tableName, options, batchSize);
147+
await using var insertCommand = GetInsertCommand(context, tableInfo, tableName, options, batchSize);
147148

148149
foreach (var chunk in entities.Chunk(batchSize))
149150
{
@@ -156,7 +157,7 @@ CancellationToken ctk
156157
// Last chunk
157158
else
158159
{
159-
var partialInsertCommand = GetInsertCommand(context, typeof(T), tableName, options, chunk.Length);
160+
var partialInsertCommand = GetInsertCommand(context, tableInfo, tableName, options, chunk.Length);
160161

161162
FillValues(chunk, partialInsertCommand.Parameters, properties);
162163
await ExecuteCommand(sync, partialInsertCommand, ctk);
@@ -177,7 +178,7 @@ private static async Task ExecuteCommand(bool sync, DbCommand insertCommand, Can
177178
}
178179
}
179180

180-
private static void FillValues<T>(T[] chunk, DbParameterCollection parameters, PropertyAccessor[] properties) where T : class
181+
private static void FillValues<T>(T[] chunk, DbParameterCollection parameters, IReadOnlyList<PropertyMetadata> properties) where T : class
181182
{
182183
var index = 0;
183184
foreach (var entity in chunk)

src/PhenX.EntityFrameworkCore.BulkInsert.Sqlite/SqliteDbContextOptionsExtensions.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.EntityFrameworkCore;
2-
using Microsoft.EntityFrameworkCore.Infrastructure;
2+
3+
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
34

45
namespace PhenX.EntityFrameworkCore.BulkInsert.Sqlite;
56

@@ -13,9 +14,7 @@ public static class SqliteDbContextOptionsExtensions
1314
/// </summary>
1415
public static DbContextOptionsBuilder UseBulkInsertSqlite(this DbContextOptionsBuilder optionsBuilder)
1516
{
16-
var extension = optionsBuilder.Options.FindExtension<BulkInsertOptionsExtension<SqliteBulkInsertProvider>>() ?? new BulkInsertOptionsExtension<SqliteBulkInsertProvider>();
17-
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
18-
return optionsBuilder;
17+
return optionsBuilder.UseProvider<SqliteBulkInsertProvider>();
1918
}
2019
}
2120

0 commit comments

Comments
 (0)