Skip to content

Commit

Permalink
Added overloads of "BulkInsert*IntoTempTableAsync" to insert into exi…
Browse files Browse the repository at this point in the history
…sting temp table.
  • Loading branch information
PawelGerr committed Jan 6, 2025
1 parent 8db9cc7 commit 10ebb55
Show file tree
Hide file tree
Showing 11 changed files with 568 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Copyright>(c) $([System.DateTime]::Now.Year), Pawel Gerr. All rights reserved.</Copyright>
<VersionPrefix>8.3.0</VersionPrefix>
<VersionPrefix>8.4.0</VersionPrefix>
<Authors>Pawel Gerr</Authors>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageProjectUrl>https://dev.azure.com/pawelgerr/Thinktecture.EntityFrameworkCore</PackageProjectUrl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ public interface ITempTableBulkInsertExecutor
/// <returns>Options to use with <see cref="ITempTableBulkInsertExecutor"/>.</returns>
ITempTableBulkInsertOptions CreateOptions(IEntityPropertiesProvider? propertiesToInsert = null);

/// <summary>
/// Creates options with default values.
/// </summary>
/// <param name="propertiesToInsert">Properties to insert.</param>
/// <returns>Options to use with <see cref="ITempTableBulkInsertExecutor"/>.</returns>
IBulkInsertOptions CreateBulkInsertOptions(IEntityPropertiesProvider? propertiesToInsert = null);

/// <summary>
/// Inserts the provided <paramref name="entities"/> into a temp table.
/// </summary>
Expand All @@ -40,4 +47,62 @@ Task<ITempTableQuery<TColumn1>> BulkInsertValuesIntoTempTableAsync<TColumn1>(
IEnumerable<TColumn1> values,
ITempTableBulkInsertOptions options,
CancellationToken cancellationToken);

/// <summary>
/// Inserts the provided <paramref name="values"/> into provided <paramref name="tempTable"/>.
/// </summary>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the values.</typeparam>
Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken);

/// <summary>
/// Inserts the provided <paramref name="values"/> into provided <paramref name="tempTable"/>.
/// </summary>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the values.</typeparam>
Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
IBulkInsertOptions? options,
CancellationToken cancellationToken);

/// <summary>
/// Inserts the provided <paramref name="entities"/> into provided <paramref name="tempTable"/>.
/// </summary>
/// <param name="entities">Entities to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="T">Type of the entities.</typeparam>
Task BulkInsertIntoTempTableAsync<T>(
IEnumerable<T> entities,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken)
where T : class;

/// <summary>
/// Inserts the provided <paramref name="entities"/> into provided <paramref name="tempTable"/>.
/// </summary>
/// <param name="entities">Entities to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="T">Type of the entities.</typeparam>
Task BulkInsertIntoTempTableAsync<T>(
IEnumerable<T> entities,
ITempTableReference tempTable,
IBulkInsertOptions? options,
CancellationToken cancellationToken)
where T : class;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ namespace Thinktecture.EntityFrameworkCore.TempTables;
/// Disposal of this query will delete the corresponding temp table.
/// </summary>
/// <typeparam name="T">Type of the query item.</typeparam>
public interface ITempTableQuery<out T> : IAsyncDisposable, IDisposable
public interface ITempTableQuery<out T> : ITempTableReference
{
/// <summary>
/// The query itself.
/// </summary>
IQueryable<T> Query { get; }

/// <summary>
/// The name of the temp table.
/// </summary>
string Name { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,179 @@ public static Task<ITempTableQuery<T>> BulkInsertIntoTempTableAsync<T>(
return executor.BulkInsertIntoTempTableAsync(entities, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="values"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the values to insert.</typeparam>
/// <returns>A query for accessing the inserted values.</returns>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="values"/> is <c>null</c>.</exception>
public static Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
this DbContext ctx,
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(values);

var executor = ctx.GetService<ITempTableBulkInsertExecutor>();

return executor.BulkInsertValuesIntoTempTableAsync(values, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="values"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the values to insert.</typeparam>
/// <returns>A query for accessing the inserted values.</returns>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="values"/> is <c>null</c>.</exception>
public static Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
this DbContext ctx,
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
IBulkInsertOptions? options = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(values);

var executor = ctx.GetService<ITempTableBulkInsertExecutor>();

return executor.BulkInsertValuesIntoTempTableAsync(values, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="values"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the column 1.</typeparam>
/// <typeparam name="TColumn2">Type of the column 2.</typeparam>
/// <returns>A query for accessing the inserted values.</returns>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="values"/> is <c>null</c>.</exception>
public static Task BulkInsertValuesIntoTempTableAsync<TColumn1, TColumn2>(
this DbContext ctx,
IEnumerable<(TColumn1 column1, TColumn2 column2)> values,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(values);

var entities = values.Select(t => new TempTable<TColumn1, TColumn2>(t.column1, t.column2));

return ctx.BulkInsertIntoTempTableAsync(entities, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="values"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="values">Values to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="TColumn1">Type of the column 1.</typeparam>
/// <typeparam name="TColumn2">Type of the column 2.</typeparam>
/// <returns>A query for accessing the inserted values.</returns>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="values"/> is <c>null</c>.</exception>
public static Task BulkInsertValuesIntoTempTableAsync<TColumn1, TColumn2>(
this DbContext ctx,
IEnumerable<(TColumn1 column1, TColumn2 column2)> values,
ITempTableReference tempTable,
IBulkInsertOptions? options = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(values);

var entities = values.Select(t => new TempTable<TColumn1, TColumn2>(t.column1, t.column2));

return ctx.BulkInsertIntoTempTableAsync(entities, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="entities"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="entities">Entities to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="propertiesToInsert">Properties to insert. If <c>null</c> then all properties are used.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="T">Entity type.</typeparam>
/// <returns>A query for accessing the inserted values.</returns>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="entities"/> is <c>null</c>.</exception>
public static Task BulkInsertIntoTempTableAsync<T>(
this DbContext ctx,
IEnumerable<T> entities,
ITempTableReference tempTable,
Expression<Func<T, object?>>? propertiesToInsert = null,
CancellationToken cancellationToken = default)
where T : class
{
var executor = ctx.GetService<ITempTableBulkInsertExecutor>();
var options = executor.CreateBulkInsertOptions(propertiesToInsert is null ? null : IEntityPropertiesProvider.Include(propertiesToInsert));

return executor.BulkInsertIntoTempTableAsync(entities, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="entities"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="entities">Entities to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="T">Entity type.</typeparam>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="entities"/> is <c>null</c>.</exception>
public static Task BulkInsertIntoTempTableAsync<T>(
this DbContext ctx,
IEnumerable<T> entities,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken = default)
where T : class
{
var executor = ctx.GetService<ITempTableBulkInsertExecutor>();

return executor.BulkInsertIntoTempTableAsync(entities, tempTable, options, cancellationToken);
}

/// <summary>
/// Copies <paramref name="entities"/> into a temp table and returns the query for accessing the inserted records.
/// </summary>
/// <param name="ctx">Database context.</param>
/// <param name="entities">Entities to insert.</param>
/// <param name="tempTable">Temp table to insert into.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <typeparam name="T">Entity type.</typeparam>
/// <exception cref="ArgumentNullException"> <paramref name="ctx"/> or <paramref name="entities"/> is <c>null</c>.</exception>
public static Task BulkInsertIntoTempTableAsync<T>(
this DbContext ctx,
IEnumerable<T> entities,
ITempTableReference tempTable,
IBulkInsertOptions? options,
CancellationToken cancellationToken = default)
where T : class
{
var executor = ctx.GetService<ITempTableBulkInsertExecutor>();

return executor.BulkInsertIntoTempTableAsync(entities, tempTable, options, cancellationToken);
}

/// <summary>
/// Truncates the table of the entity of type <typeparamref name="T"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ IBulkInsertOptions IBulkInsertExecutor.CreateOptions(IEntityPropertiesProvider?
return new SqlServerBulkInsertOptions { PropertiesToInsert = propertiesToInsert };
}

/// <inheritdoc />
public IBulkInsertOptions CreateBulkInsertOptions(IEntityPropertiesProvider? propertiesToInsert = null)
{
return new SqlServerBulkInsertOptions { PropertiesToInsert = propertiesToInsert };
}

/// <inheritdoc />
ITempTableBulkInsertOptions ITempTableBulkInsertExecutor.CreateOptions(IEntityPropertiesProvider? propertiesToInsert)
{
Expand Down Expand Up @@ -355,6 +361,100 @@ private async Task<ITempTableQuery<T>> BulkInsertIntoTempTableAsync<T, TEntity>(
}
}

/// <inheritdoc />
public Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(values);
ArgumentNullException.ThrowIfNull(tempTable);

if (options is not SqlServerTempTableBulkInsertOptions sqlServerOptions)
sqlServerOptions = new SqlServerTempTableBulkInsertOptions(options);

return BulkInsertIntoTempTableAsync<TColumn1, TempTable<TColumn1>>(values,
tempTable,
sqlServerOptions.GetBulkInsertOptions(),
SqlServerBulkOperationContextFactoryForValues.Instance,
cancellationToken);
}

/// <inheritdoc />
public Task BulkInsertValuesIntoTempTableAsync<TColumn1>(
IEnumerable<TColumn1> values,
ITempTableReference tempTable,
IBulkInsertOptions? options,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(values);
ArgumentNullException.ThrowIfNull(tempTable);

return BulkInsertIntoTempTableAsync<TColumn1, TempTable<TColumn1>>(values,
tempTable,
options,
SqlServerBulkOperationContextFactoryForValues.Instance,
cancellationToken);
}

/// <inheritdoc />
public Task BulkInsertIntoTempTableAsync<T>(
IEnumerable<T> entities,
ITempTableReference tempTable,
ITempTableBulkInsertOptions? options,
CancellationToken cancellationToken = default)
where T : class
{
ArgumentNullException.ThrowIfNull(entities);
ArgumentNullException.ThrowIfNull(tempTable);

if (options is not SqlServerTempTableBulkInsertOptions sqlServerOptions)
sqlServerOptions = new SqlServerTempTableBulkInsertOptions(options);

return BulkInsertIntoTempTableAsync<T, T>(entities,
tempTable,
sqlServerOptions.GetBulkInsertOptions(),
SqlServerBulkOperationContextFactoryForEntities.Instance,
cancellationToken);
}

/// <inheritdoc />
public Task BulkInsertIntoTempTableAsync<T>(
IEnumerable<T> entities,
ITempTableReference tempTable,
IBulkInsertOptions? options,
CancellationToken cancellationToken = default)
where T : class
{
ArgumentNullException.ThrowIfNull(entities);
ArgumentNullException.ThrowIfNull(tempTable);

return BulkInsertIntoTempTableAsync<T, T>(entities,
tempTable,
options,
SqlServerBulkOperationContextFactoryForEntities.Instance,
cancellationToken);
}

private async Task BulkInsertIntoTempTableAsync<T, TEntity>(
IEnumerable<T> entitiesOrValues,
ITempTableReference tempTable,
IBulkInsertOptions? options,
ISqlServerBulkOperationContextFactory bulkOperationContextFactory,
CancellationToken cancellationToken)
where TEntity : class
{
var type = typeof(TEntity);
var entityTypeName = EntityNameProvider.GetTempTableName(type);
var entityType = _ctx.Model.GetEntityType(entityTypeName, type);

if (options is not SqlServerBulkInsertOptions sqlServerOptions)
sqlServerOptions = new SqlServerBulkInsertOptions(options);

await BulkInsertAsync(entityType, entitiesOrValues, null, tempTable.Name, sqlServerOptions, bulkOperationContextFactory, cancellationToken).ConfigureAwait(false);
}

/// <inheritdoc />
public async Task TruncateTableAsync<T>(CancellationToken cancellationToken = default)
where T : class
Expand Down
Loading

0 comments on commit 10ebb55

Please sign in to comment.