From 17109e6686ad02b96aa0d3fef4545ffb7f0f1526 Mon Sep 17 00:00:00 2001 From: Wesley Teixeira Date: Tue, 6 Oct 2026 21:18:13 -0400 Subject: [PATCH] Map CLR and other non-primitive SQL Server types to string in schema inference `SqlServerSchemaConfig.ToDotNetTypeName` threw for `geography`, `geometry`, `hierarchyid`, `xml` and `vector`, and because the schema inference walks every table of the database, a single column of one of these types anywhere failed the `Data`, `Reset` and `Inspect` commands as a whole. They now map to `string`: SQL Server converts the string representation (WKT, path, XML, JSON array) implicitly on insert, so YAML/JSON data files can seed these columns as text. Adds the `[Test].[ClrTypes]` table to the test console and asserts the mapping in `SqlServerSelectSchema` (`vector` is left out of the table because the CI image is SQL Server 2019). --- CHANGELOG.md | 3 +++ src/DbEx.SqlServer/SqlServerSchemaConfig.cs | 4 +++ .../009-create-test-clrtypes-table.sql | 13 ++++++++++ tests/DbEx.Test/DatabaseSchemaTest.cs | 25 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 tests/DbEx.Test.Console/Migrations/009-create-test-clrtypes-table.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index f52bdd1..9bb1c69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v3.2.1 +- *Fixed:* SQL Server schema inference no longer throws `Database data type 'x' does not have corresponding .NET type mapping defined` for `geography`, `geometry`, `hierarchyid`, `xml` and `vector` columns; they map to `string`. Previously a single column of one of these types in any table failed the `Data`, `Reset` and `Inspect` commands for the whole database. + ## v3.2.0 - *Enhancement:* The `MigrationCommand.Execute` has been enhanced to support the execution of YAML/JSON data files. Raw SQL must _now_ be prefixed by `>` to explicitly differentiate from a file path. - *Enhancement:* The `MigrationCommand.Inspect` has been enhanced to also report whether each column has been identified as a JSON column (i.e. `DbColumnSchema.IsJson`). diff --git a/src/DbEx.SqlServer/SqlServerSchemaConfig.cs b/src/DbEx.SqlServer/SqlServerSchemaConfig.cs index 6028264..25fad45 100644 --- a/src/DbEx.SqlServer/SqlServerSchemaConfig.cs +++ b/src/DbEx.SqlServer/SqlServerSchemaConfig.cs @@ -212,6 +212,10 @@ public override string ToDotNetTypeName(DbColumnSchema schema) "TINYINT" => "byte", "REAL" => "float", "UNIQUEIDENTIFIER" => "Guid", + // CLR and other non-primitive types surface as their string representation (WKT, path, XML, JSON array); + // SQL Server converts the string implicitly on insert. Without a mapping, a single column of one of + // these types in any table would fail the whole schema inference (Data, Reset and Inspect). + "GEOGRAPHY" or "GEOMETRY" or "HIERARCHYID" or "XML" or "VECTOR" => "string", _ => throw new InvalidOperationException($"Database data type '{dbType}' does not have corresponding .NET type mapping defined."), }; } diff --git a/tests/DbEx.Test.Console/Migrations/009-create-test-clrtypes-table.sql b/tests/DbEx.Test.Console/Migrations/009-create-test-clrtypes-table.sql new file mode 100644 index 0000000..9fa3b7f --- /dev/null +++ b/tests/DbEx.Test.Console/Migrations/009-create-test-clrtypes-table.sql @@ -0,0 +1,13 @@ +-- Create table: [Test].[ClrTypes] + +BEGIN TRANSACTION + +CREATE TABLE [Test].[ClrTypes] ( + [ClrTypesId] INT NOT NULL PRIMARY KEY, + [Location] GEOGRAPHY NULL, + [Shape] GEOMETRY NULL, + [Path] HIERARCHYID NULL, + [Document] XML NULL +) + +COMMIT TRANSACTION diff --git a/tests/DbEx.Test/DatabaseSchemaTest.cs b/tests/DbEx.Test/DatabaseSchemaTest.cs index de1c70d..7090731 100644 --- a/tests/DbEx.Test/DatabaseSchemaTest.cs +++ b/tests/DbEx.Test/DatabaseSchemaTest.cs @@ -354,6 +354,31 @@ public async Task SqlServerSelectSchema() Assert.IsNull(col.ForeignTable); Assert.IsNull(col.ForeignColumn); Assert.IsNotNull(col.DefaultValue); + + // [Test].[ClrTypes] - CLR and other non-primitive types map to string. + tab = tables.Where(x => x.Name == "ClrTypes").SingleOrDefault(); + Assert.IsNotNull(tab); + Assert.AreEqual(5, tab.Columns.Count); + + col = tab.Columns[1]; + Assert.AreEqual("Location", col.Name); + Assert.AreEqual("geography", col.Type); + Assert.AreEqual("string", col.DotNetType); + + col = tab.Columns[2]; + Assert.AreEqual("Shape", col.Name); + Assert.AreEqual("geometry", col.Type); + Assert.AreEqual("string", col.DotNetType); + + col = tab.Columns[3]; + Assert.AreEqual("Path", col.Name); + Assert.AreEqual("hierarchyid", col.Type); + Assert.AreEqual("string", col.DotNetType); + + col = tab.Columns[4]; + Assert.AreEqual("Document", col.Name); + Assert.AreEqual("xml", col.Type); + Assert.AreEqual("string", col.DotNetType); } [Test]