Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class MySqlBulkInsertProvider(ILogger<MySqlBulkInsertProvider> logger)
protected override string AddTableCopyBulkInsertId => $"ALTER TABLE {{0}} ADD {BulkInsertId} INT AUTO_INCREMENT PRIMARY KEY;";

/// <inheritdoc />
protected override string GetTempTableName(string tableName) => $"#_temp_bulk_insert_{tableName}";
protected override string GetTempTableName(string tableName) => $"#_temp_bulk_insert_{tableName}_{Helpers.RandomString(6)}";

/// <inheritdoc />
protected override MySqlBulkInsertOptions CreateDefaultOptions() => new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class OracleBulkInsertProvider(ILogger<OracleBulkInsertProvider>? logge
/// The temporary table name is generated with a GUID to ensure uniqueness, but limited to less than 30 characters,
/// because Oracle prior 12.2 has a limit of 30 characters for identifiers.
Comment thread
PhenX marked this conversation as resolved.
Outdated
/// </summary>
protected override string GetTempTableName(string tableName) => $"#temp_bulk_insert_{Guid.NewGuid().ToString("N")[..8]}";
protected override string GetTempTableName(string tableName) => $"#temp_bulk_insert__{Helpers.RandomString(6)}";
Comment thread
PhenX marked this conversation as resolved.
Outdated

protected override OracleBulkInsertOptions CreateDefaultOptions() => new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal class SqlServerBulkInsertProvider(ILogger<SqlServerBulkInsertProvider>?
protected override string AddTableCopyBulkInsertId => $"ALTER TABLE {{0}} ADD {BulkInsertId} INT IDENTITY PRIMARY KEY;";

/// <inheritdoc />
protected override string GetTempTableName(string tableName) => $"#_temp_bulk_insert_{tableName}";
protected override string GetTempTableName(string tableName) => $"#_temp_bulk_insert_{tableName}_{Helpers.RandomString(6)}";

protected override SqlServerBulkInsertOptions CreateDefaultOptions() => new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class SqliteBulkInsertProvider(ILogger<SqliteBulkInsertProvider>? logge
protected override string AddTableCopyBulkInsertId => "--"; // No need to add an ID column in SQLite

/// <inheritdoc />
protected override string GetTempTableName(string tableName) => $"_temp_bulk_insert_{Guid.NewGuid():N}";
protected override string GetTempTableName(string tableName) => $"_temp_bulk_insert_{Helpers.RandomString(6)}";

/// <inheritdoc />
protected override BulkInsertOptions CreateDefaultOptions() => new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal abstract class BulkInsertProviderBase<TDialect, TOptions>(ILogger? logg

protected abstract string AddTableCopyBulkInsertId { get; }

protected virtual string GetTempTableName(string tableName) => $"_temp_bulk_insert_{tableName}";
protected virtual string GetTempTableName(string tableName) => $"_temp_bulk_insert_{tableName}_{Helpers.RandomString(6)}";

protected override async IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
bool sync,
Expand Down Expand Up @@ -124,6 +124,7 @@ protected override async Task BulkInsert<T>(
await connection.Close(sync, ctk);
}
}

private async Task<string> PerformBulkInsertAsync<T>(
bool sync,
DbContext context,
Expand Down
19 changes: 19 additions & 0 deletions src/PhenX.EntityFrameworkCore.BulkInsert/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace PhenX.EntityFrameworkCore.BulkInsert;

internal static class Helpers
{
private static readonly Random Random = new();
Comment thread
PhenX marked this conversation as resolved.
Outdated

public static StringBuilder AppendJoin<T>(this StringBuilder sb, string separator, IEnumerable<T> items, Action<StringBuilder, T> formatter)
{
var first = true;
Expand All @@ -27,4 +29,21 @@ public static StringBuilder AppendColumns(this StringBuilder sb, IReadOnlyList<C
{
return sb.AppendJoin(", ", columns.Select(c => c.QuotedColumName));
}

/// <summary>
/// Generates a random alphanumeric string of the specified length.
/// </summary>
public static string RandomString(int length)
{
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";

var sb = new StringBuilder(length);

for (var i = 0; i < length; i++)
{
sb.Append(chars[Random.Next(chars.Length)]);
}

return sb.ToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,34 @@ public async Task InsertEntities_WithOpenTransaction_RollsBackOnFailure(InsertSt
Assert.Empty(insertedEntities);
}

[SkippableTheory]
[CombinatorialData]
public async Task InsertEntities_WithOpenTransaction_MultipleInserts(InsertStrategy strategy)
{
// Oracle: ORA-39822: A new direct path operation is not allowed in the current transaction.
Skip.If(_context.IsProvider(ProviderType.Oracle));

// Arrange
var entities = new List<TestEntity>
{
new TestEntity { TestRun = _run, Name = $"{_run}_EntityWithTx1" },
new TestEntity { TestRun = _run, Name = $"{_run}_EntityWithTx2" },
new TestEntity { TestRun = _run, Name = $"{_run}_EntityWithTx3" },
new TestEntity { TestRun = _run, Name = $"{_run}_EntityWithTx4" },
};

// Act
await using var transaction = await _context.Database.BeginTransactionAsync();

var batches = entities.Chunk(2);
foreach (var batch in batches)
{
await _context.InsertWithStrategyAsync(strategy, batch.ToList());
}

await transaction.CommitAsync();
}

[SkippableFact]
public async Task ThrowsWhenUsingWrongConfigurationType()
{
Expand Down
Loading