在EF Core 5配置Oracle DbContext时集成NetTopologySuite
解决方案:.NET 5.0 + Oracle EF Core 拓扑类型映射(无需Devart)
环境说明
你的项目依赖版本:
Microsoft.EntityFrameworkCore5.0.10NetTopologySuite.IO.Oracle3.0.0Oracle.EntityFrameworkCore5.21.3
Oracle官方EF Core 5.x版本无内置NetTopologySuite类型映射支持,需要通过自定义类型映射、值转换器和参数配置实现SDO_GEOMETRY列的迁移与读写,同时兼容Oracle 11g。
步骤1:实现自定义值转换器
借助NetTopologySuite.IO.Oracle的序列化工具,实现Geometry与Oracle原生SdoGeometry的双向转换:
using NetTopologySuite.Geometries; using NetTopologySuite.IO.Sdo; // 非可空Geometry类型转换器 public class GeometryToSdoConverter : ValueConverter<Geometry, SdoGeometry> { private static readonly SdoGeometryReader _reader = new(); private static readonly SdoGeometryWriter _writer = new(); public GeometryToSdoConverter() : base( geo => _writer.Write(geo), sdo => _reader.Read(sdo)) { } } // 可空Geometry类型转换器 public class NullableGeometryToSdoConverter : ValueConverter<Geometry?, SdoGeometry?> { private static readonly SdoGeometryReader _reader = new(); private static readonly SdoGeometryWriter _writer = new(); public NullableGeometryToSdoConverter() : base( geo => geo == null ? null : _writer.Write(geo), sdo => sdo == null ? null : _reader.Read(sdo)) { } }
步骤2:注册Oracle拓扑类型映射
实现自定义类型映射源,让EF Core识别Geometry类型对应Oracle的SDO_GEOMETRY列,并正确配置数据库参数:
using Microsoft.EntityFrameworkCore.Storage; using Oracle.EntityFrameworkCore.Storage; using Oracle.ManagedDataAccess.Client; // 自定义类型映射源,处理Geometry类型的数据库映射逻辑 public class OracleGeoTypeMappingSource : RelationalTypeMappingSource { public OracleGeoTypeMappingSource(RelationalTypeMappingSourceDependencies dependencies) : base(dependencies) { } protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType; if (clrType == typeof(Geometry) || clrType == typeof(Geometry?)) { return new OracleGeoTypeMapping("SDO_GEOMETRY", clrType); } return base.FindMapping(mappingInfo); } } // 自定义Oracle类型映射,配置SDO_GEOMETRY参数的类型 public class OracleGeoTypeMapping : RelationalTypeMapping { public OracleGeoTypeMapping(string storeType, Type clrType) : base(storeType, clrType) { } protected OracleGeoTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters) { } public override RelationalTypeMapping Clone(string storeType, int? size) { return new OracleGeoTypeMapping(Parameters.WithStoreTypeAndSize(storeType, size)); } public override CoreTypeMapping Clone(ValueConverter converter) { return new OracleGeoTypeMapping(Parameters.WithConverter(converter)); } protected override void ConfigureParameter(DbParameter parameter) { base.ConfigureParameter(parameter); if (parameter is OracleParameter oracleParam) { oracleParam.OracleDbType = OracleDbType.Object; oracleParam.UdtTypeName = "MDSYS.SDO_GEOMETRY"; } } }
步骤3:配置DbContext
在OnModelCreating中绑定转换器,并在服务注册时替换类型映射源,同时保留Oracle 11兼容性:
DbContext代码
public class MyDbContext : DbContext { public DbSet<MyGeo> MyGeos { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 全局注册Geometry类型的转换器与列类型 foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entityType.GetProperties()) { if (property.ClrType == typeof(Geometry)) { property.SetColumnType("SDO_GEOMETRY"); property.SetValueConverter(new GeometryToSdoConverter()); } else if (property.ClrType == typeof(Geometry?)) { property.SetColumnType("SDO_GEOMETRY"); property.SetValueConverter(new NullableGeometryToSdoConverter()); } } } // 实体单独配置(可选,与全局配置二选一) modelBuilder.Entity<MyGeo>(builder => { builder.Property(p => p.MySdoGeo) .HasColumnType("SDO_GEOMETRY"); }); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseOracle("your_connection_string", o => o.UseOracleSQLCompatibility("11")); } } // 实体类 public class MyGeo { public int Id { get; set; } public Geometry MySdoGeo { get; set; } }
服务注册(Startup/Program.cs)
services.AddDbContext<MyDbContext>(options => options.UseOracle("your_connection_string", o => o.UseOracleSQLCompatibility("11")) .ReplaceService<IRelationalTypeMappingSource, OracleGeoTypeMappingSource>());
为什么之前的尝试失败?
- 直接使用SdoGeometry类型:Oracle EF Core provider无内置该类型的映射逻辑,EF Core无法识别其与数据库列的关联。
- 自定义转换器未配类型映射:仅添加
HasConversion但未注册类型映射源,EF Core仍不认可Geometry可映射到SDO_GEOMETRY,且未配置Oracle参数的UDT类型,驱动无法正确处理数据。
扩展支持的思路(若上述方案仍有问题)
- 增强类型映射源:若需支持更多拓扑类型(如
Point/Polygon),可在OracleGeoTypeMappingSource中添加对应CLR类型的映射逻辑。 - 自定义迁移SQL生成:重写
IMigrationsSqlGenerator,确保生成的CREATE TABLE语句符合Oracle 11g的SDO_GEOMETRY定义规范。 - 验证UDT权限:确保数据库用户拥有
MDSYS.SDO_GEOMETRY类型的访问权限,避免读写时出现权限错误。
内容的提问来源于stack exchange,提问作者CalciferZero
相关产品推荐
相关产品推荐

