Skip to content

Commit 4a6e942

Browse files
Generic base provider #33
1 parent 685ff84 commit 4a6e942

10 files changed

Lines changed: 140 additions & 82 deletions

File tree

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,8 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
5656
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
5757
dotnet_style_predefined_type_for_member_access = true:suggestion
5858

59+
# IDE0090: Use 'new(...)'
60+
dotnet_diagnostic.IDE0090.severity = none
61+
5962
[*.{csproj,proj,targets}]
6063
indent_size = 2

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using JetBrains.Annotations;
2+
13
using Microsoft.EntityFrameworkCore;
24
using Microsoft.EntityFrameworkCore.Storage;
35
using Microsoft.Extensions.Logging;
@@ -9,6 +11,7 @@
911

1012
namespace PhenX.EntityFrameworkCore.BulkInsert.MySql;
1113

14+
[UsedImplicitly]
1215
internal class MySqlBulkInsertProvider(ILogger<MySqlBulkInsertProvider> logger) : BulkInsertProviderBase<MySqlServerDialectBuilder, MySqlBulkInsertOptions>(logger)
1316
{
1417
//language=sql
@@ -22,13 +25,13 @@ internal class MySqlBulkInsertProvider(ILogger<MySqlBulkInsertProvider> logger)
2225
protected override MySqlBulkInsertOptions CreateDefaultOptions() => new();
2326

2427
/// <inheritdoc />
25-
public override IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
28+
protected override IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
2629
bool sync,
2730
DbContext context,
2831
TableMetadata tableInfo,
2932
IEnumerable<T> entities,
30-
BulkInsertOptions options,
31-
OnConflictOptions? onConflict = null,
33+
MySqlBulkInsertOptions options,
34+
OnConflictOptions<T>? onConflict = null,
3235
CancellationToken ctk = default)
3336
{
3437
throw new NotSupportedException("Provider does not support returning entities.");

src/PhenX.EntityFrameworkCore.BulkInsert/Abstractions/IBulkInsertProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
2020
TableMetadata tableInfo,
2121
IEnumerable<T> entities,
2222
BulkInsertOptions options,
23-
OnConflictOptions? onConflict = null,
23+
OnConflictOptions<T>? onConflict = null,
2424
CancellationToken ctk = default
2525
) where T : class;
2626

@@ -33,7 +33,7 @@ internal Task BulkInsert<T>(
3333
TableMetadata tableInfo,
3434
IEnumerable<T> entities,
3535
BulkInsertOptions options,
36-
OnConflictOptions? onConflict = null,
36+
OnConflictOptions<T>? onConflict = null,
3737
CancellationToken ctk = default
3838
) where T : class;
3939

@@ -42,5 +42,5 @@ internal Task BulkInsert<T>(
4242
/// <summary>
4343
/// Make the default options for the provider, can be a subclass of <see cref="BulkInsertOptions"/>.
4444
/// </summary>
45-
internal BulkInsertOptions InternalCreateDefaultOptions();
45+
internal BulkInsertOptions CreateDefaultOptions();
4646
}

src/PhenX.EntityFrameworkCore.BulkInsert/BulkInsertOptionsExtension.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public void Validate(IDbContextOptions options)
2626

2727
private class BulkInsertOptionsExtensionInfo(IDbContextOptionsExtension extension) : DbContextOptionsExtensionInfo(extension)
2828
{
29-
3029
/// <inheritdoc />
3130
public override int GetServiceProviderHashCode() => 0;
3231

src/PhenX.EntityFrameworkCore.BulkInsert/BulkInsertProviderBase.cs

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,32 @@
44
using Microsoft.EntityFrameworkCore.Storage;
55
using Microsoft.Extensions.Logging;
66

7-
using PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
87
using PhenX.EntityFrameworkCore.BulkInsert.Dialect;
98
using PhenX.EntityFrameworkCore.BulkInsert.Extensions;
109
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
1110
using PhenX.EntityFrameworkCore.BulkInsert.Options;
1211

1312
namespace PhenX.EntityFrameworkCore.BulkInsert;
1413

15-
internal abstract class BulkInsertProviderBase<TDialect, TOptions>(ILogger<BulkInsertProviderBase<TDialect, TOptions>> logger) : IBulkInsertProvider
14+
internal abstract class BulkInsertProviderBase<TDialect, TOptions>(ILogger? logger) : BulkInsertProviderUntyped<TDialect, TOptions>
1615
where TDialect : SqlDialectBuilder, new()
1716
where TOptions : BulkInsertOptions, new()
1817
{
19-
protected readonly TDialect SqlDialect = new();
20-
2118
protected virtual string BulkInsertId => "_bulk_insert_id";
2219

2320
protected abstract string AddTableCopyBulkInsertId { get; }
2421

2522
protected virtual string GetTempTableName(string tableName) => $"_temp_bulk_insert_{tableName}";
2623

27-
SqlDialectBuilder IBulkInsertProvider.SqlDialect => SqlDialect;
28-
29-
public BulkInsertOptions InternalCreateDefaultOptions() => CreateDefaultOptions();
30-
31-
/// <summary>
32-
/// Create the default options for the provider, can be a subclass of <see cref="BulkInsertOptions"/>.
33-
/// </summary>
34-
protected abstract TOptions CreateDefaultOptions();
35-
36-
public virtual async IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
24+
protected override async IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
3725
bool sync,
3826
DbContext context,
3927
TableMetadata tableInfo,
4028
IEnumerable<T> entities,
41-
BulkInsertOptions options,
42-
OnConflictOptions? onConflict,
29+
TOptions options,
30+
OnConflictOptions<T>? onConflict,
4331
[EnumeratorCancellation] CancellationToken ctk) where T : class
4432
{
45-
if (options is not TOptions providerOptions)
46-
{
47-
throw new InvalidOperationException($"Invalid options type: {options.GetType().Name}. Expected: {typeof(TOptions).Name}");
48-
}
49-
5033
using var activity = Telemetry.ActivitySource.StartActivity("BulkInsertReturnEntities");
5134
activity?.AddTag("tableName", tableInfo.TableName);
5235
activity?.AddTag("synchronous", sync);
@@ -59,10 +42,10 @@ public virtual async IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
5942
Log.UsingTempTableToReturnData(logger);
6043
}
6144

62-
var tableName = await PerformBulkInsertAsync(sync, context, tableInfo, entities, providerOptions, tempTableRequired: true, ctk: ctk);
45+
var tableName = await PerformBulkInsertAsync(sync, context, tableInfo, entities, options, tempTableRequired: true, ctk: ctk);
6346

6447
var result =
65-
await CopyFromTempTableAsync<T, T>(sync, context, tableInfo, tableName, true, providerOptions, onConflict, ctk: ctk)
48+
await CopyFromTempTableAsync<T, T>(sync, context, tableInfo, tableName, true, options, onConflict, ctk: ctk)
6649
?? throw new InvalidOperationException("Copy returns null enumerable.");
6750

6851
await foreach (var item in result.WithCancellation(ctk))
@@ -79,20 +62,15 @@ await CopyFromTempTableAsync<T, T>(sync, context, tableInfo, tableName, true, pr
7962
}
8063
}
8164

82-
public virtual async Task BulkInsert<T>(
65+
protected override async Task BulkInsert<T>(
8366
bool sync,
8467
DbContext context,
8568
TableMetadata tableInfo,
8669
IEnumerable<T> entities,
87-
BulkInsertOptions options,
88-
OnConflictOptions? onConflict,
70+
TOptions options,
71+
OnConflictOptions<T>? onConflict,
8972
CancellationToken ctk) where T : class
9073
{
91-
if (options is not TOptions providerOptions)
92-
{
93-
throw new InvalidOperationException($"Invalid options type: {options.GetType().Name}. Expected: {typeof(TOptions).Name}");
94-
}
95-
9674
using var activity = Telemetry.ActivitySource.StartActivity("BulkInsert");
9775
activity?.AddTag("tableName", tableInfo.TableName);
9876
activity?.AddTag("synchronous", sync);
@@ -107,9 +85,9 @@ public virtual async Task BulkInsert<T>(
10785
Log.UsingTempTableToResolveConflicts(logger);
10886
}
10987

110-
var tableName = await PerformBulkInsertAsync(sync, context, tableInfo, entities, providerOptions, tempTableRequired: true, ctk: ctk);
88+
var tableName = await PerformBulkInsertAsync(sync, context, tableInfo, entities, options, tempTableRequired: true, ctk: ctk);
11189

112-
await CopyFromTempTableAsync<T, T>(sync, context, tableInfo, tableName, false, providerOptions, onConflict, ctk);
90+
await CopyFromTempTableAsync<T, T>(sync, context, tableInfo, tableName, false, options, onConflict, ctk);
11391
}
11492
else
11593
{
@@ -118,7 +96,7 @@ public virtual async Task BulkInsert<T>(
11896
Log.UsingDirectInsert(logger);
11997
}
12098

121-
await PerformBulkInsertAsync(sync, context, tableInfo, entities, providerOptions, tempTableRequired: false, ctk: ctk);
99+
await PerformBulkInsertAsync(sync, context, tableInfo, entities, options, tempTableRequired: false, ctk: ctk);
122100
}
123101

124102
// Commit the transaction if we own them.
@@ -207,7 +185,7 @@ protected virtual async Task AddBulkInsertIdColumn<T>(
207185
string tempTableName,
208186
bool returnData,
209187
TOptions options,
210-
OnConflictOptions? onConflict,
188+
OnConflictOptions<T>? onConflict,
211189
CancellationToken ctk) where T : class where TResult : class
212190
{
213191
var query =
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
using PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
4+
using PhenX.EntityFrameworkCore.BulkInsert.Dialect;
5+
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
6+
using PhenX.EntityFrameworkCore.BulkInsert.Options;
7+
8+
namespace PhenX.EntityFrameworkCore.BulkInsert;
9+
10+
internal abstract class BulkInsertProviderUntyped<TDialect, TOptions> : IBulkInsertProvider
11+
where TDialect : SqlDialectBuilder, new()
12+
where TOptions : BulkInsertOptions, new()
13+
{
14+
protected readonly TDialect SqlDialect = new();
15+
16+
SqlDialectBuilder IBulkInsertProvider.SqlDialect => SqlDialect;
17+
18+
BulkInsertOptions IBulkInsertProvider.CreateDefaultOptions() => CreateDefaultOptions();
19+
20+
/// <summary>
21+
/// Create the default options for the provider, can be a subclass of <see cref="BulkInsertOptions"/>.
22+
/// </summary>
23+
protected abstract TOptions CreateDefaultOptions();
24+
25+
public IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
26+
bool sync,
27+
DbContext context,
28+
TableMetadata tableInfo,
29+
IEnumerable<T> entities,
30+
BulkInsertOptions options,
31+
OnConflictOptions<T>? onConflict,
32+
CancellationToken ctk) where T : class
33+
{
34+
if (options is not TOptions providerOptions)
35+
{
36+
throw new InvalidOperationException($"Invalid options type: {options.GetType().Name}. Expected: {typeof(TOptions).Name}");
37+
}
38+
39+
return BulkInsertReturnEntities(sync, context, tableInfo, entities, providerOptions, onConflict, ctk);
40+
}
41+
42+
protected abstract IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
43+
bool sync,
44+
DbContext context,
45+
TableMetadata tableInfo,
46+
IEnumerable<T> entities,
47+
TOptions options,
48+
OnConflictOptions<T>? onConflict,
49+
CancellationToken ctk) where T : class;
50+
51+
public Task BulkInsert<T>(
52+
bool sync,
53+
DbContext context,
54+
TableMetadata tableInfo,
55+
IEnumerable<T> entities,
56+
BulkInsertOptions options,
57+
OnConflictOptions<T>? onConflict,
58+
CancellationToken ctk) where T : class
59+
{
60+
if (options is not TOptions providerOptions)
61+
{
62+
throw new InvalidOperationException($"Invalid options type: {options.GetType().Name}. Expected: {typeof(TOptions).Name}");
63+
}
64+
65+
return BulkInsert(sync, context, tableInfo, entities, providerOptions, onConflict, ctk);
66+
}
67+
68+
protected abstract Task BulkInsert<T>(
69+
bool sync,
70+
DbContext context,
71+
TableMetadata tableInfo,
72+
IEnumerable<T> entities,
73+
TOptions options,
74+
OnConflictOptions<T>? onConflict,
75+
CancellationToken ctk) where T : class;
76+
}

src/PhenX.EntityFrameworkCore.BulkInsert/Dialect/SqlDialectBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public string QuoteTableName(string? schema, string tableName)
154154
/// <summary>
155155
/// Gets column names for the insert statement, from an object initializer.
156156
/// </summary>
157-
protected string[] GetColumns<T>(TableMetadata table, Expression<Func<T, object>> columns)
157+
protected static string[] GetColumns<T>(TableMetadata table, Expression<Func<T, object>> columns)
158158
{
159159
return columns.Body switch
160160
{

0 commit comments

Comments
 (0)