Skip to content

Commit 162ddef

Browse files
Geometry support.
1 parent d8ac159 commit 162ddef

15 files changed

Lines changed: 152 additions & 17 deletions

File tree

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ internal class MySqlBulkInsertProvider(ILogger<MySqlBulkInsertProvider> logger)
2222
protected override string GetTempTableName(string tableName) => $"#_temp_bulk_insert_{tableName}";
2323

2424
/// <inheritdoc />
25-
protected override MySqlBulkInsertOptions CreateDefaultOptions() => new();
25+
protected override MySqlBulkInsertOptions CreateDefaultOptions() => new()
26+
{
27+
Converters = [MySqlGeometryConverter.Instance]
28+
};
2629

2730
/// <inheritdoc />
2831
protected override IAsyncEnumerable<T> BulkInsertReturnEntities<T>(
@@ -71,11 +74,11 @@ CancellationToken ctk
7174
if (sync)
7275
{
7376
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
74-
bulkCopy.WriteToServer(new EnumerableDataReader<T>(entities, properties));
77+
bulkCopy.WriteToServer(new EnumerableDataReader<T>(entities, properties, options.Converters));
7578
}
7679
else
7780
{
78-
await bulkCopy.WriteToServerAsync(new EnumerableDataReader<T>(entities, properties), ctk);
81+
await bulkCopy.WriteToServerAsync(new EnumerableDataReader<T>(entities, properties, options.Converters), ctk);
7982
}
8083
}
8184
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using MySqlConnector;
2+
3+
using NetTopologySuite.Geometries;
4+
5+
using PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
6+
7+
namespace PhenX.EntityFrameworkCore.BulkInsert.MySql;
8+
9+
internal sealed class MySqlGeometryConverter : IValueConverter
10+
{
11+
public static readonly MySqlGeometryConverter Instance = new();
12+
13+
private MySqlGeometryConverter()
14+
{
15+
}
16+
17+
public bool TryConvertValue(object source, out object result)
18+
{
19+
if (source is Geometry geometry)
20+
{
21+
result = MySqlGeometry.FromWkb(geometry.SRID, geometry.ToBinary());
22+
return true;
23+
}
24+
25+
result = source;
26+
return false;
27+
}
28+
}

src/PhenX.EntityFrameworkCore.BulkInsert.MySql/PhenX.EntityFrameworkCore.BulkInsert.MySql.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</ItemGroup>
1010

1111
<ItemGroup>
12+
<PackageReference Include="NetTopologySuite" Version="2.6.0" />
1213
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Condition="'$(TargetFramework)' == 'net8.0'" Version="8.0.3" />
1314
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Condition="'$(TargetFramework)' == 'net9.0'" Version="9.0.0-preview.3.efcore.9.0.0" />
1415
</ItemGroup>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ protected override async Task BulkInsert<T>(
5353
if (sync)
5454
{
5555
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
56-
bulkCopy.WriteToServer(new EnumerableDataReader<T>(entities, columns));
56+
bulkCopy.WriteToServer(new EnumerableDataReader<T>(entities, columns, options.Converters));
5757
}
5858
else
5959
{
60-
await bulkCopy.WriteToServerAsync(new EnumerableDataReader<T>(entities, columns), ctk);
60+
await bulkCopy.WriteToServerAsync(new EnumerableDataReader<T>(entities, columns, options.Converters), ctk);
6161
}
6262
}
6363
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
2+
3+
/// <summary>
4+
/// Provide an interface to control how objects are written.
5+
/// </summary>
6+
public interface IValueConverter
7+
{
8+
/// <summary>
9+
/// Converts a value before written to the output.
10+
/// </summary>
11+
/// <param name="source">The source object.</param>
12+
/// <param name="result">The result type.</param>
13+
/// <returns>Indicates if an object should be written.</returns>
14+
bool TryConvertValue(object source, out object result);
15+
}

src/PhenX.EntityFrameworkCore.BulkInsert/EnumerableDataReader.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System.Data;
22

3+
using PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
34
using PhenX.EntityFrameworkCore.BulkInsert.Metadata;
45

56
namespace PhenX.EntityFrameworkCore.BulkInsert;
67

7-
internal sealed class EnumerableDataReader<T>(IEnumerable<T> rows, IReadOnlyList<ColumnMetadata> columns) : IDataReader
8+
internal sealed class EnumerableDataReader<T>(IEnumerable<T> rows, IReadOnlyList<ColumnMetadata> columns, List<IValueConverter>? converters) : IDataReader
89
{
910
private readonly IEnumerator<T> _enumerator = rows.GetEnumerator();
1011
private readonly Dictionary<string, int> _ordinalMap =
@@ -23,7 +24,7 @@ public object GetValue(int i)
2324
return DBNull.Value;
2425
}
2526

26-
return columns[i].GetValue(current)!;
27+
return GetAndConvertValue(columns[i], current);
2728
}
2829

2930
public int GetValues(object[] values)
@@ -36,12 +37,30 @@ public int GetValues(object[] values)
3637

3738
for (var i = 0; i < columns.Count; i++)
3839
{
39-
values[i] = columns[i].GetValue(current)!;
40+
values[i] = GetAndConvertValue(columns[i], current);
4041
}
4142

4243
return columns.Count;
4344
}
4445

46+
private object GetAndConvertValue(ColumnMetadata column, T entity)
47+
{
48+
var result = column.GetValue(entity!)!;
49+
if (converters != null)
50+
{
51+
foreach (var converter in converters)
52+
{
53+
if (converter.TryConvertValue(result, out var temp))
54+
{
55+
result = temp;
56+
break;
57+
}
58+
}
59+
}
60+
61+
return result;
62+
}
63+
4564
public bool Read() => _enumerator.MoveNext();
4665

4766
public Type GetFieldType(int i) => columns[i].ClrType;

src/PhenX.EntityFrameworkCore.BulkInsert/Options/BulkInsertOptions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using PhenX.EntityFrameworkCore.BulkInsert.Abstractions;
2+
13
namespace PhenX.EntityFrameworkCore.BulkInsert.Options;
24

35
/// <summary>
@@ -43,6 +45,11 @@ public class BulkInsertOptions
4345
/// </summary>
4446
public TimeSpan CopyTimeout { get; set; } = TimeSpan.FromMinutes(10);
4547

48+
/// <summary>
49+
/// The value converters.
50+
/// </summary>
51+
public List<IValueConverter>? Converters { get; set; }
52+
4653
internal int GetCopyTimeoutInSeconds()
4754
{
4855
return Math.Max(0, (int)CopyTimeout.TotalSeconds);

tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerMySql.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ protected override string GetConnectionString()
2828
protected override void Configure(DbContextOptionsBuilder optionsBuilder)
2929
{
3030
optionsBuilder
31-
.UseMySql(ServerVersion.AutoDetect(GetConnectionString()))
31+
.UseMySql(ServerVersion.AutoDetect(GetConnectionString()), o =>
32+
{
33+
o.UseNetTopologySuite();
34+
})
3235
.UseBulkInsertMySql();
3336
}
3437
}

tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerPostgreSql.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public class TestDbContainerPostgreSql<TDbContext> : TestDbContainer<TDbContext>
2525
protected override void Configure(DbContextOptionsBuilder optionsBuilder)
2626
{
2727
optionsBuilder
28-
.UseNpgsql()
28+
.UseNpgsql(o =>
29+
{
30+
o.UseNetTopologySuite();
31+
})
2932
.UseBulkInsertPostgreSql();
3033
}
3134
}

tests/PhenX.EntityFrameworkCore.BulkInsert.Tests/DbContainer/TestDbContainerSqlServer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ public class TestDbContainerSqlServer<TDbContext> : TestDbContainer<TDbContext>
2222
protected override void Configure(DbContextOptionsBuilder optionsBuilder)
2323
{
2424
optionsBuilder
25-
.UseSqlServer()
25+
.UseSqlServer(o =>
26+
{
27+
o.UseNetTopologySuite();
28+
})
2629
.UseBulkInsertSqlServer();
2730
}
2831
}

0 commit comments

Comments
 (0)