Skip to content

Commit be9717b

Browse files
author
fabien.menager
committed
Remove recursive option for now
1 parent 2364dd0 commit be9717b

10 files changed

Lines changed: 4 additions & 364 deletions

File tree

src/EntityFrameworkCore.ExecuteInsert/BulkInsertProviderBase.cs

Lines changed: 2 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Collections;
21
using System.Data.Common;
3-
using System.Reflection;
42

53
using EntityFrameworkCore.ExecuteInsert.Abstractions;
64
using EntityFrameworkCore.ExecuteInsert.Dialect;
@@ -15,25 +13,13 @@ namespace EntityFrameworkCore.ExecuteInsert;
1513
public abstract class BulkInsertProviderBase<TDialect> : IBulkInsertProvider
1614
where TDialect : SqlDialectBuilder, new()
1715
{
18-
protected readonly TDialect SqlDialect;
19-
20-
protected BulkInsertProviderBase()
21-
{
22-
SqlDialect = new TDialect();
23-
}
16+
protected readonly TDialect SqlDialect = new();
2417

2518
protected virtual string BulkInsertId => "_bulk_insert_id";
2619

27-
private static readonly MethodInfo CastMethod = typeof(Enumerable).GetMethod("Cast")!;
28-
private static readonly MethodInfo BulkInsertPkMethod = typeof(BulkInsertProviderBase<TDialect>).GetMethod(nameof(BulkInsertWithPrimaryKeyAsync))!;
29-
private static readonly MethodInfo GetChildrenMethod = typeof(BulkInsertProviderBase<TDialect>).GetMethod(nameof(GetChildrenEntities))!;
30-
3120
protected abstract string CreateTableCopySql { get; }
3221
protected abstract string AddTableCopyBulkInsertId { get; }
3322

34-
private static readonly MethodInfo GetFieldValueMethod =
35-
typeof(DbDataReader).GetMethod(nameof(DbDataReader.GetFieldValue))!;
36-
3723
protected async Task<string> CreateTableCopyAsync<T>(
3824
DbContext context,
3925
DbConnection connection,
@@ -89,67 +75,6 @@ public async Task<List<T>> CopyFromTempTableAsync<T>(DbContext context,
8975
cancellationToken: cancellationToken);
9076
}
9177

92-
public async Task<List<KeyValuePair<long, object[]>>> CopyFromTempTablePrimaryKeyAsync<T>(
93-
DbContext context,
94-
DbConnection connection,
95-
string tempTableName,
96-
BulkInsertOptions options,
97-
CancellationToken cancellationToken = default) where T : class
98-
{
99-
return await CopyFromTempTableWithKeysAsync<T, object[]>(
100-
context,
101-
connection,
102-
tempTableName,
103-
options,
104-
cancellationToken: cancellationToken
105-
);
106-
}
107-
108-
private async Task<List<KeyValuePair<long, TResult>>> CopyFromTempTableWithKeysAsync<T, TResult>(
109-
DbContext context,
110-
DbConnection connection,
111-
string tempTableName,
112-
BulkInsertOptions options,
113-
CancellationToken cancellationToken = default
114-
)
115-
where T : class
116-
where TResult : class
117-
{
118-
var (schemaName, tableName, primaryKey) = GetTableInfo(context, typeof(T));
119-
var escapedTableName = EscapeTableName(schemaName, tableName);
120-
121-
var indexColumn = Escape(BulkInsertId);
122-
var returnedColumns = new[] { indexColumn }
123-
.Concat(primaryKey.Properties.Select(p => Escape(p.GetColumnName()))).ToArray();
124-
125-
var query = ""; //BuildInsertSelectQuery(tempTableName, escapedTableName, returnedColumns, returnedColumns, moveRows);
126-
127-
var result = new List<KeyValuePair<long, TResult>>();
128-
129-
await using var command = connection.CreateCommand();
130-
command.CommandText = query;
131-
132-
var getFieldValueMethods = primaryKey.Properties
133-
.Select(p => GetFieldValueMethod.MakeGenericMethod(p.ClrType))
134-
.ToArray();
135-
136-
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
137-
while (await reader.ReadAsync(cancellationToken))
138-
{
139-
var index = (long)reader.GetValue(0);
140-
var values = new object[primaryKey.Properties.Count];
141-
for (var i = 0; i < primaryKey.Properties.Count; i++)
142-
{
143-
values[i] = getFieldValueMethods[i].Invoke(reader, new object[] { i + 1 })!;
144-
}
145-
146-
var entity = Activator.CreateInstance(typeof(TResult), values) as TResult;
147-
result.Add(new KeyValuePair<long, TResult>(index, entity!));
148-
}
149-
150-
return result;
151-
}
152-
15378
private async Task<List<TResult>> CopyFromTempTableWithoutKeysAsync<T, TResult>(DbContext context,
15479
DbConnection connection,
15580
string tempTableName,
@@ -204,27 +129,6 @@ public async Task<List<T>> BulkInsertWithIdentityAsync<T>(
204129
return result;
205130
}
206131

207-
public async Task<List<KeyValuePair<long, object[]>>> BulkInsertWithPrimaryKeyAsync<T>(
208-
DbContext context,
209-
IEnumerable<T> entities,
210-
BulkInsertOptions options,
211-
CancellationToken ctk = default
212-
) where T : class
213-
{
214-
var (connection, wasClosed) = await context.GetConnection(ctk);
215-
216-
var (tableName, _) = await PerformBulkInsertAsync(context, entities, options, tempTableRequired: true, ctk: ctk);
217-
218-
var result = await CopyFromTempTablePrimaryKeyAsync<T>(context, connection, tableName, options, ctk);
219-
220-
if (wasClosed)
221-
{
222-
await connection.CloseAsync();
223-
}
224-
225-
return result;
226-
}
227-
228132
public async Task BulkInsertWithoutReturnAsync<T>(
229133
DbContext context,
230134
IEnumerable<T> entities,
@@ -265,32 +169,7 @@ public async Task BulkInsertWithoutReturnAsync<T>(
265169

266170
var (connection, wasClosed) = await context.GetConnection(ctk);
267171

268-
if (options.Recursive)
269-
{
270-
// Insert children first
271-
var navigationProperties = context.GetNavigationProperties(typeof(T));
272-
273-
foreach (var navigationProperty in navigationProperties)
274-
{
275-
var itemType = navigationProperty.ClrType;
276-
var tupleType = typeof(KeyValuePair<,>).MakeGenericType(typeof(long), itemType);
277-
278-
// Call GetChildrenEntities with reflection because the type is not known at compile time
279-
var allChildren = GetChildrenMethod.MakeGenericMethod(typeof(T)).Invoke(this, [entities, navigationProperty]) as IEnumerable<KeyValuePair<long, object>>;
280-
281-
// Cast the IEnumerable to the correct type
282-
var items = new List<KeyValuePair<long, object>>(allChildren);
283-
// var itemsCasted = CastMethod.MakeGenericMethod(tupleType).Invoke(null, [items]);
284-
// var items = allChildren.Cast<KeyValuePair<long, object>>();
285-
286-
// Call BulkInsertWithPrimaryKeyAsync to insert elements and get their primary key values
287-
var bulkInsert = BulkInsertPkMethod.MakeGenericMethod(tupleType);
288-
289-
var pkValues = await (bulkInsert.Invoke(this, [context, items, options, ctk]) as Task<List<KeyValuePair<long, object[]>>>)!;
290-
}
291-
}
292-
293-
var tableName = tempTableRequired || options.Recursive
172+
var tableName = tempTableRequired
294173
? await CreateTableCopyAsync<T>(context, connection, ctk)
295174
: GetEscapedTableName(context, typeof(T));
296175

@@ -313,32 +192,6 @@ public async Task BulkInsertWithoutReturnAsync<T>(
313192
protected abstract Task BulkInsert<T>(DbContext context, IEnumerable<T> entities,
314193
string tableName, PropertyAccessor[] properties, BulkInsertOptions options, CancellationToken ctk) where T : class;
315194

316-
public IEnumerable<KeyValuePair<long, object>> GetChildrenEntities<T>(IEnumerable<T> entities, INavigation navigationProperty) where T : class
317-
{
318-
var navProp = navigationProperty.PropertyInfo;
319-
var isCollection = navigationProperty.IsCollection;
320-
long index = 0;
321-
322-
foreach (var e in entities)
323-
{
324-
var value = navProp!.GetValue(e);
325-
326-
if (isCollection && value is IEnumerable enumerable)
327-
{
328-
foreach (var childEntity in enumerable)
329-
{
330-
yield return new KeyValuePair<long, object>(index, childEntity);
331-
}
332-
}
333-
else if (value != null)
334-
{
335-
yield return new KeyValuePair<long, object>(index, value);
336-
}
337-
338-
index++;
339-
}
340-
}
341-
342195
/// <summary>
343196
/// Escapes a schema and table name using database-specific delimiters.
344197
/// </summary>

src/EntityFrameworkCore.ExecuteInsert/Dialect/SqlDialectBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ protected IEnumerable<string> GetUpdates<T>(DbContext context, Expression<Func<T
204204
}
205205

206206
/// <summary>
207-
/// Converts an expression to a SQL string.
207+
/// Converts an expression to an SQL string.
208208
/// </summary>
209209
/// <param name="context">The DbContext</param>
210210
/// <param name="expr">The expression, with simple operations</param>

src/EntityFrameworkCore.ExecuteInsert/Extensions/DbContextExtensions.cs

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Concurrent;
2-
using System.Data;
1+
using System.Data;
32
using System.Data.Common;
43

54
using Microsoft.EntityFrameworkCore;
@@ -9,9 +8,6 @@ namespace EntityFrameworkCore.ExecuteInsert.Extensions;
98

109
public static class DbContextExtensions
1110
{
12-
// Reflection cache to store property metadata for each entity type
13-
private static readonly ConcurrentDictionary<Type, IProperty[]?> PropertyCache = new();
14-
1511
/// <summary>
1612
/// Gets cached properties for an entity type, using reflection if not already cached.
1713
/// </summary>
@@ -25,34 +21,6 @@ public static IProperty[] GetProperties(this DbContext context, Type entityType,
2521
.ToArray();
2622
}
2723

28-
public static INavigation[] GetCollectionNavigationProperties(this DbContext context, Type getType)
29-
{
30-
var entityType = context.Model.FindEntityType(getType);
31-
if (entityType == null)
32-
{
33-
throw new InvalidOperationException($"Could not determine entity type for type {getType.Name}");
34-
}
35-
36-
return entityType
37-
.GetNavigations()
38-
.Where(n => n.IsCollection)
39-
.ToArray();
40-
}
41-
42-
public static INavigation[] GetNavigationProperties(this DbContext context, Type getType)
43-
{
44-
var entityType = context.Model.FindEntityType(getType);
45-
if (entityType == null)
46-
{
47-
throw new InvalidOperationException($"Could not determine entity type for type {getType.Name}");
48-
}
49-
50-
return entityType
51-
.GetNavigations()
52-
.Where(n => !n.IsCollection)
53-
.ToArray();
54-
}
55-
5624
public static async Task<(DbConnection connection, bool wasClosed)> GetConnection(this DbContext context, CancellationToken ctk = default)
5725
{
5826
var connection = context.Database.GetDbConnection();

src/EntityFrameworkCore.ExecuteInsert/Options/BulkInsertOptions.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22

33
public class BulkInsertOptions
44
{
5-
/// <summary>
6-
/// Insert entities recursively.
7-
/// </summary>
8-
[Obsolete("Not supported yet.")]
9-
public bool Recursive { get; set; }
10-
115
/// <summary>
126
/// Move rows between tables instead of inserting them.
137
/// Only supported for PostgreSQL.

tests/EntityFrameworkCore.ExecuteInsert.Tests/DbContext/TestDbContextWithNavProps.cs

Lines changed: 0 additions & 58 deletions
This file was deleted.

tests/EntityFrameworkCore.ExecuteInsert.Tests/Tests/Basic/BasicTestsBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ public async Task InsertsThousandsOfEntitiesSuccessfully()
133133
// Act
134134
await DbContainer.DbContext.ExecuteInsertAsync(entities, o =>
135135
{
136-
o.Recursive = false;
137136
o.MoveRows = false;
138137
});
139138

0 commit comments

Comments
 (0)