diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 63b65901a2..5f0d8b01d9 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -7,7 +7,7 @@ updates:
# and because it submits 3 different PRs
- dependency-name: "Microsoft.EntityFrameworkCore.*"
- dependency-name: "Microsoft.Extensions.*"
- - dependency-name: "Npgsql"
+ - dependency-name: "HuaweiCloud.Driver.GaussDB"
schedule:
interval: daily
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bd14700af5..c3d017cdfa 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -60,48 +60,70 @@ jobs:
run: dotnet build --configuration Debug
shell: bash
- - name: Start PostgreSQL ${{ matrix.pg_major }} (Linux)
+ - name: Start GaussDB ${{ matrix.pg_major }} (Linux)
if: startsWith(matrix.os, 'ubuntu')
run: |
- # First uninstall any PostgreSQL installed on the image
- dpkg-query -W --showformat='${Package}\n' 'postgresql-*' | xargs sudo dpkg -P postgresql
+ # First uninstall any GaussDB installed on the image
+ dpkg-query -W --showformat='${Package}\n' 'opengauss-*' | xargs sudo dpkg -P opengauss
# Automated repository configuration
- sudo apt install -y postgresql-common
- sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -v ${{ matrix.pg_major }} -y
+ sudo apt install -y opengauss-common
+ sudo /usr/share/opengauss-common/pgdg/apt.opengauss.org.sh -v ${{ matrix.pg_major }} -y
sudo apt-get update -qq
- sudo apt-get install -qq postgresql-${{ matrix.pg_major }}
+ sudo apt-get install -qq opengauss-${{ matrix.pg_major }}
# To disable PostGIS for prereleases (because it usually isn't available until late), surround with the following:
#if [ -z "${{ matrix.pg_prerelease }}" ]; then
- sudo apt-get install -qq postgresql-${{ matrix.pg_major }}-postgis-${{ env.postgis_version }}
+ sudo apt-get install -qq opengauss-${{ matrix.pg_major }}-postgis-${{ env.postgis_version }}
#fi
- sudo sed -i 's/max_connections = 100/max_connections = 200/g' /etc/postgresql/${{ matrix.pg_major }}/main/postgresql.conf
- sudo systemctl restart postgresql
+ sudo sed -i 's/max_connections = 100/max_connections = 200/g' /etc/opengauss/${{ matrix.pg_major }}/main/opengauss.conf
+ sudo systemctl restart opengauss
sudo -u postgres psql -c "CREATE USER npgsql_tests SUPERUSER PASSWORD 'npgsql_tests'"
- - name: Start PostgreSQL ${{ matrix.pg_major }} (Windows)
+ - name: Start GaussDB ${{ matrix.pg_major }} (Windows)
if: startsWith(matrix.os, 'windows')
run: |
# Find EnterpriseDB version number
EDB_VERSION=$(pwsh -c "
\$global:progressPreference='silentlyContinue';
- Invoke-WebRequest -URI https://www.postgresql.org/applications-v2.xml |
+ Invoke-WebRequest -URI https://www.opengauss.org/applications-v2.xml |
Select-Object -ExpandProperty Content |
- Select-Xml -XPath '/applications/application[id=\"postgresql_${{ matrix.pg_major }}\" and platform=\"windows-x64\"]/version/text()' |
+ Select-Xml -XPath '/applications/application[id=\"opengauss_${{ matrix.pg_major }}\" and platform=\"windows-x64\"]/version/text()' |
Select-Object -First 1 -ExpandProperty Node |
Select-Object -ExpandProperty Value")
- # Install PostgreSQL
- echo "Installing PostgreSQL (version: ${EDB_VERSION})"
- curl -o pgsql.zip -L https://get.enterprisedb.com/postgresql/postgresql-${EDB_VERSION}-windows-x64-binaries.zip
+ # Install GaussDB
+ echo "Installing GaussDB (version: ${EDB_VERSION})"
+ curl -o pgsql.zip -L https://get.enterprisedb.com/opengauss/opengauss-${EDB_VERSION}-windows-x64-binaries.zip
unzip pgsql.zip -x 'pgsql/include/**' 'pgsql/doc/**' 'pgsql/pgAdmin 4/**' 'pgsql/StackBuilder/**'
- # Match Npgsql CI Docker image and stash one level up
+ # Match GaussDB CI Docker image and stash one level up
cp $GITHUB_WORKSPACE/.build/{server.crt,server.key} pgsql
- # Start PostgreSQL
+ # Find OSGEO version number
+ OSGEO_VERSION=$(\
+ curl -Ls https://download.osgeo.org/postgis/windows/pg${{ matrix.pg_major }} |
+ sed -n 's/.*>postgis-bundle-pg${{ matrix.pg_major }}-\(${{ env.postgis_version }}.[0-9]*.[0-9]*\)x64.zip<.*/\1/p' |
+ tail -n 1)
+ if [ -z "$OSGEO_VERSION" ]; then
+ OSGEO_VERSION=$(\
+ curl -Ls https://download.osgeo.org/postgis/windows/pg${{ matrix.pg_major }}/archive |
+ sed -n 's/.*>postgis-bundle-pg${{ matrix.pg_major }}-\(${{ env.postgis_version }}.[0-9]*.[0-9]*\)x64.zip<.*/\1/p' |
+ tail -n 1)
+ POSTGIS_PATH="archive/"
+ else
+ POSTGIS_PATH=""
+ fi
+
+ # Install PostGIS
+ echo "Installing PostGIS (version: ${OSGEO_VERSION})"
+ POSTGIS_FILE="postgis-bundle-pg${{ matrix.pg_major }}-${OSGEO_VERSION}x64"
+ curl -o postgis.zip -L https://download.osgeo.org/postgis/windows/pg${{ matrix.pg_major }}/${POSTGIS_FILE}.zip
+ unzip postgis.zip -d postgis
+ cp -a postgis/$POSTGIS_FILE/. pgsql/
+
+ # Start GaussDB
pgsql/bin/initdb -D pgsql/PGDATA -E UTF8 -U postgres
pgsql/bin/pg_ctl -D pgsql/PGDATA -l logfile -o '-c max_connections=200 -c max_prepared_transactions=10 -c ssl=true -c ssl_cert_file=../server.crt -c ssl_key_file=../server.key' start
@@ -146,7 +168,7 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
- name: EFCore.PG.CI
+ name: EFCore.GaussDB.CI
path: nupkgs
- name: Publish packages to MyGet (vnext)
@@ -178,7 +200,7 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
- name: EFCore.PG.Release
+ name: EFCore.GaussDB.Release
path: nupkgs
# TODO: Create a release
diff --git a/.gitignore b/.gitignore
index dbb844096e..ec5e244bf1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ artifacts/
TestResult.xml
.dotnet
.vscode/
+example/GetStarted/GetStarted/.env
diff --git a/Directory.Build.props b/Directory.Build.props
index 1643ba4198..e8127ca1b2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,28 +1,29 @@
- 11.0.0-preview.1
- net10.0
+ 0.0.1
+ net10.0
latest
enable
true
latest
+ $(NoWarn);NU5105
true
- $(MSBuildThisFileDirectory)Npgsql.snk
+ $(MSBuildThisFileDirectory)GaussDB.snk
true
true
true
true
- Copyright 2025 © The Npgsql Development Team
- Npgsql
+ Copyright 2026 © The GaussDB Development Team
+ GaussDB
true
PostgreSQL
- https://github.com/npgsql/efcore.pg
- postgresql.png
+ https://github.com/HuaweiCloudDeveloper/gaussdb-efcore
+ gaussdb.png
-
+
@@ -51,8 +52,8 @@
-
-
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d5d1266684..c09d390ae2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,30 +1,24 @@
-
+
10.0.0
10.0.0
- 10.0.0
+ 0.1.0
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
@@ -33,4 +27,4 @@
-
+
\ No newline at end of file
diff --git a/EFCore.GaussDB.slnx b/EFCore.GaussDB.slnx
new file mode 100644
index 0000000000..530bea5566
--- /dev/null
+++ b/EFCore.GaussDB.slnx
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EFCore.PG.slnx.DotSettings b/EFCore.GaussDB.slnx.DotSettings
similarity index 100%
rename from EFCore.PG.slnx.DotSettings
rename to EFCore.GaussDB.slnx.DotSettings
diff --git a/EFCore.PG.slnx b/EFCore.PG.slnx
deleted file mode 100644
index 02e7db8e67..0000000000
--- a/EFCore.PG.slnx
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Npgsql.snk b/GaussDB.snk
similarity index 100%
rename from Npgsql.snk
rename to GaussDB.snk
diff --git a/NuGet.config b/NuGet.config
index f95d7a9e97..5d39099bd6 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,22 +1,22 @@
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
diff --git a/QueryBaseline.cs b/QueryBaseline.cs
index 15a46b8add..ddec87b640 100644
--- a/QueryBaseline.cs
+++ b/QueryBaseline.cs
@@ -72,7 +72,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -120,7 +120,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -366,7 +366,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -414,7 +414,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -660,7 +660,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -708,7 +708,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -962,7 +962,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1010,7 +1010,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1288,7 +1288,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1534,7 +1534,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1582,7 +1582,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1828,7 +1828,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -1876,7 +1876,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -2122,7 +2122,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsSingleline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 238
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -2170,7 +2170,7 @@
-Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.PG.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
+Microsoft.EntityFrameworkCore.Query.SimpleQueryNpgsqlTest.Regex_IsMatchOptionsMultiline() : line() na R:\RafaelNuvem\ProjetosGit\EFCorePostgreSQL\test\EFCore.GaussDB.FunctionalTests\Query\SimpleQueryNpgsqlTest.Functions.cs:linha 229
AssertSql(
@"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
@@ -3958,7 +3958,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryAnd() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryAnd() :
AssertSql(
@"SELECT (to_tsquery('a & b') && to_tsquery('c & d'))
FROM ""Customers"" AS c
@@ -3966,7 +3966,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryOr() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryOr() :
AssertSql(
@"SELECT (to_tsquery('a & b') || to_tsquery('c & d'))
FROM ""Customers"" AS c
@@ -3974,7 +3974,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_String() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_String() :
AssertSql(
@"SELECT (to_tsvector('a') @@ 'b')
FROM ""Customers"" AS c
@@ -3982,7 +3982,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_Tsquery() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.Matches_With_Tsquery() :
AssertSql(
@"SELECT (to_tsvector('a') @@ to_tsquery('b'))
FROM ""Customers"" AS c
@@ -3990,7 +3990,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryIsContainedIn() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryIsContainedIn() :
AssertSql(
@"SELECT (to_tsquery('b') <@ to_tsquery('a & b'))
FROM ""Customers"" AS c
@@ -3998,7 +3998,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryContains() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.TsQueryContains() :
AssertSql(
@"SELECT (to_tsquery('a & b') @> to_tsquery('b'))
FROM ""Customers"" AS c
@@ -4006,7 +4006,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.TsVectorConcat() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.TsVectorConcat() :
AssertSql(
@"SELECT (to_tsvector('b') || to_tsvector('c'))
FROM ""Customers"" AS c
@@ -4014,7 +4014,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.SpatialQueryNpgsqlGeographyTest.d__9.MoveNext() : line 84
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.SpatialQueryNpgsqlGeographyTest.d__9.MoveNext() : line 84
AssertSql(
@"@__point_0='POINT (0 1)' (DbType = Object)
@@ -4023,7 +4023,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.SpatialQueryNpgsqlGeographyTest.d__9.MoveNext() : line 84
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.SpatialQueryNpgsqlGeographyTest.d__9.MoveNext() : line 84
AssertSql(
@"@__point_0='POINT (0 1)' (DbType = Object)
@@ -4048,7 +4048,7 @@
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.WebSearchToTsQuery_With_Config_From_Variable() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.WebSearchToTsQuery_With_Config_From_Variable() :
AssertSql(
@"@__config_1='english'
@@ -4058,7 +4058,7 @@ SELECT websearch_to_tsquery(CAST(@__config_1 AS regconfig), 'a OR b')
-Npgsql.EntityFrameworkCore.PostgreSQL.Query.FullTextSearchDbFunctionsNpgsqlTest.WebSearchToTsQuery_With_Config() :
+HuaweiCloud.EntityFrameworkCore.GaussDB.Query.FullTextSearchDbFunctionsNpgsqlTest.WebSearchToTsQuery_With_Config() :
AssertSql(
@"SELECT websearch_to_tsquery(CAST('english' AS regconfig), 'a OR b')
FROM ""Customers"" AS c
diff --git a/README.md b/README.md
index a3edf26ef7..f60e2eb4cf 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,11 @@
-# Npgsql Entity Framework Core provider for PostgreSQL
+# GaussDB Entity Framework Core provider for GaussDB
-[](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL/)
-[](https://www.myget.org/feed/npgsql/package/nuget/Npgsql.EntityFrameworkCore.PostgreSQL)
-[](https://www.myget.org/feed/npgsql-vnext/package/nuget/Npgsql.EntityFrameworkCore.PostgreSQL)
-[](https://github.com/npgsql/efcore.pg/actions/workflows/build.yml)
-[](https://gitter.im/npgsql/npgsql)
+[](https://www.nuget.org/packages/HuaweiCloud.EntityFrameworkCore.GaussDB/)
+[](https://www.myget.org/feed/npgsql/package/nuget/HuaweiCloud.EntityFrameworkCore.GaussDB)
+[](https://www.myget.org/feed/npgsql-vnext/package/nuget/HuaweiCloud.EntityFrameworkCore.GaussDB)
+[](https://github.com/HuaweiCloudDeveloper/gaussdb-efcore/actions/workflows/build.yml)
-Npgsql.EntityFrameworkCore.PostgreSQL is the open source EF Core provider for PostgreSQL. It allows you to interact with PostgreSQL via the most widely-used .NET O/RM from Microsoft, and use familiar LINQ syntax to express queries. It's built on top of [Npgsql](https://github.com/npgsql/npgsql).
+HuaweiCloud.EntityFrameworkCore.GaussDB is the open source EF Core provider for GaussDB. It allows you to interact with GaussDB via the most widely-used .NET O/RM from Microsoft, and use familiar LINQ syntax to express queries. It's built on top of [GaussDB](https://github.com/HuaweiCloudDeveloper/gaussdb-dotnet).
The provider looks and feels just like any other Entity Framework Core provider. Here's a quick sample to get you started:
@@ -27,7 +26,7 @@ public class BlogContext : DbContext
public DbSet Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
- => optionsBuilder.UseNpgsql(@"Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase");
+ => optionsBuilder.UseGaussDB(@"Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase");
}
public class Blog
@@ -37,10 +36,10 @@ public class Blog
}
```
-Aside from providing general EF Core support for PostgreSQL, the provider also exposes some PostgreSQL-specific capabilities, allowing you to query JSON, array or range columns, as well as many other advanced features. For more information, see the [the Npgsql site](http://www.npgsql.org/efcore/index.html). For information about EF Core in general, see the [EF Core website](https://docs.microsoft.com/ef/core/).
+Aside from providing general EF Core support for GaussDB, the provider also exposes some GaussDB-specific capabilities, allowing you to query JSON, array or range columns, as well as many other advanced features. For more information, see the [the GaussDB site](https://doc.hcs.huawei.com/db/zh-cn/index.html). For information about EF Core in general, see the [EF Core website](https://docs.microsoft.com/ef/core/).
## Related packages
-* Spatial plugin to work with PostgreSQL PostGIS: [Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite)
-* NodaTime plugin to use better date/time types with PostgreSQL: [Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime)
-* The underlying Npgsql ADO.NET provider is [Npgsql](https://www.nuget.org/packages/Npgsql).
+* Spatial plugin to work with GaussDB PostGIS: [HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite](https://www.nuget.org/packages/HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite)
+* NodaTime plugin to use better date/time types with GaussDB: [HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime](https://www.nuget.org/packages/HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime)
+* The underlying GaussDB ADO.NET provider is [GaussDB](https://www.nuget.org/packages/HuaweiCloud.Driver.GaussDB/).
diff --git a/example/GetStarted/GetStarted/.env-example b/example/GetStarted/GetStarted/.env-example
new file mode 100644
index 0000000000..ae3289e61e
--- /dev/null
+++ b/example/GetStarted/GetStarted/.env-example
@@ -0,0 +1 @@
+GaussDBConnString=
\ No newline at end of file
diff --git a/example/GetStarted/GetStarted/Employee.cs b/example/GetStarted/GetStarted/Employee.cs
new file mode 100644
index 0000000000..5398694d9f
--- /dev/null
+++ b/example/GetStarted/GetStarted/Employee.cs
@@ -0,0 +1,29 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace GetStarted
+{
+ ///
+ /// 定义员工实体类,映射到 GaussDB 数据库中的 "employees" 表
+ ///
+ public class Employee
+ {
+ ///
+ /// 主键
+ ///
+ [Column("id")]
+ public int Id { get; set; }
+
+ ///
+ /// 员工姓名,最长 128 字符
+ ///
+ [Column("name")]
+ public string Name { get; set; } = default!;
+
+ ///
+ /// 员工年龄
+ ///
+ [Column("age")]
+ public int Age { get; set; }
+ }
+
+}
diff --git a/example/GetStarted/GetStarted/GaussDBDbContext.cs b/example/GetStarted/GetStarted/GaussDBDbContext.cs
new file mode 100644
index 0000000000..244937371e
--- /dev/null
+++ b/example/GetStarted/GetStarted/GaussDBDbContext.cs
@@ -0,0 +1,47 @@
+using System;
+using dotenv.net;
+
+namespace GetStarted
+{
+ ///
+ /// EF Core 上下文类,用于配置和操作 GaussDB 数据库
+ ///
+ public class GaussDBDbContext(DbContextOptions options) : DbContext(options)
+ {
+
+ ///
+ /// 员工表,EF Core 会自动根据模型类进行映射
+ ///
+ public DbSet Employees { get; set; } = null!;
+
+ ///
+ /// 连接字符串配置
+ ///
+ ///
+ ///
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+
+ DotEnv.Load(); // 加载 .env 文件中的环境变量
+
+ // 从环境变量中读取连接字符串(需通过 dotenv 或系统环境变量设置)
+ var connString = Environment.GetEnvironmentVariable("GaussDBConnString")
+ ?? throw new InvalidOperationException("未找到 GaussDBConnString 环境变量");
+
+ // 使用 GaussDB 的 EF Core provider(确保你已安装 UseGaussDB 的 NuGet 扩展)
+ optionsBuilder.UseGaussDB(connString);
+ }
+
+ ///
+ /// 定义实体与表的映射关系
+ ///
+ ///
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().ToTable("employees"); // 映射表名
+ modelBuilder.Entity().HasKey(e => e.Id); // 设置主键
+ modelBuilder.Entity().Property(e => e.Name).HasMaxLength(128); // 设置字段长度
+ }
+ }
+
+}
diff --git a/example/GetStarted/GetStarted/GetStarted.csproj b/example/GetStarted/GetStarted/GetStarted.csproj
new file mode 100644
index 0000000000..9ea932d17a
--- /dev/null
+++ b/example/GetStarted/GetStarted/GetStarted.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ disable
+ $(NoWarn);CS8002
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
diff --git a/example/GetStarted/GetStarted/Program.cs b/example/GetStarted/GetStarted/Program.cs
new file mode 100644
index 0000000000..68196f293d
--- /dev/null
+++ b/example/GetStarted/GetStarted/Program.cs
@@ -0,0 +1,125 @@
+using System.Data;
+using dotenv.net;
+using GetStarted;
+
+DotEnv.Load(); // 加载 .env 文件中的环境变量
+
+var connString = Environment.GetEnvironmentVariable("GaussDBConnString");
+ArgumentNullException.ThrowIfNull(connString);
+// ReSharper disable AccessToDisposedClosure
+// Dispose will be handled
+await using var conn = new GaussDBConnection(connString);
+if (conn.State is ConnectionState.Closed)
+{
+ await conn.OpenAsync();
+}
+
+Console.WriteLine($@"Connection state: {conn.State}");
+
+
+// 配置DbContext
+var optionsBuilder = new DbContextOptionsBuilder();
+optionsBuilder.UseGaussDB(connString)
+ .LogTo(Console.WriteLine) // 开启日志,便于排查详细错误
+ .EnableSensitiveDataLogging(); // 开发环境临时开启,生产环境关闭
+
+await using var ctx = new GaussDBDbContext(optionsBuilder.Options);
+
+// ⚠️开发时建议使用:清空旧表并重新建表
+try
+{
+ if (await ctx.Database.CanConnectAsync())
+ {
+ Console.WriteLine("📦 检测到数据库存在,准备删除...");
+ await ctx.Database.EnsureDeletedAsync(); // 删除数据库
+ Console.WriteLine("✅ 数据库已成功删除");
+ }
+ else
+ {
+ Console.WriteLine("ℹ️ 数据库不存在,无需删除");
+ }
+}
+catch (Exception e)
+{
+ Console.WriteLine(e.Message + e.StackTrace);
+}
+
+// 然后创建数据库结构
+var CreateSuccess = await ctx.Database.EnsureCreatedAsync(); // 创建数据库结构
+Console.WriteLine("✅ 数据库结构已创建" + CreateSuccess);
+
+if (!CreateSuccess)
+{
+ // ✅ 手动创建表
+ await CreateTable(ctx);
+}
+
+// 插入初始数据
+ctx.Employees?.AddRange(
+ new Employee { Name = "John", Age = 30 },
+ new Employee { Name = "Alice", Age = 16 },
+ new Employee { Name = "Mike", Age = 24 }
+);
+await ctx.SaveChangesAsync(); // 提交更改
+Console.WriteLine("✅ 初始数据已插入");
+
+// 查询所有员工
+await QueryTest(ctx);
+// 更新 Alice 的年龄为 18
+var alice = await ctx.Employees.FirstOrDefaultAsync(e => e.Name == "Alice");
+if (alice != null)
+{
+ alice.Age = 18;
+ await ctx.SaveChangesAsync();
+ Console.WriteLine("✅ 已更新 Alice 的年龄为 18");
+}
+
+// 查询年龄大于等于 18 的员工
+await QueryTest(ctx, e => e.Age >= 18);
+
+// 删除年龄大于 10 的员工
+var toDelete = await ctx.Employees.Where(e => e.Age > 10).ToListAsync();
+ctx.Employees.RemoveRange(toDelete);
+await ctx.SaveChangesAsync();
+Console.WriteLine("✅ 删除了年龄 > 10 的员工");
+
+// 查询剩余人数
+var count = await ctx.Employees.CountAsync();
+Console.WriteLine($"📊 当前员工总数: {count}");
+
+Console.WriteLine("🎉 所有操作已完成!");
+
+
+// 封装查询方法,可传入条件表达式
+static async Task QueryTest(GaussDBDbContext ctx, Expression> predicate = null)
+{
+ var query = ctx.Employees?.AsQueryable();
+
+ // 如果有条件,则筛选
+ if (predicate != null)
+ query = query?.Where(predicate);
+
+ if (query == null)
+ return;
+
+ var results = await query.OrderBy(s => s.Age).ToListAsync();
+ foreach (var e in results)
+ {
+ Console.WriteLine($"👤 ID: {e.Id}, Name: {e.Name}, Age: {e.Age}");
+ }
+}
+
+async Task CreateTable(GaussDBDbContext ctx)
+{
+ const string createSql = """
+ CREATE TABLE IF NOT EXISTS employees (
+ id INT PRIMARY KEY,
+ name VARCHAR(128),
+ age INT
+ );
+ """;
+
+ await ctx.Database.ExecuteSqlRawAsync(createSql);
+ Console.WriteLine("✅ 创建表 employees 完成");
+}
+
diff --git a/gaussdb.png b/gaussdb.png
new file mode 100644
index 0000000000..efbd2cd9c4
Binary files /dev/null and b/gaussdb.png differ
diff --git a/global.json b/global.json
index 6a6aeda3e2..b48eef46e3 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
"version": "10.0.100",
- "rollForward": "latestMinor",
- "allowPrerelease": false
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
}
}
diff --git a/postgresql.png b/postgresql.png
deleted file mode 100644
index 3a21b19f7a..0000000000
Binary files a/postgresql.png and /dev/null differ
diff --git a/src/EFCore.GaussDB.NTS/Design/Internal/GaussDBNetTopologySuiteDesignTimeServices.cs b/src/EFCore.GaussDB.NTS/Design/Internal/GaussDBNetTopologySuiteDesignTimeServices.cs
new file mode 100644
index 0000000000..9a7472cd39
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Design/Internal/GaussDBNetTopologySuiteDesignTimeServices.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite.Scaffolding.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteDesignTimeServices : IDesignTimeServices
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
+ => serviceCollection
+ .AddSingleton()
+ .AddSingleton()
+ .TryAddSingleton();
+}
diff --git a/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj b/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj
new file mode 100644
index 0000000000..1078c971a0
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj
@@ -0,0 +1,34 @@
+
+
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite
+
+ Shay Rojansky
+ NetTopologySuite PostGIS spatial support plugin for GaussDB Entity Framework Core provider.
+ npgsql;postgresql;postgres;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql;spatial;postgis;nts
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ build
+
+
+
+
diff --git a/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj.Backup.tmp b/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj.Backup.tmp
new file mode 100644
index 0000000000..04175bb3be
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/EFCore.GaussDB.NTS.csproj.Backup.tmp
@@ -0,0 +1,34 @@
+
+
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite
+
+ Shay Rojansky
+ NetTopologySuite PostGIS spatial support plugin for GaussDB Entity Framework Core provider.
+ npgsql;postgresql;postgres;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql;spatial;postgis;nts
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ build
+
+
+
+
diff --git a/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.cs b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.cs
new file mode 100644
index 0000000000..db7d77c550
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.cs
@@ -0,0 +1,54 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// NetTopologySuite specific extension methods for .
+///
+public static class GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions
+{
+ ///
+ /// Use NetTopologySuite to access SQL Server spatial data.
+ ///
+ ///
+ /// The options builder so that further configuration can be chained.
+ ///
+ public static GaussDBDbContextOptionsBuilder UseNetTopologySuite(
+ this GaussDBDbContextOptionsBuilder optionsBuilder,
+ CoordinateSequenceFactory? coordinateSequenceFactory = null,
+ PrecisionModel? precisionModel = null,
+ Ordinates handleOrdinates = Ordinates.None,
+ bool geographyAsDefault = false)
+ {
+ var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
+
+ var extension = coreOptionsBuilder.Options.FindExtension()
+ ?? new GaussDBNetTopologySuiteOptionsExtension();
+
+ if (coordinateSequenceFactory is not null)
+ {
+ extension = extension.WithCoordinateSequenceFactory(coordinateSequenceFactory);
+ }
+
+ if (precisionModel is not null)
+ {
+ extension = extension.WithPrecisionModel(precisionModel);
+ }
+
+ if (handleOrdinates is not Ordinates.None)
+ {
+ extension = extension.WithHandleOrdinates(handleOrdinates);
+ }
+
+ if (geographyAsDefault)
+ {
+ extension = extension.WithGeographyDefault();
+ }
+
+ ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
+
+ return optionsBuilder;
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbFunctionsExtensions.cs b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbFunctionsExtensions.cs
new file mode 100644
index 0000000000..85e2e4b54b
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteDbFunctionsExtensions.cs
@@ -0,0 +1,76 @@
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Provides GaussDB-specific spatial extension methods on .
+///
+public static class GaussDBNetTopologySuiteDbFunctionsExtensions
+{
+ ///
+ /// Returns a new geometry with its coordinates transformed to a different spatial reference system.
+ /// Translates to ST_Transform(geometry, srid).
+ ///
+ ///
+ /// See https://postgis.net/docs/ST_Transform.html.
+ ///
+ public static TGeometry Transform(this DbFunctions _, TGeometry geometry, int srid)
+ where TGeometry : Geometry
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Transform)));
+
+ ///
+ /// Forces the geometries into a "2-dimensional mode" so that all output representations will only have the X and Y coordinates.
+ /// Translates to ST_Force2D(geometry)
+ ///
+ ///
+ /// See https://postgis.net/docs/ST_Force2D.html.
+ ///
+ public static TGeometry Force2D(this DbFunctions _, TGeometry geometry)
+ where TGeometry : Geometry
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Force2D)));
+
+ ///
+ /// Tests whether the distance from the origin geometry to another is less than or equal to a specified value.
+ /// Translates to ST_DWithin.
+ ///
+ ///
+ /// See https://postgis.net/docs/ST_DWithin.html.
+ ///
+ /// The instance.
+ /// The origin geometry.
+ /// The geometry to check the distance to.
+ /// The distance value to compare.
+ /// Whether to use sphere or spheroid distance measurement.
+ /// if the geometries are less than distance apart.
+ public static bool IsWithinDistance(this DbFunctions _, Geometry geometry, Geometry anotherGeometry, double distance, bool useSpheroid)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(IsWithinDistance)));
+
+ ///
+ /// Returns the minimum distance between the origin geometry and another geometry g.
+ /// Translates to ST_Distance.
+ ///
+ ///
+ /// See https://postgis.net/docs/ST_Distance.html.
+ ///
+ /// The instance.
+ /// The origin geometry.
+ /// The geometry from which to compute the distance.
+ /// Whether to use sphere or spheroid distance measurement.
+ /// The distance between the geometries.
+ public static double Distance(this DbFunctions _, Geometry geometry, Geometry anotherGeometry, bool useSpheroid)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));
+
+ ///
+ /// Returns the 2D distance between two geometries. Used in the "ORDER BY" clause, provides index-assisted nearest-neighbor result
+ /// sets. Translates to <->.
+ ///
+ ///
+ /// See https://postgis.net/docs/ST_Distance.html.
+ ///
+ /// The instance.
+ /// The origin geometry.
+ /// The geometry from which to compute the distance.
+ /// The 2D distance between the geometries.
+ public static double DistanceKnn(this DbFunctions _, Geometry geometry, Geometry anotherGeometry)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(DistanceKnn)));
+}
diff --git a/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteServiceCollectionExtensions.cs b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..ad29b3d5ba
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Extensions/GaussDBNetTopologySuiteServiceCollectionExtensions.cs
@@ -0,0 +1,38 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query.ExpressionTranslators.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite extension methods for .
+///
+public static class GaussDBNetTopologySuiteServiceCollectionExtensions
+{
+ ///
+ /// Adds the services required for NetTopologySuite support in the GaussDB provider for Entity Framework.
+ ///
+ /// The to add services to.
+ /// The same service collection so that multiple calls can be chained.
+ public static IServiceCollection AddEntityFrameworkGaussDBNetTopologySuite(
+ this IServiceCollection serviceCollection)
+ {
+ Check.NotNull(serviceCollection, nameof(serviceCollection));
+
+ new EntityFrameworkGaussDBServicesBuilder(serviceCollection)
+ .TryAdd()
+ .TryAdd(p => p.GetRequiredService())
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAddProviderSpecificServices(
+ x => x.TryAddSingleton());
+
+ return serviceCollection;
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Infrastructure/Internal/GaussDBNetTopologySuiteOptionsExtension.cs b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/GaussDBNetTopologySuiteOptionsExtension.cs
new file mode 100644
index 0000000000..4f371e34b7
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/GaussDBNetTopologySuiteOptionsExtension.cs
@@ -0,0 +1,232 @@
+using System.Text;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteOptionsExtension : IDbContextOptionsExtension
+{
+ private DbContextOptionsExtensionInfo? _info;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual CoordinateSequenceFactory? CoordinateSequenceFactory { get; private set; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual PrecisionModel? PrecisionModel { get; private set; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual Ordinates HandleOrdinates { get; private set; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual bool IsGeographyDefault { get; private set; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteOptionsExtension() { }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected GaussDBNetTopologySuiteOptionsExtension(GaussDBNetTopologySuiteOptionsExtension copyFrom)
+ {
+ IsGeographyDefault = copyFrom.IsGeographyDefault;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual GaussDBNetTopologySuiteOptionsExtension Clone()
+ => new(this);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ApplyServices(IServiceCollection services)
+ => services.AddEntityFrameworkGaussDBNetTopologySuite();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual DbContextOptionsExtensionInfo Info
+ => _info ??= new ExtensionInfo(this);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual GaussDBNetTopologySuiteOptionsExtension WithCoordinateSequenceFactory(
+ CoordinateSequenceFactory? coordinateSequenceFactory)
+ {
+ var clone = Clone();
+
+ clone.CoordinateSequenceFactory = coordinateSequenceFactory;
+
+ return clone;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual GaussDBNetTopologySuiteOptionsExtension WithPrecisionModel(PrecisionModel? precisionModel)
+ {
+ var clone = Clone();
+
+ clone.PrecisionModel = precisionModel;
+
+ return clone;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual GaussDBNetTopologySuiteOptionsExtension WithHandleOrdinates(Ordinates handleOrdinates)
+ {
+ var clone = Clone();
+
+ clone.HandleOrdinates = handleOrdinates;
+
+ return clone;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual GaussDBNetTopologySuiteOptionsExtension WithGeographyDefault(bool isGeographyDefault = true)
+ {
+ var clone = Clone();
+
+ clone.IsGeographyDefault = isGeographyDefault;
+
+ return clone;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void Validate(IDbContextOptions options)
+ {
+ Check.NotNull(options, nameof(options));
+
+ var internalServiceProvider = options.FindExtension()?.InternalServiceProvider;
+ if (internalServiceProvider is not null)
+ {
+ using (var scope = internalServiceProvider.CreateScope())
+ {
+ if (scope.ServiceProvider.GetService>()
+ ?.Any(s => s is GaussDBNetTopologySuiteTypeMappingSourcePlugin)
+ != true)
+ {
+ throw new InvalidOperationException(
+ $"{nameof(GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.UseNetTopologySuite)} requires {nameof(GaussDBNetTopologySuiteServiceCollectionExtensions.AddEntityFrameworkGaussDBNetTopologySuite)} to be called on the internal service provider used.");
+ }
+ }
+ }
+ }
+
+ private sealed class ExtensionInfo(IDbContextOptionsExtension extension) : DbContextOptionsExtensionInfo(extension)
+ {
+ private string? _logFragment;
+
+ private new GaussDBNetTopologySuiteOptionsExtension Extension
+ => (GaussDBNetTopologySuiteOptionsExtension)base.Extension;
+
+ public override bool IsDatabaseProvider
+ => false;
+
+ public override int GetServiceProviderHashCode()
+ => Extension.IsGeographyDefault.GetHashCode();
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => other is ExtensionInfo otherInfo
+ && ReferenceEquals(Extension.CoordinateSequenceFactory, otherInfo.Extension.CoordinateSequenceFactory)
+ && ReferenceEquals(Extension.PrecisionModel, otherInfo.Extension.PrecisionModel)
+ && Extension.HandleOrdinates == otherInfo.Extension.HandleOrdinates
+ && Extension.IsGeographyDefault == otherInfo.Extension.IsGeographyDefault;
+
+ public override void PopulateDebugInfo(IDictionary debugInfo)
+ {
+ Check.NotNull(debugInfo, nameof(debugInfo));
+
+ var prefix = "GaussDB:" + nameof(GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.UseNetTopologySuite);
+ debugInfo[prefix] = "1";
+ debugInfo[$"{prefix}:{nameof(IsGeographyDefault)}"] = Extension.IsGeographyDefault.ToString();
+ }
+
+ public override string LogFragment
+ {
+ get
+ {
+ if (_logFragment is null)
+ {
+ var builder = new StringBuilder("using NetTopologySuite");
+ if (Extension.IsGeographyDefault)
+ {
+ builder.Append(" (geography by default)");
+ }
+
+ builder.Append(' ');
+
+ _logFragment = builder.ToString();
+ }
+
+ return _logFragment;
+ }
+ }
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Infrastructure/Internal/IGaussDBNetTopologySuiteSingletonOptions.cs b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/IGaussDBNetTopologySuiteSingletonOptions.cs
new file mode 100644
index 0000000000..412a687795
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/IGaussDBNetTopologySuiteSingletonOptions.cs
@@ -0,0 +1,41 @@
+// ReSharper disable once CheckNamespace
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+///
+/// Represents options for GaussDB NetTopologySuite that can only be set at the singleton level.
+///
+public interface IGaussDBNetTopologySuiteSingletonOptions : ISingletonOptions
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ CoordinateSequenceFactory? CoordinateSequenceFactory { get; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ PrecisionModel? PrecisionModel { get; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ Ordinates HandleOrdinates { get; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ bool IsGeographyDefault { get; }
+}
diff --git a/src/EFCore.PG.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs
similarity index 79%
rename from src/EFCore.PG.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs
rename to src/EFCore.GaussDB.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs
index e8b06a8353..d8a5187cf3 100644
--- a/src/EFCore.PG.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs
+++ b/src/EFCore.GaussDB.NTS/Infrastructure/Internal/NetTopologySuiteDataSourceConfigurationPlugin.cs
@@ -1,4 +1,4 @@
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -6,8 +6,8 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class NetTopologySuiteDataSourceConfigurationPlugin(INpgsqlNetTopologySuiteSingletonOptions options)
- : INpgsqlDataSourceConfigurationPlugin
+public class NetTopologySuiteDataSourceConfigurationPlugin(IGaussDBNetTopologySuiteSingletonOptions options)
+ : IGaussDBDataSourceConfigurationPlugin
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -15,7 +15,7 @@ public class NetTopologySuiteDataSourceConfigurationPlugin(INpgsqlNetTopologySui
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public void Configure(NpgsqlDataSourceBuilder npgsqlDataSourceBuilder)
+ public void Configure(GaussDBDataSourceBuilder npgsqlDataSourceBuilder)
=> npgsqlDataSourceBuilder.UseNetTopologySuite(
options.CoordinateSequenceFactory,
options.PrecisionModel,
diff --git a/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteConventionSetPlugin.cs b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteConventionSetPlugin.cs
new file mode 100644
index 0000000000..4ee9657882
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteConventionSetPlugin.cs
@@ -0,0 +1,25 @@
+// ReSharper disable once CheckNamespace
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteConventionSetPlugin : IConventionSetPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ConventionSet ModifyConventions(ConventionSet conventionSet)
+ {
+ conventionSet.ModelFinalizingConventions.Add(new GaussDBNetTopologySuiteExtensionAddingConvention());
+
+ return conventionSet;
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteExtensionAddingConvention.cs b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteExtensionAddingConvention.cs
new file mode 100644
index 0000000000..1a12069ff2
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteExtensionAddingConvention.cs
@@ -0,0 +1,21 @@
+// ReSharper disable once CheckNamespace
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteExtensionAddingConvention : IModelFinalizingConvention
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext context)
+ => modelBuilder.HasPostgresExtension("postgis");
+}
diff --git a/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteSingletonOptions.cs b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteSingletonOptions.cs
new file mode 100644
index 0000000000..5f0215a6e3
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Internal/GaussDBNetTopologySuiteSingletonOptions.cs
@@ -0,0 +1,35 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Internal;
+
+///
+public class GaussDBNetTopologySuiteSingletonOptions : IGaussDBNetTopologySuiteSingletonOptions
+{
+ ///
+ public virtual CoordinateSequenceFactory? CoordinateSequenceFactory { get; set; }
+
+ ///
+ public virtual PrecisionModel? PrecisionModel { get; set; }
+
+ ///
+ public virtual Ordinates HandleOrdinates { get; set; }
+
+ ///
+ public virtual bool IsGeographyDefault { get; set; }
+
+ ///
+ public virtual void Initialize(IDbContextOptions options)
+ {
+ var npgsqlNtsOptions = options.FindExtension()
+ ?? new GaussDBNetTopologySuiteOptionsExtension();
+
+ CoordinateSequenceFactory = npgsqlNtsOptions.CoordinateSequenceFactory;
+ PrecisionModel = npgsqlNtsOptions.PrecisionModel;
+ HandleOrdinates = npgsqlNtsOptions.HandleOrdinates;
+ IsGeographyDefault = npgsqlNtsOptions.IsGeographyDefault;
+ }
+
+ ///
+ public virtual void Validate(IDbContextOptions options) { }
+}
diff --git a/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000000..5ecf7343b3
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteAggregateMethodCallTranslatorPlugin.cs
@@ -0,0 +1,155 @@
+using NetTopologySuite.Algorithm;
+using NetTopologySuite.Geometries.Utilities;
+using NetTopologySuite.Operation.Union;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Query.ExpressionTranslators.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteAggregateMethodCallTranslatorPlugin : IAggregateMethodCallTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteAggregateMethodCallTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ if (sqlExpressionFactory is not GaussDBExpressionFactory npgsqlSqlExpressionFactory)
+ {
+ throw new ArgumentException($"Must be an {nameof(GaussDBExpressionFactory)}", nameof(sqlExpressionFactory));
+ }
+
+ Translators =
+ [
+ new GaussDBNetTopologySuiteAggregateMethodTranslator(npgsqlSqlExpressionFactory, typeMappingSource)
+ ];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteAggregateMethodTranslator : IAggregateMethodCallTranslator
+{
+ private static readonly MethodInfo GeometryCombineMethod
+ = typeof(GeometryCombiner).GetRuntimeMethod(nameof(GeometryCombiner.Combine), [typeof(IEnumerable)])!;
+
+ private static readonly MethodInfo ConvexHullMethod
+ = typeof(ConvexHull).GetRuntimeMethod(nameof(ConvexHull.Create), [typeof(IEnumerable)])!;
+
+ private static readonly MethodInfo UnionMethod
+ = typeof(UnaryUnionOp).GetRuntimeMethod(nameof(UnaryUnionOp.Union), [typeof(IEnumerable)])!;
+
+ private static readonly MethodInfo EnvelopeCombineMethod
+ = typeof(EnvelopeCombiner).GetRuntimeMethod(nameof(EnvelopeCombiner.CombineAsGeometry), [typeof(IEnumerable)])!;
+
+ private readonly GaussDBExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteAggregateMethodTranslator(
+ GaussDBExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _typeMappingSource = typeMappingSource;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SqlExpression? Translate(
+ MethodInfo method,
+ EnumerableExpression source,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (source.Selector is not SqlExpression sqlExpression)
+ {
+ return null;
+ }
+
+ if (method == ConvexHullMethod)
+ {
+ // PostGIS has no built-in aggregate convex hull, but we can simply apply ST_Collect beforehand as recommended in the docs
+ // https://postgis.net/docs/ST_ConvexHull.html
+ return _sqlExpressionFactory.Function(
+ "ST_ConvexHull",
+ [
+ _sqlExpressionFactory.AggregateFunction(
+ "ST_Collect",
+ [sqlExpression],
+ source,
+ nullable: true,
+ argumentsPropagateNullability: [false],
+ typeof(Geometry),
+ GetMapping())
+ ],
+ nullable: true,
+ argumentsPropagateNullability: [true],
+ typeof(Geometry),
+ GetMapping());
+ }
+
+ if (method == EnvelopeCombineMethod)
+ {
+ // ST_Extent returns a PostGIS box2d, which isn't a geometry and has no binary output function.
+ // Convert it to a geometry first.
+ return _sqlExpressionFactory.Convert(
+ _sqlExpressionFactory.AggregateFunction(
+ "ST_Extent",
+ [sqlExpression],
+ source,
+ nullable: true,
+ argumentsPropagateNullability: [false],
+ typeof(Geometry),
+ GetMapping()),
+ typeof(Geometry), GetMapping());
+ }
+
+ if (method == UnionMethod || method == GeometryCombineMethod)
+ {
+ return _sqlExpressionFactory.AggregateFunction(
+ method == UnionMethod ? "ST_Union" : "ST_Collect",
+ [sqlExpression],
+ source,
+ nullable: true,
+ argumentsPropagateNullability: [false],
+ typeof(Geometry),
+ GetMapping());
+ }
+
+ return null;
+
+ RelationalTypeMapping? GetMapping()
+ => _typeMappingSource.FindMapping(typeof(Geometry), sqlExpression.TypeMapping?.StoreType ?? "geometry");
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMemberTranslatorPlugin.cs b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMemberTranslatorPlugin.cs
new file mode 100644
index 0000000000..5584979682
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMemberTranslatorPlugin.cs
@@ -0,0 +1,187 @@
+// ReSharper disable once CheckNamespace
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Query.ExpressionTranslators.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteMemberTranslatorPlugin : IMemberTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteMemberTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ Translators = [new GaussDBGeometryMemberTranslator(sqlExpressionFactory, typeMappingSource)];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBGeometryMemberTranslator : IMemberTranslator
+{
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+ private readonly CaseWhenClause[] _ogcGeometryTypeWhenThenList;
+
+ private static readonly bool[][] TrueArrays = [[], [true], [true, true], [true, true, true]];
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBGeometryMemberTranslator(
+ ISqlExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _typeMappingSource = typeMappingSource;
+
+ _ogcGeometryTypeWhenThenList =
+ [
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_CircularString"), _sqlExpressionFactory.Constant(OgcGeometryType.CircularString)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_CompoundCurve"), _sqlExpressionFactory.Constant(OgcGeometryType.CompoundCurve)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_CurvePolygon"), _sqlExpressionFactory.Constant(OgcGeometryType.CurvePolygon)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_GeometryCollection"),
+ _sqlExpressionFactory.Constant(OgcGeometryType.GeometryCollection)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_LineString"), _sqlExpressionFactory.Constant(OgcGeometryType.LineString)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_MultiCurve"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiCurve)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_MultiLineString"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiLineString)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_MultiPoint"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPoint)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_MultiPolygon"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiPolygon)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_MultiSurface"), _sqlExpressionFactory.Constant(OgcGeometryType.MultiSurface)),
+ new CaseWhenClause(_sqlExpressionFactory.Constant("ST_Point"), _sqlExpressionFactory.Constant(OgcGeometryType.Point)),
+ new CaseWhenClause(_sqlExpressionFactory.Constant("ST_Polygon"), _sqlExpressionFactory.Constant(OgcGeometryType.Polygon)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.Constant("ST_PolyhedralSurface"),
+ _sqlExpressionFactory.Constant(OgcGeometryType.PolyhedralSurface)),
+ new CaseWhenClause(_sqlExpressionFactory.Constant("ST_Tin"), _sqlExpressionFactory.Constant(OgcGeometryType.TIN))
+ ];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ var declaringType = member.DeclaringType;
+
+ if (instance is null || !typeof(Geometry).IsAssignableFrom(declaringType))
+ {
+ return null;
+ }
+
+ var typeMapping = instance.TypeMapping;
+ Debug.Assert(typeMapping is not null, "Instance must have typeMapping assigned.");
+ var storeType = instance.TypeMapping!.StoreType;
+
+ if (typeof(Point).IsAssignableFrom(declaringType))
+ {
+ var function = member.Name switch
+ {
+ nameof(Point.X) => "ST_X",
+ nameof(Point.Y) => "ST_Y",
+ nameof(Point.Z) => "ST_Z",
+ nameof(Point.M) => "ST_M",
+ _ => null
+ };
+
+ if (function is not null)
+ {
+ return Function(function, [instance], typeof(double));
+ }
+ }
+
+ if (typeof(LineString).IsAssignableFrom(declaringType))
+ {
+ if (member.Name == "Count")
+ {
+ return Function("ST_NumPoints", [instance], typeof(int));
+ }
+ }
+
+ return member.Name switch
+ {
+ nameof(Geometry.Area) => Function("ST_Area", [instance], typeof(double)),
+ nameof(Geometry.Boundary) => Function("ST_Boundary", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Centroid) => Function("ST_Centroid", [instance], typeof(Point), ResultGeometryMapping()),
+ nameof(GeometryCollection.Count) => Function("ST_NumGeometries", [instance], typeof(int)),
+ nameof(Geometry.Dimension) => Function("ST_Dimension", [instance], typeof(Dimension)),
+ nameof(LineString.EndPoint) => Function("ST_EndPoint", [instance], typeof(Point), ResultGeometryMapping()),
+ nameof(Geometry.Envelope) => Function("ST_Envelope", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Polygon.ExteriorRing) => Function("ST_ExteriorRing", [instance], typeof(LineString), ResultGeometryMapping()),
+ nameof(Geometry.GeometryType) => Function("GeometryType", [instance], typeof(string)),
+ nameof(LineString.IsClosed) => Function("ST_IsClosed", [instance], typeof(bool)),
+ nameof(Geometry.IsEmpty) => Function("ST_IsEmpty", [instance], typeof(bool)),
+ nameof(LineString.IsRing) => Function("ST_IsRing", [instance], typeof(bool)),
+ nameof(Geometry.IsSimple) => Function("ST_IsSimple", [instance], typeof(bool)),
+ nameof(Geometry.IsValid) => Function("ST_IsValid", [instance], typeof(bool)),
+ nameof(Geometry.Length) => Function("ST_Length", [instance], typeof(double)),
+ nameof(Geometry.NumGeometries) => Function("ST_NumGeometries", [instance], typeof(int)),
+ nameof(Polygon.NumInteriorRings) => Function("ST_NumInteriorRings", [instance], typeof(int)),
+ nameof(Geometry.NumPoints) => Function("ST_NumPoints", [instance], typeof(int)),
+ nameof(Geometry.PointOnSurface) => Function("ST_PointOnSurface", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.InteriorPoint) => Function("ST_PointOnSurface", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.SRID) => Function("ST_SRID", [instance], typeof(int)),
+ nameof(LineString.StartPoint) => Function("ST_StartPoint", [instance], typeof(Point), ResultGeometryMapping()),
+
+ nameof(Geometry.OgcGeometryType) => _sqlExpressionFactory.Case(
+ Function("ST_GeometryType", [instance], typeof(string)),
+ _ogcGeometryTypeWhenThenList,
+ elseResult: null),
+
+ _ => null
+ };
+
+ SqlExpression Function(string name, SqlExpression[] arguments, Type returnType, RelationalTypeMapping? typeMapping = null)
+ => _sqlExpressionFactory.Function(
+ name, arguments,
+ nullable: true, argumentsPropagateNullability: TrueArrays[arguments.Length],
+ returnType, typeMapping);
+
+ RelationalTypeMapping ResultGeometryMapping()
+ {
+ Debug.Assert(typeof(Geometry).IsAssignableFrom(returnType));
+ return _typeMappingSource.FindMapping(returnType, storeType)!;
+ }
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMethodCallTranslatorPlugin.cs b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000000..8be22b911a
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Query/ExpressionTranslators/Internal/GaussDBNetTopologySuiteMethodCallTranslatorPlugin.cs
@@ -0,0 +1,248 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query.Expressions;
+using ExpressionExtensions = Microsoft.EntityFrameworkCore.Query.ExpressionExtensions;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Query.ExpressionTranslators.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteMethodCallTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ if (sqlExpressionFactory is not GaussDBExpressionFactory npgsqlSqlExpressionFactory)
+ {
+ throw new ArgumentException($"Must be an {nameof(GaussDBExpressionFactory)}", nameof(sqlExpressionFactory));
+ }
+
+ Translators = [new GaussDBGeometryMethodTranslator(npgsqlSqlExpressionFactory, typeMappingSource)];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBGeometryMethodTranslator : IMethodCallTranslator
+{
+ private readonly GaussDBExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
+ private static readonly bool[][] TrueArrays =
+ [
+ [], [true], [true, true], [true, true, true], [true, true, true, true]
+ ];
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBGeometryMethodTranslator(
+ GaussDBExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _typeMappingSource = typeMappingSource;
+ }
+
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ => method.DeclaringType switch
+ {
+ var t when typeof(Geometry).IsAssignableFrom(t) && instance is not null
+ => TranslateGeometryMethod(instance, method, arguments),
+
+ var t when t == typeof(GaussDBNetTopologySuiteDbFunctionsExtensions)
+ => TranslateDbFunction(method, arguments),
+
+ // This handles the collection indexer (geom_collection[x] -> ST_GeometryN(geom_collection, x + 1))
+ // This is needed as a special case because EF transforms the indexer into a call to Enumerable.ElementAt
+ var t when t == typeof(Enumerable)
+ && method.Name is nameof(Enumerable.ElementAt)
+ && method.ReturnType == typeof(Geometry)
+ && arguments is [var collection, var index]
+ && _typeMappingSource.FindMapping(typeof(Geometry), collection.TypeMapping!.StoreType) is RelationalTypeMapping geometryTypeMapping
+ => _sqlExpressionFactory.Function(
+ "ST_GeometryN",
+ [collection, OneBased(index)],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[2],
+ method.ReturnType,
+ geometryTypeMapping),
+
+ _ => null
+ };
+
+ private SqlExpression? TranslateDbFunction(
+ MethodInfo method,
+ IReadOnlyList arguments)
+ => method.Name switch
+ {
+ nameof(GaussDBNetTopologySuiteDbFunctionsExtensions.Transform) => _sqlExpressionFactory.Function(
+ "ST_Transform",
+ [arguments[1], arguments[2]],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[2],
+ method.ReturnType,
+ arguments[1].TypeMapping),
+
+ nameof(GaussDBNetTopologySuiteDbFunctionsExtensions.Force2D) => _sqlExpressionFactory.Function(
+ "ST_Force2D",
+ [arguments[1]],
+ nullable: true,
+ TrueArrays[1],
+ method.ReturnType,
+ arguments[1].TypeMapping),
+
+ nameof(GaussDBNetTopologySuiteDbFunctionsExtensions.DistanceKnn) => _sqlExpressionFactory.MakePostgresBinary(
+ GaussDBExpressionType.Distance,
+ arguments[1],
+ arguments[2]),
+
+ nameof(GaussDBNetTopologySuiteDbFunctionsExtensions.Distance) =>
+ TranslateGeometryMethod(arguments[1], method, [arguments[2], arguments[3]]),
+ nameof(GaussDBNetTopologySuiteDbFunctionsExtensions.IsWithinDistance) =>
+ TranslateGeometryMethod(arguments[1], method, [arguments[2], arguments[3], arguments[4]]),
+
+ _ => null
+ };
+
+ private SqlExpression? TranslateGeometryMethod(
+ SqlExpression instance,
+ MethodInfo method,
+ IReadOnlyList arguments)
+ {
+ var typeMapping = ExpressionExtensions.InferTypeMapping(
+ arguments.Prepend(instance).Where(e => typeof(Geometry).IsAssignableFrom(e.Type)).ToArray());
+
+ Debug.Assert(typeMapping is not null, "At least one argument must have typeMapping.");
+ var storeType = typeMapping.StoreType;
+
+ instance = _sqlExpressionFactory.ApplyTypeMapping(instance, _typeMappingSource.FindMapping(instance.Type, storeType));
+
+ var typeMappedArguments = new List();
+ foreach (var argument in arguments)
+ {
+ typeMappedArguments.Add(
+ _sqlExpressionFactory.ApplyTypeMapping(
+ argument,
+ typeof(Geometry).IsAssignableFrom(argument.Type)
+ ? _typeMappingSource.FindMapping(argument.Type, storeType)
+ : _typeMappingSource.FindMapping(argument.Type)));
+ }
+
+ arguments = typeMappedArguments;
+
+ return method.Name switch
+ {
+ nameof(Geometry.AsBinary)
+ => Function("ST_AsBinary", [instance], typeof(byte[])),
+ nameof(Geometry.AsText)
+ => Function("ST_AsText", [instance], typeof(string)),
+ nameof(Geometry.Buffer)
+ => Function("ST_Buffer", new[] { instance }.Concat(arguments).ToArray(), typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Contains)
+ => Function("ST_Contains", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.ConvexHull)
+ => Function("ST_ConvexHull", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.CoveredBy)
+ => Function("ST_CoveredBy", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Covers)
+ => Function("ST_Covers", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Crosses)
+ => Function("ST_Crosses", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Disjoint)
+ => Function("ST_Disjoint", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Difference)
+ => Function("ST_Difference", [instance, arguments[0]], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Distance)
+ => Function("ST_Distance", new[] { instance }.Concat(arguments).ToArray(), typeof(double)),
+ nameof(Geometry.EqualsExact)
+ => Function("ST_OrderingEquals", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.EqualsTopologically)
+ => Function("ST_Equals", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.GetGeometryN)
+ => Function("ST_GeometryN", [instance, OneBased(arguments[0])], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Polygon.GetInteriorRingN)
+ => Function("ST_InteriorRingN", [instance, OneBased(arguments[0])], typeof(Geometry), ResultGeometryMapping()),
+ nameof(LineString.GetPointN)
+ => Function("ST_PointN", [instance, OneBased(arguments[0])], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Intersection)
+ => Function("ST_Intersection", [instance, arguments[0]], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Intersects)
+ => Function("ST_Intersects", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.IsWithinDistance)
+ => Function("ST_DWithin", new[] { instance }.Concat(arguments).ToArray(), typeof(bool)),
+ nameof(Geometry.Normalized)
+ => Function("ST_Normalize", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Overlaps)
+ => Function("ST_Overlaps", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Relate)
+ => Function("ST_Relate", [instance, arguments[0], arguments[1]], typeof(bool)),
+ nameof(Geometry.Reverse)
+ => Function("ST_Reverse", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.SymmetricDifference)
+ => Function("ST_SymDifference", [instance, arguments[0]], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.ToBinary)
+ => Function("ST_AsBinary", [instance], typeof(byte[])),
+ nameof(Geometry.ToText)
+ => Function("ST_AsText", [instance], typeof(string)),
+ nameof(Geometry.Touches)
+ => Function("ST_Touches", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Within)
+ => Function("ST_Within", [instance, arguments[0]], typeof(bool)),
+ nameof(Geometry.Union) when arguments.Count == 0
+ => Function("ST_UnaryUnion", [instance], typeof(Geometry), ResultGeometryMapping()),
+ nameof(Geometry.Union) when arguments.Count == 1
+ => Function("ST_Union", [instance, arguments[0]], typeof(Geometry), ResultGeometryMapping()),
+
+ _ => null
+ };
+
+ SqlExpression Function(string name, SqlExpression[] arguments, Type returnType, RelationalTypeMapping? typeMapping = null)
+ => _sqlExpressionFactory.Function(
+ name, arguments,
+ nullable: true, argumentsPropagateNullability: TrueArrays[arguments.Length],
+ returnType, typeMapping);
+
+ RelationalTypeMapping ResultGeometryMapping()
+ {
+ Debug.Assert(typeof(Geometry).IsAssignableFrom(method.ReturnType));
+ return _typeMappingSource.FindMapping(method.ReturnType, storeType)!;
+ }
+ }
+
+ // NetTopologySuite uses 0-based indexing, but PostGIS uses 1-based
+ private SqlExpression OneBased(SqlExpression arg)
+ => arg is SqlConstantExpression constant
+ ? _sqlExpressionFactory.Constant((int)constant.Value! + 1, constant.TypeMapping)
+ : _sqlExpressionFactory.Add(arg, _sqlExpressionFactory.Constant(1));
+}
diff --git a/src/EFCore.GaussDB.NTS/README.md b/src/EFCore.GaussDB.NTS/README.md
new file mode 100644
index 0000000000..8a641dc96e
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/README.md
@@ -0,0 +1,43 @@
+# GaussDB Entity Framework Core provider for GaussDB
+
+HuaweiCloud.EntityFrameworkCore.GaussDB is the open source EF Core provider for GaussDB. It allows you to interact with GaussDB via the most widely-used .NET O/RM from Microsoft, and use familiar LINQ syntax to express queries.
+
+This package is a plugin which allows you to interact with spatial data provided by the GaussDB [PostGIS extension](https://postgis.net); PostGIS is a mature, standard extension considered to provide top-of-the-line database spatial features. On the .NET side, the plugin adds support for the types from the [NetTopologySuite library](https://github.com/NetTopologySuite/NetTopologySuite), allowing you to read and write them directly to GaussDB.
+
+To use the plugin, simply add `UseNetTopologySuite` as below and use NetTopologySuite types in your entity properties:
+
+```csharp
+await using var ctx = new BlogContext();
+await ctx.Database.EnsureDeletedAsync();
+await ctx.Database.EnsureCreatedAsync();
+
+// Insert a Blog
+ctx.Cities.Add(new()
+{
+ Name = "FooCity",
+ Center = new Point(10, 10)
+});
+await ctx.SaveChangesAsync();
+
+// Query all cities with the given center point
+var newBlogs = await ctx.Cities.Where(b => b.Center == new Point(10, 10)).ToListAsync();
+
+public class BlogContext : DbContext
+{
+ public DbSet Cities { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseGaussDB(
+ @"Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase",
+ o => o.UseNetTopologySuite());
+}
+
+public class City
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public Point Center { get; set; }
+}
+```
+
+The plugin also supports translating many NetTopologySuite methods and properties into corresponding PostGIS operations. For more information, see the [NetTopologySuite plugin documentation page](https://www.npgsql.org/efcore/mapping/nts.html).
diff --git a/src/EFCore.GaussDB.NTS/Scaffolding/Internal/GaussDBNetTopologySuiteCodeGeneratorPlugin.cs b/src/EFCore.GaussDB.NTS/Scaffolding/Internal/GaussDBNetTopologySuiteCodeGeneratorPlugin.cs
new file mode 100644
index 0000000000..cac7708191
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Scaffolding/Internal/GaussDBNetTopologySuiteCodeGeneratorPlugin.cs
@@ -0,0 +1,30 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite.Scaffolding.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
+{
+ private static readonly MethodInfo _useNetTopologySuiteMethodInfo
+ = typeof(GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBNetTopologySuiteDbContextOptionsBuilderExtensions.UseNetTopologySuite),
+ typeof(GaussDBDbContextOptionsBuilder),
+ typeof(CoordinateSequenceFactory),
+ typeof(PrecisionModel),
+ typeof(Ordinates),
+ typeof(bool));
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override MethodCallCodeFragment GenerateProviderOptions()
+ => new(_useNetTopologySuiteMethodInfo);
+}
diff --git a/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBGeometryTypeMapping.cs b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBGeometryTypeMapping.cs
new file mode 100644
index 0000000000..b3e8c5996b
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBGeometryTypeMapping.cs
@@ -0,0 +1,126 @@
+using System.Data.Common;
+using System.Text;
+using JetBrains.Annotations;
+using NetTopologySuite.IO;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+[UsedImplicitly]
+public class GaussDBGeometryTypeMapping : RelationalGeometryTypeMapping, IGaussDBTypeMapping
+{
+ private readonly bool _isGeography;
+
+ ///
+ public virtual GaussDBDbType GaussDBDbType
+ => _isGeography ? GaussDBDbType.Geography : GaussDBDbType.Geometry;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBGeometryTypeMapping(string storeType, bool isGeography)
+ : base(converter: null, storeType, GaussDBJsonGeometryWktReaderWriter.Instance)
+ {
+ _isGeography = isGeography;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected GaussDBGeometryTypeMapping(RelationalTypeMappingParameters parameters)
+ : base(parameters, converter: null)
+ {
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
+ => new GaussDBGeometryTypeMapping(parameters);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override void ConfigureParameter(DbParameter parameter)
+ {
+ base.ConfigureParameter(parameter);
+
+ ((GaussDBParameter)parameter).GaussDBDbType = GaussDBDbType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override string GenerateNonNullSqlLiteral(object value)
+ {
+ var geometry = (Geometry)value;
+ var builder = new StringBuilder();
+
+ builder
+ .Append(_isGeography ? "GEOGRAPHY" : "GEOMETRY")
+ .Append(" '");
+
+ if (geometry.SRID > 0)
+ {
+ builder
+ .Append("SRID=")
+ .Append(geometry.SRID)
+ .Append(';');
+ }
+
+ builder
+ .Append(geometry.AsText())
+ .Append('\'');
+
+ return builder.ToString();
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override string AsText(object value)
+ => ((Geometry)value).AsText();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override int GetSrid(object value)
+ => ((Geometry)value).SRID;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override Type WktReaderType
+ => typeof(WKTReader);
+}
diff --git a/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBJsonGeometryWktReaderWriter.cs b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBJsonGeometryWktReaderWriter.cs
new file mode 100644
index 0000000000..e1f9348aa8
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBJsonGeometryWktReaderWriter.cs
@@ -0,0 +1,47 @@
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore.Storage.Json;
+using NetTopologySuite.IO;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+///
+/// Reads and writes JSON using the well-known-text format for values.
+///
+public sealed class GaussDBJsonGeometryWktReaderWriter : JsonValueReaderWriter
+{
+ private static readonly PropertyInfo InstanceProperty = typeof(GaussDBJsonGeometryWktReaderWriter).GetProperty(nameof(Instance))!;
+
+ private static readonly WKTReader WktReader = new();
+
+ ///
+ /// The singleton instance of this stateless reader/writer.
+ ///
+ public static GaussDBJsonGeometryWktReaderWriter Instance { get; } = new();
+
+ private GaussDBJsonGeometryWktReaderWriter()
+ {
+ }
+
+ ///
+ public override Geometry FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
+ => WktReader.Read(manager.CurrentReader.GetString());
+
+ ///
+ public override void ToJsonTyped(Utf8JsonWriter writer, Geometry value)
+ {
+ var wkt = value.ToText();
+
+ // If the SRID is defined, prefix the WKT with it (SRID=4326;POINT(-44.3 60.1))
+ // Although this is a GaussDB extension, NetTopologySuite supports it (see #3236)
+ if (value.SRID > 0)
+ {
+ wkt = $"SRID={value.SRID};{wkt}";
+ }
+
+ writer.WriteStringValue(wkt);
+ }
+
+ ///
+ public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
+}
diff --git a/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBNetTopologySuiteTypeMappingSourcePlugin.cs b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBNetTopologySuiteTypeMappingSourcePlugin.cs
new file mode 100644
index 0000000000..aae1421f8e
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/Storage/Internal/GaussDBNetTopologySuiteTypeMappingSourcePlugin.cs
@@ -0,0 +1,204 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNetTopologySuiteTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
+{
+ // Note: we reference the options rather than copying IsGeographyDefault out, because that field is initialized
+ // rather late by SingletonOptionsInitializer
+ private readonly IGaussDBNetTopologySuiteSingletonOptions _options;
+
+ private static bool TryGetClrType(string subtypeName, [NotNullWhen(true)] out Type? clrType)
+ {
+ clrType = subtypeName switch
+ {
+ "POINT" => typeof(Point),
+ "LINESTRING" => typeof(LineString),
+ "POLYGON" => typeof(Polygon),
+ "MULTIPOINT" => typeof(MultiPoint),
+ "MULTILINESTRING" => typeof(MultiLineString),
+ "MULTIPOLYGON" => typeof(MultiPolygon),
+ "GEOMETRYCOLLECTION" => typeof(GeometryCollection),
+ "GEOMETRY" => typeof(Geometry),
+ _ => null
+ };
+
+ return clrType is not null;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNetTopologySuiteTypeMappingSourcePlugin(IGaussDBNetTopologySuiteSingletonOptions options)
+ {
+ _options = Check.NotNull(options, nameof(options));
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
+ {
+ // TODO: Array
+ var clrType = mappingInfo.ClrType;
+ var storeTypeName = mappingInfo.StoreTypeName;
+ var isGeography = _options.IsGeographyDefault;
+
+ if (clrType is not null)
+ {
+ if (!clrType.IsAssignableTo(typeof(Geometry)))
+ {
+ return null;
+ }
+
+ // TODO: if store type is null, consider setting it based on the CLR type, i.e. create GEOMETRY(Point) instead of Geometry when
+ // the CLR property is NTS Point.
+ }
+
+ if (storeTypeName is not null)
+ {
+ if (!TryParseStoreTypeName(storeTypeName, out _, out isGeography, out var parsedSubtype, out _, out _))
+ {
+ return null;
+ }
+
+ clrType ??= parsedSubtype;
+ }
+
+ storeTypeName ??= isGeography ? "geography" : "geometry";
+
+ Check.DebugAssert(clrType is not null, "clrType is not null");
+
+ var typeMapping = (RelationalTypeMapping)Activator.CreateInstance(
+ typeof(GaussDBGeometryTypeMapping<>).MakeGenericType(clrType), storeTypeName, isGeography)!;
+
+ // TODO: for geometry collection support (and why the following is commented out), see #2850.
+
+ // // TODO: Also restrict the element type mapping based on the user-specified store type?
+ // var elementType = clrType == typeof(MultiPoint)
+ // ? typeof(Point)
+ // : clrType == typeof(MultiLineString)
+ // ? typeof(LineString)
+ // : clrType == typeof(MultiPolygon)
+ // ? typeof(Polygon)
+ // : clrType == typeof(GeometryCollection)
+ // ? typeof(Geometry)
+ // : null;
+ //
+ // if (elementType is not null)
+ // {
+ // var elementTypeMapping = FindMapping(new() { ClrType = elementType })!;
+ //
+ // typeMapping = typeMapping.Clone(elementMapping: elementTypeMapping);
+ // }
+
+ return typeMapping;
+ }
+
+ ///
+ /// Given a PostGIS store type name (e.g. GEOMETRY, GEOGRAPHY(Point, 4326), GEOMETRY(LineStringM, 4326)),
+ /// attempts to parse it and return its components.
+ ///
+ public static bool TryParseStoreTypeName(
+ string storeTypeName,
+ out string subtypeName,
+ out bool isGeography,
+ out Type? clrType,
+ out int srid,
+ out Ordinates ordinates)
+ {
+ storeTypeName = storeTypeName.Trim();
+ subtypeName = storeTypeName;
+ isGeography = false;
+ clrType = typeof(Geometry);
+ srid = -1;
+ ordinates = Ordinates.AllOrdinates;
+
+ var openParen = storeTypeName.IndexOf("(", StringComparison.Ordinal);
+
+ var baseType = openParen > 0 ? storeTypeName.Substring(0, openParen).Trim() : storeTypeName;
+
+ if (baseType.Equals("GEOMETRY", StringComparison.OrdinalIgnoreCase))
+ {
+ isGeography = false;
+ }
+ else if (baseType.Equals("GEOGRAPHY", StringComparison.OrdinalIgnoreCase))
+ {
+ isGeography = true;
+ }
+ else
+ {
+ return false;
+ }
+
+ if (openParen == -1)
+ {
+ return true;
+ }
+
+ var closeParen = storeTypeName.IndexOf(")", openParen + 1, StringComparison.Ordinal);
+ if (closeParen != storeTypeName.Length - 1)
+ {
+ return false;
+ }
+
+ var comma = storeTypeName.IndexOf(",", openParen + 1, StringComparison.Ordinal);
+ if (comma == -1)
+ {
+ subtypeName = storeTypeName.Substring(openParen + 1, closeParen - openParen - 1).Trim();
+ }
+ else
+ {
+ subtypeName = storeTypeName.Substring(openParen + 1, comma - openParen - 1).Trim();
+
+ if (!int.TryParse(storeTypeName.Substring(comma + 1, closeParen - comma - 1).Trim(), out srid))
+ {
+ return false;
+ }
+ }
+
+ subtypeName = subtypeName.ToUpper(CultureInfo.InvariantCulture);
+
+ // We have geometry(subtype, srid), parse the subtype (POINT, POINTZ, POINTM, POINTZM...)
+
+ if (TryGetClrType(subtypeName, out clrType))
+ {
+ return true;
+ }
+
+ if (subtypeName.EndsWith("ZM", StringComparison.Ordinal) && TryGetClrType(subtypeName[..^2], out clrType))
+ {
+ ordinates = Ordinates.XYZM;
+ return true;
+ }
+
+ if (subtypeName.EndsWith("M", StringComparison.Ordinal) && TryGetClrType(subtypeName[..^1], out clrType))
+ {
+ ordinates = Ordinates.XYM;
+ return true;
+ }
+
+ if (subtypeName.EndsWith("Z", StringComparison.Ordinal) && TryGetClrType(subtypeName[..^1], out clrType))
+ {
+ ordinates = Ordinates.XYZ;
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/EFCore.GaussDB.NTS/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite.targets b/src/EFCore.GaussDB.NTS/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite.targets
new file mode 100644
index 0000000000..742cb518ea
--- /dev/null
+++ b/src/EFCore.GaussDB.NTS/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite.targets
@@ -0,0 +1,46 @@
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ $(IntermediateOutputPath)EFCoreGaussDBNetTopologySuite$(DefaultLanguageSourceExtension)
+
+
+
+
+
+
+ CompileBefore
+
+
+
+
+ CompileAfter
+
+
+
+
+
+
+ Compile
+
+
+
+
+
+
+ <_Parameter1>HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal.GaussDBNetTopologySuiteDesignTimeServices, HuaweiCloud.EntityFrameworkCore.GaussDB.NetTopologySuite
+ <_Parameter2>HuaweiCloud.EntityFrameworkCore.GaussDB
+
+
+
+
+
+
+
+
diff --git a/src/EFCore.GaussDB.NodaTime/Design/Internal/NpgsqlNodaTimeDesignTimeServices.cs b/src/EFCore.GaussDB.NodaTime/Design/Internal/NpgsqlNodaTimeDesignTimeServices.cs
new file mode 100644
index 0000000000..70000bf60e
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Design/Internal/NpgsqlNodaTimeDesignTimeServices.cs
@@ -0,0 +1,27 @@
+using JetBrains.Annotations;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Scaffolding.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+[UsedImplicitly]
+public class GaussDBNodaTimeDesignTimeServices : IDesignTimeServices
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
+ => serviceCollection
+ .AddSingleton()
+ .AddSingleton();
+}
diff --git a/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj b/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj
new file mode 100644
index 0000000000..abe6e67928
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj
@@ -0,0 +1,33 @@
+
+
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime
+
+ Conan Yao
+ NodaTime support plugin for GaussDB Entity Framework Core provider.
+ gauss;gaussdb;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql;nodatime;date;time
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ build
+
+
+
+
diff --git a/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj.Backup.tmp b/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj.Backup.tmp
new file mode 100644
index 0000000000..db15a12deb
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/EFCore.GaussDB.NodaTime.csproj.Backup.tmp
@@ -0,0 +1,33 @@
+
+
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime
+ HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime
+
+ Shay Rojansky
+ NodaTime support plugin for GaussDB Entity Framework Core provider.
+ npgsql;postgresql;postgres;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql;nodatime;date;time
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ build
+
+
+
+
diff --git a/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbContextOptionsBuilderExtensions.cs b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbContextOptionsBuilderExtensions.cs
new file mode 100644
index 0000000000..d7a503e207
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbContextOptionsBuilderExtensions.cs
@@ -0,0 +1,30 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// NodaTime specific extension methods for .
+///
+public static class GaussDBNodaTimeDbContextOptionsBuilderExtensions
+{
+ ///
+ /// Configure NodaTime type mappings for Entity Framework.
+ ///
+ /// The options builder so that further configuration can be chained.
+ public static GaussDBDbContextOptionsBuilder UseNodaTime(
+ this GaussDBDbContextOptionsBuilder optionsBuilder)
+ {
+ Check.NotNull(optionsBuilder, nameof(optionsBuilder));
+
+ var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
+
+ var extension = coreOptionsBuilder.Options.FindExtension()
+ ?? new GaussDBNodaTimeOptionsExtension();
+
+ ((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
+
+ return optionsBuilder;
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbFunctionsExtensions.cs b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbFunctionsExtensions.cs
new file mode 100644
index 0000000000..2fea658776
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeDbFunctionsExtensions.cs
@@ -0,0 +1,189 @@
+// ReSharper disable once CheckNamespace
+
+namespace Microsoft.EntityFrameworkCore;
+
+///
+/// Provides extension methods supporting NodaTime function translation for GaussDB.
+///
+public static class GaussDBNodaTimeDbFunctionsExtensions
+{
+ ///
+ /// Computes the sum of the non-null input intervals. Corresponds to the GaussDB sum aggregate function.
+ ///
+ /// The instance.
+ /// The input values to be summed.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core LINQ query.
+ ///
+ public static Period? Sum(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Sum)));
+
+ ///
+ /// Computes the sum of the non-null input intervals. Corresponds to the GaussDB sum aggregate function.
+ ///
+ /// The instance.
+ /// The input values to be summed.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core LINQ
+ /// query.
+ ///
+ public static Duration? Sum(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Sum)));
+
+ ///
+ /// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the GaussDB avg aggregate function.
+ ///
+ /// The instance.
+ /// The input values to be computed into an average.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core LINQ
+ /// query.
+ ///
+ public static Period? Average(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Average)));
+
+ ///
+ /// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the GaussDB avg aggregate function.
+ ///
+ /// The instance.
+ /// The input values to be computed into an average.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core LINQ
+ /// query.
+ ///
+ public static Duration? Average(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Average)));
+
+ ///
+ /// Returns the distance between two instants as a , particularly suitable for sorting where the appropriate index
+ /// is defined.
+ ///
+ ///
+ /// This requires the btree_gist built-in GaussDB extension, see
+ /// .
+ ///
+ public static int Distance(this DbFunctions _, Instant a, Instant b)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));
+
+ ///
+ /// Returns the distance between two zoned timestamps as a , particularly suitable for sorting where the
+ /// appropriate index is defined.
+ ///
+ ///
+ /// This requires the btree_gist built-in GaussDB extension, see
+ /// .
+ ///
+ public static int Distance(this DbFunctions _, ZonedDateTime a, ZonedDateTime b)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));
+
+ ///
+ /// Returns the distance between two local timestamps as a , particularly suitable for sorting where the
+ /// appropriate index is defined.
+ ///
+ ///
+ /// This requires the btree_gist built-in GaussDB extension, see
+ /// .
+ ///
+ public static int Distance(this DbFunctions _, LocalDateTime a, LocalDateTime b)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));
+
+ ///
+ /// Returns the distance between two dates as a number of days, particularly suitable for sorting where the appropriate index is
+ /// defined.
+ ///
+ ///
+ /// This requires the btree_gist built-in GaussDB extension, see
+ /// .
+ ///
+ public static int Distance(this DbFunctions _, LocalDate a, LocalDate b)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Distance)));
+
+ #region Aggregate functions
+
+ ///
+ /// Computes the union of the non-null input intervals. Corresponds to the GaussDB range_agg aggregate function.
+ ///
+ /// The instance.
+ /// The intervals to be aggregated via union into a multirange.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core LINQ
+ /// query.
+ ///
+ public static Interval[] RangeAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeAgg)));
+
+ ///
+ /// Computes the union of the non-null input date intervals. Corresponds to the GaussDB range_agg aggregate function.
+ ///
+ /// The instance.
+ /// The date intervals to be aggregated via union into a multirange.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF Core
+ /// LINQ query.
+ ///
+ public static DateInterval[] RangeAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeAgg)));
+
+ ///
+ /// Computes the intersection of the non-null input intervals. Corresponds to the GaussDB range_intersect_agg aggregate function.
+ ///
+ /// The instance.
+ /// The intervals on which to perform the intersection operation.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF
+ /// Core LINQ query.
+ ///
+ public static Interval RangeIntersectAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeIntersectAgg)));
+
+ ///
+ /// Computes the intersection of the non-null input date intervals. Corresponds to the GaussDB range_intersect_agg aggregate
+ /// function.
+ ///
+ /// The instance.
+ /// The date intervals on which to perform the intersection operation.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an
+ /// EF Core LINQ query.
+ ///
+ public static DateInterval RangeIntersectAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeIntersectAgg)));
+
+ ///
+ /// Computes the intersection of the non-null input interval multiranges.
+ /// Corresponds to the GaussDB range_intersect_agg aggregate function.
+ ///
+ /// The instance.
+ /// The intervals on which to perform the intersection operation.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of an EF
+ /// Core LINQ query.
+ ///
+ public static Interval[] RangeIntersectAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeIntersectAgg)));
+
+ ///
+ /// Computes the intersection of the non-null input date interval multiranges.
+ /// Corresponds to the GaussDB range_intersect_agg aggregate function.
+ ///
+ /// The instance.
+ /// The date intervals on which to perform the intersection operation.
+ /// GaussDB documentation for aggregate functions.
+ ///
+ /// is only intended for use via SQL translation as part of
+ /// an EF Core LINQ query.
+ ///
+ public static DateInterval[] RangeIntersectAgg(this DbFunctions _, IEnumerable input)
+ => throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(RangeIntersectAgg)));
+
+ #endregion Aggregate functions
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeServiceCollectionExtensions.cs b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..a59879f891
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Extensions/GaussDBNodaTimeServiceCollectionExtensions.cs
@@ -0,0 +1,34 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.Extensions.DependencyInjection;
+
+///
+/// HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime extension methods for .
+///
+public static class GaussDBNodaTimeServiceCollectionExtensions
+{
+ ///
+ /// Adds the services required for NodaTime support in the GaussDB provider for Entity Framework.
+ ///
+ /// The to add services to.
+ /// The same service collection so that multiple calls can be chained.
+ public static IServiceCollection AddEntityFrameworkGaussDBNodaTime(
+ this IServiceCollection serviceCollection)
+ {
+ Check.NotNull(serviceCollection, nameof(serviceCollection));
+
+ new EntityFrameworkGaussDBServicesBuilder(serviceCollection)
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+ .TryAdd();
+
+ return serviceCollection;
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Extensions/TypeExtensions.cs b/src/EFCore.GaussDB.NodaTime/Extensions/TypeExtensions.cs
new file mode 100644
index 0000000000..c99010cac3
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Extensions/TypeExtensions.cs
@@ -0,0 +1,22 @@
+// ReSharper disable once CheckNamespace
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB;
+
+internal static class TypeExtensions
+{
+ internal static bool IsGenericList(this Type type)
+ => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
+
+ internal static bool IsArrayOrGenericList(this Type type)
+ => type.IsArray || type.IsGenericList();
+
+ internal static bool TryGetElementType(this Type type, out Type? elementType)
+ {
+ elementType = type.IsArray
+ ? type.GetElementType()
+ : type.IsGenericList()
+ ? type.GetGenericArguments()[0]
+ : null;
+ return elementType is not null;
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/GaussDBNodaTimeOptionsExtension.cs b/src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/GaussDBNodaTimeOptionsExtension.cs
new file mode 100644
index 0000000000..9676d204cd
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/GaussDBNodaTimeOptionsExtension.cs
@@ -0,0 +1,78 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeOptionsExtension : IDbContextOptionsExtension
+{
+ private DbContextOptionsExtensionInfo? _info;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ApplyServices(IServiceCollection services)
+ => services.AddEntityFrameworkGaussDBNodaTime();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual DbContextOptionsExtensionInfo Info
+ => _info ??= new ExtensionInfo(this);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void Validate(IDbContextOptions options)
+ {
+ var internalServiceProvider = options.FindExtension()?.InternalServiceProvider;
+ if (internalServiceProvider is not null)
+ {
+ using (var scope = internalServiceProvider.CreateScope())
+ {
+ if (scope.ServiceProvider.GetService>()
+ ?.Any(s => s is GaussDBNodaTimeTypeMappingSourcePlugin)
+ != true)
+ {
+ throw new InvalidOperationException(
+ $"{nameof(GaussDBNodaTimeDbContextOptionsBuilderExtensions.UseNodaTime)} requires {nameof(GaussDBNodaTimeServiceCollectionExtensions.AddEntityFrameworkGaussDBNodaTime)} to be called on the internal service provider used.");
+ }
+ }
+ }
+ }
+
+ private sealed class ExtensionInfo(IDbContextOptionsExtension extension) : DbContextOptionsExtensionInfo(extension)
+ {
+ private new GaussDBNodaTimeOptionsExtension Extension
+ => (GaussDBNodaTimeOptionsExtension)base.Extension;
+
+ public override bool IsDatabaseProvider
+ => false;
+
+ public override int GetServiceProviderHashCode()
+ => 0;
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ => true;
+
+ public override void PopulateDebugInfo(IDictionary debugInfo)
+ => debugInfo["GaussDB:" + nameof(GaussDBNodaTimeDbContextOptionsBuilderExtensions.UseNodaTime)] = "1";
+
+ public override string LogFragment
+ => "using NodaTime ";
+ }
+}
diff --git a/src/EFCore.PG.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs b/src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs
similarity index 80%
rename from src/EFCore.PG.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs
rename to src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs
index b6fb4e58d9..33648fbf79 100644
--- a/src/EFCore.PG.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs
+++ b/src/EFCore.GaussDB.NodaTime/Infrastructure/Internal/NodaTimeDataSourceConfigurationPlugin.cs
@@ -1,4 +1,4 @@
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -6,7 +6,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class NodaTimeDataSourceConfigurationPlugin : INpgsqlDataSourceConfigurationPlugin
+public class NodaTimeDataSourceConfigurationPlugin : IGaussDBDataSourceConfigurationPlugin
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -14,6 +14,6 @@ public class NodaTimeDataSourceConfigurationPlugin : INpgsqlDataSourceConfigurat
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public void Configure(NpgsqlDataSourceBuilder npgsqlDataSourceBuilder)
+ public void Configure(GaussDBDataSourceBuilder npgsqlDataSourceBuilder)
=> npgsqlDataSourceBuilder.UseNodaTime();
}
diff --git a/src/EFCore.GaussDB.NodaTime/Properties/AssemblyInfo.cs b/src/EFCore.GaussDB.NodaTime/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..476c7a9b4a
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Properties/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Runtime.CompilerServices;
+
+[assembly:
+ InternalsVisibleTo(
+ "HuaweiCloud.EntityFrameworkCore.GaussDB.FunctionalTests, PublicKey="
+ + "0024000004800000940000000602000000240000525341310004000001000100"
+ + "2b3c590b2a4e3d347e6878dc0ff4d21eb056a50420250c6617044330701d35c9"
+ + "8078a5df97a62d83c9a2db2d072523a8fc491398254c6b89329b8c1dcef43a1e"
+ + "7aa16153bcea2ae9a471145624826f60d7c8e71cd025b554a0177bd935a78096"
+ + "29f0a7afc778ebb4ad033e1bf512c1a9c6ceea26b077bc46cac93800435e77ee")]
diff --git a/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeAggregateMethodCallTranslatorPlugin.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeAggregateMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000000..683079a12f
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeAggregateMethodCallTranslatorPlugin.cs
@@ -0,0 +1,108 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeAggregateMethodCallTranslatorPlugin : IAggregateMethodCallTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeAggregateMethodCallTranslatorPlugin(
+ ISqlExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ {
+ if (sqlExpressionFactory is not GaussDBExpressionFactory npgsqlSqlExpressionFactory)
+ {
+ throw new ArgumentException($"Must be an {nameof(GaussDBExpressionFactory)}", nameof(sqlExpressionFactory));
+ }
+
+ Translators =
+ [
+ new GaussDBNodaTimeAggregateMethodTranslator(npgsqlSqlExpressionFactory, typeMappingSource)
+ ];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeAggregateMethodTranslator : IAggregateMethodCallTranslator
+{
+ private static readonly bool[][] FalseArrays = [[], [false]];
+
+ private readonly GaussDBExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeAggregateMethodTranslator(
+ GaussDBExpressionFactory sqlExpressionFactory,
+ IRelationalTypeMappingSource typeMappingSource)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _typeMappingSource = typeMappingSource;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual SqlExpression? Translate(
+ MethodInfo method,
+ EnumerableExpression source,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (source.Selector is not SqlExpression sqlExpression || method.DeclaringType != typeof(GaussDBNodaTimeDbFunctionsExtensions))
+ {
+ return null;
+ }
+
+ return method.Name switch
+ {
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Sum) => _sqlExpressionFactory.AggregateFunction(
+ "sum", [sqlExpression], source, nullable: true, argumentsPropagateNullability: FalseArrays[1],
+ returnType: sqlExpression.Type, sqlExpression.TypeMapping),
+
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Average) => _sqlExpressionFactory.AggregateFunction(
+ "avg", [sqlExpression], source, nullable: true, argumentsPropagateNullability: FalseArrays[1],
+ returnType: sqlExpression.Type, sqlExpression.TypeMapping),
+
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.RangeAgg) => _sqlExpressionFactory.AggregateFunction(
+ "range_agg", [sqlExpression], source, nullable: true, argumentsPropagateNullability: FalseArrays[1],
+ returnType: method.ReturnType, _typeMappingSource.FindMapping(method.ReturnType)),
+
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.RangeIntersectAgg) => _sqlExpressionFactory.AggregateFunction(
+ "range_intersect_agg", [sqlExpression], source, nullable: true, argumentsPropagateNullability: FalseArrays[1],
+ returnType: sqlExpression.Type, sqlExpression.TypeMapping),
+
+ _ => null
+ };
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeEvaluatableExpressionFilterPlugin.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeEvaluatableExpressionFilterPlugin.cs
new file mode 100644
index 0000000000..bd28001259
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeEvaluatableExpressionFilterPlugin.cs
@@ -0,0 +1,50 @@
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeEvaluatableExpressionFilterPlugin : IEvaluatableExpressionFilterPlugin
+{
+ private static readonly MethodInfo GetCurrentInstantMethod =
+ typeof(SystemClock).GetRuntimeMethod(nameof(SystemClock.GetCurrentInstant), [])!;
+
+ private static readonly MemberInfo SystemClockInstanceMember =
+ typeof(SystemClock).GetMember(nameof(SystemClock.Instance)).FirstOrDefault()!;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual bool IsEvaluatableExpression(Expression expression)
+ {
+ switch (expression)
+ {
+ case MethodCallExpression methodCallExpression when methodCallExpression.Method == GetCurrentInstantMethod:
+ return false;
+
+ case MemberExpression memberExpression:
+ if (memberExpression.Member == SystemClockInstanceMember)
+ {
+ return false;
+ }
+
+ // We support translating certain NodaTime patterns which accept a time zone as a parameter,
+ // e.g. Instant.InZone(timezone), as long as the timezone is expressed as an access on DateTimeZoneProviders.Tzdb.
+ // Prevent this from being evaluated locally and so parameterized, so we can access the member access on
+ // DateTimeZoneProviders and extract the constant (see GaussDBNodaTimeMethodCallTranslatorPlugin)
+ if (memberExpression.Member.DeclaringType == typeof(DateTimeZoneProviders))
+ {
+ return false;
+ }
+
+ break;
+ }
+
+ return true;
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMemberTranslatorPlugin.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMemberTranslatorPlugin.cs
new file mode 100644
index 0000000000..721cbdc1c4
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMemberTranslatorPlugin.cs
@@ -0,0 +1,395 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
+
+///
+/// Provides translation services for members.
+///
+///
+/// See: https://www.postgresql.org/docs/current/static/functions-datetime.html
+///
+public class GaussDBNodaTimeMemberTranslatorPlugin : IMemberTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeMemberTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ Translators =
+ [
+ new GaussDBNodaTimeMemberTranslator(typeMappingSource, (GaussDBExpressionFactory)sqlExpressionFactory)
+ ];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeMemberTranslator : IMemberTranslator
+{
+ private static readonly MemberInfo SystemClock_Instance =
+ typeof(SystemClock).GetRuntimeProperty(nameof(SystemClock.Instance))!;
+
+ private static readonly MemberInfo ZonedDateTime_LocalDateTime =
+ typeof(ZonedDateTime).GetRuntimeProperty(nameof(ZonedDateTime.LocalDateTime))!;
+
+ private static readonly MemberInfo Interval_Start =
+ typeof(Interval).GetRuntimeProperty(nameof(Interval.Start))!;
+
+ private static readonly MemberInfo Interval_End =
+ typeof(Interval).GetRuntimeProperty(nameof(Interval.End))!;
+
+ private static readonly MemberInfo Interval_HasStart =
+ typeof(Interval).GetRuntimeProperty(nameof(Interval.HasStart))!;
+
+ private static readonly MemberInfo Interval_HasEnd =
+ typeof(Interval).GetRuntimeProperty(nameof(Interval.HasEnd))!;
+
+ private static readonly MemberInfo Interval_Duration =
+ typeof(Interval).GetRuntimeProperty(nameof(Interval.Duration))!;
+
+ private static readonly MemberInfo DateInterval_Start =
+ typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.Start))!;
+
+ private static readonly MemberInfo DateInterval_End =
+ typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.End))!;
+
+ private static readonly MemberInfo DateInterval_Length =
+ typeof(DateInterval).GetRuntimeProperty(nameof(DateInterval.Length))!;
+
+ private static readonly MemberInfo DateTimeZoneProviders_TzDb =
+ typeof(DateTimeZoneProviders).GetRuntimeProperty(nameof(DateTimeZoneProviders.Tzdb))!;
+
+ private readonly GaussDBExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+ private readonly RelationalTypeMapping _dateTypeMapping;
+ private readonly RelationalTypeMapping _periodTypeMapping;
+ private readonly RelationalTypeMapping _localDateTimeTypeMapping;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeMemberTranslator(
+ IRelationalTypeMappingSource typeMappingSource,
+ GaussDBExpressionFactory sqlExpressionFactory)
+ {
+ _typeMappingSource = typeMappingSource;
+ _sqlExpressionFactory = sqlExpressionFactory;
+ _dateTypeMapping = typeMappingSource.FindMapping(typeof(LocalDate))!;
+ _periodTypeMapping = typeMappingSource.FindMapping(typeof(Period))!;
+ _localDateTimeTypeMapping = typeMappingSource.FindMapping(typeof(LocalDateTime))!;
+ }
+
+ private static readonly bool[][] TrueArrays = [[], [true], [true, true]];
+
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MemberInfo member,
+ Type returnType,
+ IDiagnosticsLogger logger)
+ {
+ // This is necessary to allow translation of methods on SystemClock.Instance
+ if (member == SystemClock_Instance)
+ {
+ return _sqlExpressionFactory.Constant(SystemClock.Instance);
+ }
+
+ if (member == DateTimeZoneProviders_TzDb)
+ {
+ return PendingDateTimeZoneProviderExpression.Instance;
+ }
+
+ if (instance is null)
+ {
+ return null;
+ }
+
+ var declaringType = member.DeclaringType;
+
+ if (declaringType == typeof(LocalDateTime)
+ || declaringType == typeof(LocalDate)
+ || declaringType == typeof(LocalTime)
+ || declaringType == typeof(Period))
+ {
+ return TranslateDateTime(instance, member);
+ }
+
+ if (declaringType == typeof(ZonedDateTime))
+ {
+ return TranslateZonedDateTime(instance, member, returnType);
+ }
+
+ if (declaringType == typeof(Duration))
+ {
+ return TranslateDuration(instance, member);
+ }
+
+ if (declaringType == typeof(Interval))
+ {
+ return TranslateInterval(instance, member);
+ }
+
+ if (declaringType == typeof(DateInterval))
+ {
+ return TranslateDateInterval(instance, member);
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateDuration(SqlExpression instance, MemberInfo member)
+ {
+ return member.Name switch
+ {
+ nameof(Duration.TotalDays) => TranslateDurationTotalMember(instance, 86400),
+ nameof(Duration.TotalHours) => TranslateDurationTotalMember(instance, 3600),
+ nameof(Duration.TotalMinutes) => TranslateDurationTotalMember(instance, 60),
+ nameof(Duration.TotalSeconds) => GetDatePartExpressionDouble(instance, "epoch"),
+ nameof(Duration.TotalMilliseconds) => TranslateDurationTotalMember(instance, 0.001),
+ nameof(Duration.Days) => GetDatePartExpression(instance, "day"),
+ nameof(Duration.Hours) => GetDatePartExpression(instance, "hour"),
+ nameof(Duration.Minutes) => GetDatePartExpression(instance, "minute"),
+ nameof(Duration.Seconds) => GetDatePartExpression(instance, "second", true),
+ nameof(Duration.Milliseconds) => null, // Too annoying, floating point and sub-millisecond handling
+ _ => null,
+ };
+
+ SqlExpression TranslateDurationTotalMember(SqlExpression instance, double divisor)
+ => _sqlExpressionFactory.Divide(GetDatePartExpressionDouble(instance, "epoch"), _sqlExpressionFactory.Constant(divisor));
+ }
+
+ private SqlExpression? TranslateInterval(SqlExpression instance, MemberInfo member)
+ {
+ if (member == Interval_Start)
+ {
+ return Lower();
+ }
+
+ if (member == Interval_End)
+ {
+ return Upper();
+ }
+
+ if (member == Interval_HasStart)
+ {
+ return _sqlExpressionFactory.Not(
+ _sqlExpressionFactory.Function(
+ "lower_inf",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(bool)));
+ }
+
+ if (member == Interval_HasEnd)
+ {
+ return _sqlExpressionFactory.Not(
+ _sqlExpressionFactory.Function(
+ "upper_inf",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(bool)));
+ }
+
+ if (member == Interval_Duration)
+ {
+ return _sqlExpressionFactory.Subtract(Upper(), Lower(), _typeMappingSource.FindMapping(typeof(Duration)));
+ }
+
+ return null;
+
+ SqlExpression Lower()
+ => _sqlExpressionFactory.Function(
+ "lower",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(Interval),
+ _typeMappingSource.FindMapping(typeof(Instant)));
+
+ SqlExpression Upper()
+ => _sqlExpressionFactory.Function(
+ "upper",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(Interval),
+ _typeMappingSource.FindMapping(typeof(Instant)));
+ }
+
+ private SqlExpression? TranslateDateInterval(SqlExpression instance, MemberInfo member)
+ {
+ // NodaTime DateInterval is inclusive on both ends.
+ // GaussDB daterange is a discrete range type; this means it gets normalized to inclusive lower bound, exclusive upper bound.
+ // So we can translate Start as-is, but need to subtract a day for End.
+ if (member == DateInterval_Start)
+ {
+ return Lower();
+ }
+
+ if (member == DateInterval_End)
+ {
+ // GaussDB creates a result of type 'timestamp without time zone' when subtracting intervals from dates, so add a cast back
+ // to date.
+ return _sqlExpressionFactory.Convert(
+ _sqlExpressionFactory.Subtract(
+ Upper(),
+ _sqlExpressionFactory.Constant(Period.FromDays(1), _periodTypeMapping)), typeof(LocalDate),
+ _typeMappingSource.FindMapping(typeof(LocalDate)));
+ }
+
+ if (member == DateInterval_Length)
+ {
+ return _sqlExpressionFactory.Subtract(Upper(), Lower());
+ }
+
+ return null;
+
+ SqlExpression Lower()
+ => _sqlExpressionFactory.Function(
+ "lower",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(LocalDate),
+ _dateTypeMapping);
+
+ SqlExpression Upper()
+ => _sqlExpressionFactory.Function(
+ "upper",
+ [instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(LocalDate),
+ _dateTypeMapping);
+ }
+
+ private SqlExpression? TranslateDateTime(SqlExpression instance, MemberInfo member)
+ => member.Name switch
+ {
+ "Year" or "Years" => GetDatePartExpression(instance, "year"),
+ "Month" or "Months" => GetDatePartExpression(instance, "month"),
+ "DayOfYear" => GetDatePartExpression(instance, "doy"),
+ "Day" or "Days" => GetDatePartExpression(instance, "day"),
+ "Hour" or "Hours" => GetDatePartExpression(instance, "hour"),
+ "Minute" or "Minutes" => GetDatePartExpression(instance, "minute"),
+ "Second" or "Seconds" => GetDatePartExpression(instance, "second", true),
+ "Millisecond" or "Milliseconds" => null, // Too annoying
+
+ // Unlike DateTime.DayOfWeek, NodaTime's IsoDayOfWeek enum doesn't exactly correspond to GaussDB's
+ // values returned by date_part('dow', ...): in NodaTime Sunday is 7 and not 0, which is None.
+ // So we generate a CASE WHEN expression to translate GaussDB's 0 to 7.
+ "DayOfWeek" when GetDatePartExpression(instance, "dow", true) is var getValueExpression
+ => _sqlExpressionFactory.Case(
+ getValueExpression,
+ [new CaseWhenClause(_sqlExpressionFactory.Constant(0), _sqlExpressionFactory.Constant(7))],
+ getValueExpression),
+
+ // PG allows converting a timestamp directly to date, truncating the time; but given a timestamptz, it performs a time zone
+ // conversion (based on TimeZone), which we don't want (so avoid translating except on timestamp).
+ // The translation for ZonedDateTime.Date converts to timestamp before ending up here.
+ "Date" when instance.TypeMapping is TimestampLocalDateTimeMapping or LegacyTimestampInstantMapping
+ => _sqlExpressionFactory.Convert(instance, typeof(LocalDate), _typeMappingSource.FindMapping(typeof(LocalDate))!),
+
+ "TimeOfDay" => _sqlExpressionFactory.Convert(
+ instance,
+ typeof(LocalTime),
+ _typeMappingSource.FindMapping(typeof(LocalTime), storeTypeName: "time")),
+
+ _ => null
+ };
+
+ ///
+ /// Constructs the date_part expression.
+ ///
+ /// The expression.
+ /// The name of the date_part to construct.
+ /// True if the result should be wrapped with floor(...); otherwise, false.
+ ///
+ /// The date_part expression.
+ ///
+ ///
+ /// date_part returns doubles, which we floor and cast into ints
+ /// This also gets rid of sub-second components when retrieving seconds.
+ ///
+ private SqlExpression GetDatePartExpression(
+ SqlExpression instance,
+ string partName,
+ bool floor = false)
+ {
+ var result = GetDatePartExpressionDouble(instance, partName, floor);
+ return _sqlExpressionFactory.Convert(result, typeof(int));
+ }
+
+ private SqlExpression GetDatePartExpressionDouble(
+ SqlExpression instance,
+ string partName,
+ bool floor = false)
+ {
+ var result = _sqlExpressionFactory.Function(
+ "date_part",
+ [_sqlExpressionFactory.Constant(partName), instance],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[2],
+ typeof(double));
+
+ if (floor)
+ {
+ result = _sqlExpressionFactory.Function(
+ "floor",
+ [result],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ typeof(double));
+ }
+
+ return result;
+ }
+
+ private SqlExpression? TranslateZonedDateTime(SqlExpression instance, MemberInfo member, Type returnType)
+ {
+ if (instance is PendingZonedDateTimeExpression pendingZonedDateTime)
+ {
+ instance = _sqlExpressionFactory.AtTimeZone(
+ pendingZonedDateTime.Operand,
+ pendingZonedDateTime.TimeZoneId,
+ typeof(LocalDateTime),
+ _localDateTimeTypeMapping);
+
+ return member == ZonedDateTime_LocalDateTime
+ ? instance
+ : TranslateDateTime(instance, member);
+ }
+
+ // date_part, which is used to extract most components, doesn't have an overload for timestamptz, so passing one directly
+ // converts it to the local timezone as per TimeZone. Explicitly convert it to a 'timestamp without time zone' in UTC.
+ // The same works also for the LocalDateTime member.
+ instance = _sqlExpressionFactory.AtUtc(instance);
+
+ return member == ZonedDateTime_LocalDateTime
+ ? instance
+ : TranslateDateTime(instance, member);
+ }
+}
diff --git a/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMethodCallTranslatorPlugin.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMethodCallTranslatorPlugin.cs
new file mode 100644
index 0000000000..72ec2613ea
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/GaussDBNodaTimeMethodCallTranslatorPlugin.cs
@@ -0,0 +1,426 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query.Expressions;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Query.Expressions.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
+
+///
+/// Provides translation services for members.
+///
+///
+/// See: https://www.postgresql.org/docs/current/static/functions-datetime.html
+///
+public class GaussDBNodaTimeMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeMethodCallTranslatorPlugin(
+ IRelationalTypeMappingSource typeMappingSource,
+ ISqlExpressionFactory sqlExpressionFactory)
+ {
+ Translators =
+ [
+ new GaussDBNodaTimeMethodCallTranslator(typeMappingSource, (GaussDBExpressionFactory)sqlExpressionFactory)
+ ];
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual IEnumerable Translators { get; }
+}
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeMethodCallTranslator : IMethodCallTranslator
+{
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+ private readonly GaussDBExpressionFactory _sqlExpressionFactory;
+
+ private static readonly MethodInfo SystemClock_GetCurrentInstant =
+ typeof(SystemClock).GetRuntimeMethod(nameof(SystemClock.GetCurrentInstant), Type.EmptyTypes)!;
+
+ private static readonly MethodInfo Instant_InUtc =
+ typeof(Instant).GetRuntimeMethod(nameof(Instant.InUtc), Type.EmptyTypes)!;
+
+ private static readonly MethodInfo Instant_InZone =
+ typeof(Instant).GetRuntimeMethod(nameof(Instant.InZone), [typeof(DateTimeZone)])!;
+
+ private static readonly MethodInfo Instant_ToDateTimeUtc =
+ typeof(Instant).GetRuntimeMethod(nameof(Instant.ToDateTimeUtc), Type.EmptyTypes)!;
+
+ private static readonly MethodInfo Instant_Distance =
+ typeof(GaussDBNodaTimeDbFunctionsExtensions).GetRuntimeMethod(
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Distance), [typeof(DbFunctions), typeof(Instant), typeof(Instant)])!;
+
+ private static readonly MethodInfo ZonedDateTime_ToInstant =
+ typeof(ZonedDateTime).GetRuntimeMethod(nameof(ZonedDateTime.ToInstant), Type.EmptyTypes)!;
+
+ private static readonly MethodInfo ZonedDateTime_Distance =
+ typeof(GaussDBNodaTimeDbFunctionsExtensions).GetRuntimeMethod(
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Distance),
+ [typeof(DbFunctions), typeof(ZonedDateTime), typeof(ZonedDateTime)])!;
+
+ private static readonly MethodInfo LocalDateTime_InZoneLeniently =
+ typeof(LocalDateTime).GetRuntimeMethod(nameof(LocalDateTime.InZoneLeniently), [typeof(DateTimeZone)])!;
+
+ private static readonly MethodInfo LocalDateTime_Distance =
+ typeof(GaussDBNodaTimeDbFunctionsExtensions).GetRuntimeMethod(
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Distance),
+ [typeof(DbFunctions), typeof(LocalDateTime), typeof(LocalDateTime)])!;
+
+ private static readonly MethodInfo LocalDate_Distance =
+ typeof(GaussDBNodaTimeDbFunctionsExtensions).GetRuntimeMethod(
+ nameof(GaussDBNodaTimeDbFunctionsExtensions.Distance), [typeof(DbFunctions), typeof(LocalDate), typeof(LocalDate)])!;
+
+ private static readonly MethodInfo Period_FromYears = typeof(Period).GetRuntimeMethod(nameof(Period.FromYears), [typeof(int)])!;
+
+ private static readonly MethodInfo Period_FromMonths =
+ typeof(Period).GetRuntimeMethod(nameof(Period.FromMonths), [typeof(int)])!;
+
+ private static readonly MethodInfo Period_FromWeeks = typeof(Period).GetRuntimeMethod(nameof(Period.FromWeeks), [typeof(int)])!;
+ private static readonly MethodInfo Period_FromDays = typeof(Period).GetRuntimeMethod(nameof(Period.FromDays), [typeof(int)])!;
+
+ private static readonly MethodInfo Period_FromHours = typeof(Period).GetRuntimeMethod(
+ nameof(Period.FromHours), [typeof(long)])!;
+
+ private static readonly MethodInfo Period_FromMinutes =
+ typeof(Period).GetRuntimeMethod(nameof(Period.FromMinutes), [typeof(long)])!;
+
+ private static readonly MethodInfo Period_FromSeconds =
+ typeof(Period).GetRuntimeMethod(nameof(Period.FromSeconds), [typeof(long)])!;
+
+ private static readonly MethodInfo Interval_Contains
+ = typeof(Interval).GetRuntimeMethod(nameof(Interval.Contains), [typeof(Instant)])!;
+
+ private static readonly MethodInfo DateInterval_Contains_LocalDate
+ = typeof(DateInterval).GetRuntimeMethod(nameof(DateInterval.Contains), [typeof(LocalDate)])!;
+
+ private static readonly MethodInfo DateInterval_Contains_DateInterval
+ = typeof(DateInterval).GetRuntimeMethod(nameof(DateInterval.Contains), [typeof(DateInterval)])!;
+
+ private static readonly MethodInfo DateInterval_Intersection
+ = typeof(DateInterval).GetRuntimeMethod(nameof(DateInterval.Intersection), [typeof(DateInterval)])!;
+
+ private static readonly MethodInfo DateInterval_Union
+ = typeof(DateInterval).GetRuntimeMethod(nameof(DateInterval.Union), [typeof(DateInterval)])!;
+
+ private static readonly MethodInfo IDateTimeZoneProvider_get_Item
+ = typeof(IDateTimeZoneProvider).GetRuntimeMethod("get_Item", [typeof(string)])!;
+
+ private static readonly bool[][] TrueArrays = [[], [true], [true, true]];
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBNodaTimeMethodCallTranslator(
+ IRelationalTypeMappingSource typeMappingSource,
+ GaussDBExpressionFactory sqlExpressionFactory)
+ {
+ _typeMappingSource = typeMappingSource;
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+#pragma warning disable EF1001
+ ///
+ public virtual SqlExpression? Translate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ var translated =
+ TranslateInstant(instance, method, arguments)
+ ?? TranslateZonedDateTime(instance, method, arguments)
+ ?? TranslateLocalDateTime(instance, method, arguments)
+ ?? TranslateLocalDate(instance, method, arguments)
+ ?? TranslatePeriod(instance, method, arguments, logger)
+ ?? TranslateInterval(instance, method, arguments, logger)
+ ?? TranslateDateInterval(instance, method, arguments, logger);
+
+ if (translated is not null)
+ {
+ return translated;
+ }
+
+ if (method == IDateTimeZoneProvider_get_Item && instance is PendingDateTimeZoneProviderExpression)
+ {
+ // We're translating an expression such as 'DateTimeZoneProviders.Tzdb["Europe/Berlin"]'.
+ // Note that the .NET type of that expression is DateTimeZone, but we just return the string ID for the time zone.
+ return arguments[0];
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateInstant(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments)
+ {
+ if (method == SystemClock_GetCurrentInstant)
+ {
+ return GaussDBNodaTimeTypeMappingSourcePlugin.LegacyTimestampBehavior
+ ? _sqlExpressionFactory.AtTimeZone(
+ _sqlExpressionFactory.Function(
+ "NOW",
+ [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ method.ReturnType),
+ _sqlExpressionFactory.Constant("UTC"),
+ method.ReturnType)
+ : _sqlExpressionFactory.Function(
+ "NOW",
+ [],
+ nullable: false,
+ argumentsPropagateNullability: [],
+ method.ReturnType,
+ _typeMappingSource.FindMapping(typeof(Instant), "timestamp with time zone"));
+ }
+
+ if (method == Instant_InUtc)
+ {
+ // Instant -> ZonedDateTime is a no-op (different types in .NET but both mapped to timestamptz in PG)
+ return instance;
+ }
+
+ if (method == Instant_InZone)
+ {
+ // When InZone is called, we have a mismatch: on the .NET NodaTime side, we have a ZonedDateTime; but on the GaussDB side,
+ // the AT TIME ZONE expression returns a 'timestamp without time zone' (when applied to a 'timestamp with time zone', which is
+ // what ZonedDateTime is mapped to).
+ return new PendingZonedDateTimeExpression(instance!, arguments[0]);
+ }
+
+ if (method == Instant_ToDateTimeUtc)
+ {
+ return _sqlExpressionFactory.Convert(
+ instance!,
+ typeof(DateTime),
+ _typeMappingSource.FindMapping(typeof(DateTime), "timestamp with time zone"));
+ }
+
+ if (method == Instant_Distance)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.Distance, arguments[1], arguments[2]);
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateZonedDateTime(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments)
+ {
+ if (method == ZonedDateTime_ToInstant)
+ {
+ // We get here with the expression localDateTime.InZoneLeniently(DateTimeZoneProviders.Tzdb["Europe/Berlin"]).ToInstant()
+ if (instance is PendingZonedDateTimeExpression pendingZonedDateTime)
+ {
+ return _sqlExpressionFactory.AtTimeZone(
+ pendingZonedDateTime.Operand,
+ pendingZonedDateTime.TimeZoneId,
+ typeof(Instant),
+ _typeMappingSource.FindMapping(typeof(Instant)));
+ }
+
+ // Otherwise, ZonedDateTime -> ToInstant is a no-op (different types in .NET but both mapped to timestamptz in PG)
+ return instance;
+ }
+
+ if (method == ZonedDateTime_Distance)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.Distance, arguments[1], arguments[2]);
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateLocalDateTime(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments)
+ {
+ if (method == LocalDateTime_InZoneLeniently)
+ {
+ return new PendingZonedDateTimeExpression(instance!, arguments[0]);
+ }
+
+ if (method == LocalDateTime_Distance)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.Distance, arguments[1], arguments[2]);
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateLocalDate(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments)
+ {
+ if (method == LocalDate_Distance)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.Distance, arguments[1], arguments[2]);
+ }
+
+ if (method.DeclaringType == typeof(LocalDate))
+ {
+ return method.Name switch
+ {
+ nameof(LocalDate.At) => new SqlBinaryExpression(
+ ExpressionType.Add,
+ _sqlExpressionFactory.ApplyDefaultTypeMapping(instance!),
+ _sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0]),
+ typeof(LocalDateTime),
+ _typeMappingSource.FindMapping(typeof(LocalDateTime))),
+
+ nameof(LocalDate.AtMidnight) => new SqlBinaryExpression(
+ ExpressionType.Add,
+ _sqlExpressionFactory.ApplyDefaultTypeMapping(instance!),
+ new SqlConstantExpression(new LocalTime(0, 0, 0), _typeMappingSource.FindMapping(typeof(LocalTime))),
+ typeof(LocalDateTime),
+ _typeMappingSource.FindMapping(typeof(LocalDateTime))),
+
+ _ => null
+ };
+ }
+ return null;
+ }
+
+ private SqlExpression? TranslatePeriod(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (method.DeclaringType != typeof(Period))
+ {
+ return null;
+ }
+
+ if (method == Period_FromYears)
+ {
+ return IntervalPart("years", arguments[0]);
+ }
+
+ if (method == Period_FromMonths)
+ {
+ return IntervalPart("months", arguments[0]);
+ }
+
+ if (method == Period_FromWeeks)
+ {
+ return IntervalPart("weeks", arguments[0]);
+ }
+
+ if (method == Period_FromDays)
+ {
+ return IntervalPart("days", arguments[0]);
+ }
+
+ if (method == Period_FromHours)
+ {
+ return IntervalPartOverBigInt("hours", arguments[0]);
+ }
+
+ if (method == Period_FromMinutes)
+ {
+ return IntervalPartOverBigInt("mins", arguments[0]);
+ }
+
+ if (method == Period_FromSeconds)
+ {
+ return IntervalPart(
+ "secs", _sqlExpressionFactory.Convert(arguments[0], typeof(double), _typeMappingSource.FindMapping(typeof(double))));
+ }
+
+ return null;
+
+ static GaussDBFunctionExpression IntervalPart(string datePart, SqlExpression parameter)
+ => GaussDBFunctionExpression.CreateWithNamedArguments(
+ "make_interval",
+ [parameter],
+ [datePart],
+ nullable: true,
+ argumentsPropagateNullability: TrueArrays[1],
+ builtIn: true,
+ typeof(Period),
+ typeMapping: null);
+
+ GaussDBFunctionExpression IntervalPartOverBigInt(string datePart, SqlExpression parameter)
+ {
+ parameter = _sqlExpressionFactory.ApplyDefaultTypeMapping(parameter);
+
+ // NodaTime Period.FromHours/Minutes/Seconds accept a long parameter, but PG interval_part accepts an int.
+ // If the parameter happens to be an int cast up to a long, just unwrap it, otherwise downcast from bigint to int
+ // (this will throw on the PG side if the bigint is out of int range)
+ if (parameter is SqlUnaryExpression { OperatorType: ExpressionType.Convert } convertExpression
+ && convertExpression.TypeMapping!.StoreType == "bigint"
+ && convertExpression.Operand.TypeMapping!.StoreType == "integer")
+ {
+ return IntervalPart(datePart, convertExpression.Operand);
+ }
+
+ return IntervalPart(
+ datePart, _sqlExpressionFactory.Convert(parameter, typeof(int), _typeMappingSource.FindMapping(typeof(int))));
+ }
+ }
+
+ private SqlExpression? TranslateInterval(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (method == Interval_Contains)
+ {
+ return _sqlExpressionFactory.Contains(instance!, arguments[0]);
+ }
+
+ return null;
+ }
+
+ private SqlExpression? TranslateDateInterval(
+ SqlExpression? instance,
+ MethodInfo method,
+ IReadOnlyList arguments,
+ IDiagnosticsLogger logger)
+ {
+ if (method == DateInterval_Contains_LocalDate
+ || method == DateInterval_Contains_DateInterval)
+ {
+ return _sqlExpressionFactory.Contains(instance!, arguments[0]);
+ }
+
+ if (method == DateInterval_Intersection)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.RangeIntersect, instance!, arguments[0]);
+ }
+
+ if (method == DateInterval_Union)
+ {
+ return _sqlExpressionFactory.MakePostgresBinary(GaussDBExpressionType.RangeUnion, instance!, arguments[0]);
+ }
+
+ return null;
+ }
+#pragma warning restore EF1001
+}
diff --git a/src/EFCore.PG.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs
similarity index 88%
rename from src/EFCore.PG.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs
rename to src/EFCore.GaussDB.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs
index 7c2ee5ef94..8d8e37d71a 100644
--- a/src/EFCore.PG.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/PendingDateTimeZoneProviderExpression.cs
@@ -1,4 +1,4 @@
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
internal class PendingDateTimeZoneProviderExpression : SqlExpression
{
diff --git a/src/EFCore.PG.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs b/src/EFCore.GaussDB.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs
similarity index 91%
rename from src/EFCore.PG.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs
rename to src/EFCore.GaussDB.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs
index 41739d7137..ede7a95a27 100644
--- a/src/EFCore.PG.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs
+++ b/src/EFCore.GaussDB.NodaTime/Query/Internal/PendingZonedDateTimeExpression.cs
@@ -1,4 +1,4 @@
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Query.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Query.Internal;
internal class PendingZonedDateTimeExpression : SqlExpression
{
diff --git a/src/EFCore.GaussDB.NodaTime/README.md b/src/EFCore.GaussDB.NodaTime/README.md
new file mode 100644
index 0000000000..7ab8c52384
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/README.md
@@ -0,0 +1,43 @@
+# GaussDB Entity Framework Core provider for GaussDB
+
+HuaweiCloud.EntityFrameworkCore.GaussDB is the open source EF Core provider for GaussDB. It allows you to interact with GaussDB via the most widely-used .NET O/RM from Microsoft, and use familiar LINQ syntax to express queries.
+
+This package is a plugin which allows you to use the [NodaTime](https://nodatime.org) date/time library when interacting with GaussDB; this provides a better and safer API for dealing with date and time data.
+
+To use the plugin, simply add `UseNodaTime` as below and use NodaTime types in your entity properties:
+
+```csharp
+await using var ctx = new BlogContext();
+await ctx.Database.EnsureDeletedAsync();
+await ctx.Database.EnsureCreatedAsync();
+
+// Insert a Blog
+ctx.Blogs.Add(new()
+{
+ Name = "FooBlog",
+ CreationTime = SystemClock.Instance.GetCurrentInstant()
+});
+await ctx.SaveChangesAsync();
+
+// Query all blogs created in 2020 or after
+var newBlogs = await ctx.Blogs.Where(b => b.CreationTime >= Instant.FromUtc(2020, 1, 1, 0, 0, 0)).ToListAsync();
+
+public class BlogContext : DbContext
+{
+ public DbSet Blogs { get; set; }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseGaussDB(
+ @"Host=myserver;Username=mylogin;Password=mypass;Database=mydatabase",
+ o => o.UseNodaTime());
+}
+
+public class Blog
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public Instant CreationTime { get; set; }
+}
+```
+
+The plugin also supports translating most NodaTime methods and properties into corresponding GaussDB date/time operations. For more information, see the [NodaTime plugin documentation page](https://www.npgsql.org/efcore/mapping/nodatime.html).
diff --git a/src/EFCore.GaussDB.NodaTime/Scaffolding/Internal/GaussDBNodaTimeCodeGeneratorPlugin.cs b/src/EFCore.GaussDB.NodaTime/Scaffolding/Internal/GaussDBNodaTimeCodeGeneratorPlugin.cs
new file mode 100644
index 0000000000..d98fec52ee
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Scaffolding/Internal/GaussDBNodaTimeCodeGeneratorPlugin.cs
@@ -0,0 +1,26 @@
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Infrastructure;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Scaffolding.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeCodeGeneratorPlugin : ProviderCodeGeneratorPlugin
+{
+ private static readonly MethodInfo _useNodaTimeMethodInfo
+ = typeof(GaussDBNodaTimeDbContextOptionsBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBNodaTimeDbContextOptionsBuilderExtensions.UseNodaTime),
+ typeof(GaussDBDbContextOptionsBuilder));
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override MethodCallCodeFragment GenerateProviderOptions()
+ => new(_useNodaTimeMethodInfo);
+}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs
similarity index 89%
rename from src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs
index a8e68760bc..e1dd31d472 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalMultirangeMapping.cs
@@ -1,7 +1,7 @@
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -9,7 +9,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class DateIntervalMultirangeMapping : NpgsqlTypeMapping
+public class DateIntervalMultirangeMapping : GaussDBTypeMapping
{
private readonly DateIntervalRangeMapping _dateIntervalRangeMapping;
@@ -20,7 +20,7 @@ public class DateIntervalMultirangeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public DateIntervalMultirangeMapping(Type clrType, DateIntervalRangeMapping dateIntervalRangeMapping)
- : base("datemultirange", clrType, NpgsqlDbType.DateMultirange)
+ : base("datemultirange", clrType, GaussDBDbType.DateMultirange)
{
_dateIntervalRangeMapping = dateIntervalRangeMapping;
}
@@ -32,7 +32,7 @@ public DateIntervalMultirangeMapping(Type clrType, DateIntervalRangeMapping date
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected DateIntervalMultirangeMapping(RelationalTypeMappingParameters parameters, DateIntervalRangeMapping dateIntervalRangeMapping)
- : base(parameters, NpgsqlDbType.DateMultirange)
+ : base(parameters, GaussDBDbType.DateMultirange)
{
_dateIntervalRangeMapping = dateIntervalRangeMapping;
}
@@ -62,5 +62,5 @@ public override RelationalTypeMapping WithStoreTypeAndSize(string storeType, int
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override string GenerateNonNullSqlLiteral(object value)
- => NpgsqlMultirangeTypeMapping.GenerateNonNullSqlLiteral(value, _dateIntervalRangeMapping, "datemultirange");
+ => GaussDBMultirangeTypeMapping.GenerateNonNullSqlLiteral(value, _dateIntervalRangeMapping, "datemultirange");
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs
similarity index 94%
rename from src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs
index 3a072e8925..92cbe43f30 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateIntervalRangeMapping.cs
@@ -1,8 +1,8 @@
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -10,7 +10,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class DateIntervalRangeMapping : NpgsqlTypeMapping
+public class DateIntervalRangeMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo _constructorWithDates =
typeof(DateInterval).GetConstructor([typeof(LocalDate), typeof(LocalDate)])!;
@@ -33,7 +33,7 @@ public class DateIntervalRangeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public DateIntervalRangeMapping()
- : base("daterange", typeof(DateInterval), NpgsqlDbType.DateRange)
+ : base("daterange", typeof(DateInterval), GaussDBDbType.DateRange)
{
}
@@ -44,7 +44,7 @@ public DateIntervalRangeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected DateIntervalRangeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.DateRange)
+ : base(parameters, GaussDBDbType.DateRange)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/DateMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateMapping.cs
similarity index 92%
rename from src/EFCore.PG.NodaTime/Storage/Internal/DateMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/DateMapping.cs
index eea1ef0a5d..ce32b6caea 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/DateMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateMapping.cs
@@ -1,11 +1,11 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+using static HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties.Util;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
-using static Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Utilties.Util;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class DateMapping : NpgsqlTypeMapping
+public class DateMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo Constructor =
typeof(LocalDate).GetConstructor([typeof(int), typeof(int), typeof(int)])!;
@@ -33,7 +33,7 @@ public class DateMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public DateMapping()
- : base("date", typeof(LocalDate), NpgsqlDbType.Date, JsonLocalDateReaderWriter.Instance)
+ : base("date", typeof(LocalDate), GaussDBDbType.Date, JsonLocalDateReaderWriter.Instance)
{
}
@@ -44,7 +44,7 @@ public DateMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected DateMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Date)
+ : base(parameters, GaussDBDbType.Date)
{
}
@@ -86,7 +86,7 @@ protected override string GenerateEmbeddedNonNullSqlLiteral(object value)
private static string FormatLocalDate(LocalDate date)
{
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
if (date == LocalDate.MinIsoValue)
{
@@ -124,7 +124,7 @@ public override LocalDate FromJsonTyped(ref Utf8JsonReaderManager manager, objec
{
var s = manager.CurrentReader.GetString()!;
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
switch (s)
{
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/DateTimeZoneMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateTimeZoneMapping.cs
similarity index 98%
rename from src/EFCore.PG.NodaTime/Storage/Internal/DateTimeZoneMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/DateTimeZoneMapping.cs
index fea8f021b2..4127182232 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/DateTimeZoneMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DateTimeZoneMapping.cs
@@ -1,6 +1,6 @@
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/DurationIntervalMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DurationIntervalMapping.cs
similarity index 91%
rename from src/EFCore.PG.NodaTime/Storage/Internal/DurationIntervalMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/DurationIntervalMapping.cs
index bb44e93bbc..355ed307e9 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/DurationIntervalMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/DurationIntervalMapping.cs
@@ -1,9 +1,9 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -11,7 +11,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class DurationIntervalMapping : NpgsqlTypeMapping
+public class DurationIntervalMapping : GaussDBTypeMapping
{
private static readonly MethodInfo FromDays = typeof(Duration).GetRuntimeMethod(nameof(Duration.FromDays), [typeof(int)])!;
private static readonly MethodInfo FromHours = typeof(Duration).GetRuntimeMethod(nameof(Duration.FromHours), [typeof(int)])!;
@@ -42,7 +42,7 @@ public class DurationIntervalMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public DurationIntervalMapping()
- : base("interval", typeof(Duration), NpgsqlDbType.Interval, JsonDurationReaderWriter.Instance)
+ : base("interval", typeof(Duration), GaussDBDbType.Interval, JsonDurationReaderWriter.Instance)
{
}
@@ -53,7 +53,7 @@ public DurationIntervalMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected DurationIntervalMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Interval)
+ : base(parameters, GaussDBDbType.Interval)
{
}
@@ -91,7 +91,7 @@ protected override string GenerateNonNullSqlLiteral(object value)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override string GenerateEmbeddedNonNullSqlLiteral(object value)
- => NpgsqlIntervalTypeMapping.FormatTimeSpanAsInterval(((Duration)value).ToTimeSpan());
+ => GaussDBIntervalTypeMapping.FormatTimeSpanAsInterval(((Duration)value).ToTimeSpan());
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -142,10 +142,10 @@ private sealed class JsonDurationReaderWriter : JsonValueReaderWriter
public static JsonDurationReaderWriter Instance { get; } = new();
public override Duration FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
- => Duration.FromTimeSpan(NpgsqlIntervalTypeMapping.ParseIntervalAsTimeSpan(manager.CurrentReader.GetString()!));
+ => Duration.FromTimeSpan(GaussDBIntervalTypeMapping.ParseIntervalAsTimeSpan(manager.CurrentReader.GetString()!));
public override void ToJsonTyped(Utf8JsonWriter writer, Duration value)
- => writer.WriteStringValue(NpgsqlIntervalTypeMapping.FormatTimeSpanAsInterval(value.ToTimeSpan()));
+ => writer.WriteStringValue(GaussDBIntervalTypeMapping.FormatTimeSpanAsInterval(value.ToTimeSpan()));
///
public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
diff --git a/src/EFCore.GaussDB.NodaTime/Storage/Internal/GaussDBNodaTimeTypeMappingSourcePlugin.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/GaussDBNodaTimeTypeMappingSourcePlugin.cs
new file mode 100644
index 0000000000..38aa3e5c24
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/GaussDBNodaTimeTypeMappingSourcePlugin.cs
@@ -0,0 +1,230 @@
+using System.Collections.Concurrent;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+
+// ReSharper disable once CheckNamespace
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBNodaTimeTypeMappingSourcePlugin : IRelationalTypeMappingSourcePlugin
+{
+#if DEBUG
+ internal static bool LegacyTimestampBehavior;
+ internal static bool DisableDateTimeInfinityConversions;
+#else
+ internal static readonly bool LegacyTimestampBehavior;
+ internal static readonly bool DisableDateTimeInfinityConversions;
+#endif
+
+ static GaussDBNodaTimeTypeMappingSourcePlugin()
+ {
+ LegacyTimestampBehavior = AppContext.TryGetSwitch("GaussDB.EnableLegacyTimestampBehavior", out var enabled) && enabled;
+ DisableDateTimeInfinityConversions = AppContext.TryGetSwitch("GaussDB.DisableDateTimeInfinityConversions", out enabled) && enabled;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ConcurrentDictionary StoreTypeMappings { get; }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ConcurrentDictionary ClrTypeMappings { get; }
+
+ #region TypeMapping
+
+ private readonly TimestampLocalDateTimeMapping _timestampLocalDateTime = TimestampLocalDateTimeMapping.Default;
+ private readonly LegacyTimestampInstantMapping _legacyTimestampInstant = LegacyTimestampInstantMapping.Default;
+
+ private readonly TimestampTzInstantMapping _timestamptzInstant = TimestampTzInstantMapping.Default;
+ private readonly TimestampTzZonedDateTimeMapping _timestamptzZonedDateTime = TimestampTzZonedDateTimeMapping.Default;
+ private readonly TimestampTzOffsetDateTimeMapping _timestamptzOffsetDateTime = TimestampTzOffsetDateTimeMapping.Default;
+
+ private readonly DateMapping _date = DateMapping.Default;
+ private readonly TimeMapping _time = TimeMapping.Default;
+ private readonly TimeTzMapping _timetz = TimeTzMapping.Default;
+ private readonly PeriodIntervalMapping _periodInterval = PeriodIntervalMapping.Default;
+ private readonly DurationIntervalMapping _durationInterval = DurationIntervalMapping.Default;
+
+ // GaussDB has no native type for representing time zones - it just uses the IANA ID as text.
+ private readonly DateTimeZoneMapping _timeZone = new("text");
+
+ // Built-in ranges
+ private readonly GaussDBRangeTypeMapping _timestampLocalDateTimeRange;
+ private readonly GaussDBRangeTypeMapping _legacyTimestampInstantRange;
+ private readonly GaussDBRangeTypeMapping _timestamptzInstantRange;
+ private readonly GaussDBRangeTypeMapping _timestamptzZonedDateTimeRange;
+ private readonly GaussDBRangeTypeMapping _timestamptzOffsetDateTimeRange;
+ private readonly GaussDBRangeTypeMapping _dateRange;
+ private readonly DateIntervalRangeMapping _dateIntervalRange = new();
+ private readonly IntervalRangeMapping _intervalRange = new();
+
+ #endregion
+
+ ///
+ /// Constructs an instance of the class.
+ ///
+ public GaussDBNodaTimeTypeMappingSourcePlugin(ISqlGenerationHelper sqlGenerationHelper)
+ {
+ _timestampLocalDateTimeRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "tsrange", typeof(GaussDBRange), GaussDBDbType.TimestampRange, _timestampLocalDateTime);
+ _legacyTimestampInstantRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "tsrange", typeof(GaussDBRange), GaussDBDbType.TimestampRange, _legacyTimestampInstant);
+ _timestamptzInstantRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "tstzrange", typeof(GaussDBRange), GaussDBDbType.TimestampTzRange, _timestamptzInstant);
+ _timestamptzZonedDateTimeRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "tstzrange", typeof(GaussDBRange), GaussDBDbType.TimestampTzRange, _timestamptzZonedDateTime);
+ _timestamptzOffsetDateTimeRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "tstzrange", typeof(GaussDBRange), GaussDBDbType.TimestampTzRange, _timestamptzOffsetDateTime);
+ _dateRange = GaussDBRangeTypeMapping.CreatBuiltInRangeMapping(
+ "daterange", typeof(GaussDBRange), GaussDBDbType.DateRange, _date);
+
+ var storeTypeMappings = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ {
+ // We currently allow _legacyTimestampInstant even in non-legacy mode, since when upgrading to 6.0 with existing
+ // migrations, model snapshots still contain old mappings (Instant mapped to timestamp), and EF Core's model differ
+ // expects type mappings to be found for these. See https://github.com/dotnet/efcore/issues/26168.
+ "timestamp without time zone", LegacyTimestampBehavior
+ ? [_legacyTimestampInstant, _timestampLocalDateTime]
+ : [_timestampLocalDateTime, _legacyTimestampInstant]
+ },
+ {
+ "timestamp with time zone", [_timestamptzInstant, _timestamptzZonedDateTime, _timestamptzOffsetDateTime]
+ },
+ { "date", [_date] },
+ { "time without time zone", [_time] },
+ { "time with time zone", [_timetz] },
+ { "interval", [_periodInterval, _durationInterval] },
+ {
+ "tsrange", LegacyTimestampBehavior
+ ? [_legacyTimestampInstantRange, _timestampLocalDateTimeRange]
+ : [_timestampLocalDateTimeRange, _legacyTimestampInstantRange]
+ },
+ {
+ "tstzrange", [
+ _intervalRange, _timestamptzInstantRange, _timestamptzZonedDateTimeRange, _timestamptzOffsetDateTimeRange
+ ]
+ },
+ { "daterange", [_dateIntervalRange, _dateRange] }
+ };
+
+ // Set up aliases
+ storeTypeMappings["timestamp"] = storeTypeMappings["timestamp without time zone"];
+ storeTypeMappings["timestamptz"] = storeTypeMappings["timestamp with time zone"];
+ storeTypeMappings["time"] = storeTypeMappings["time without time zone"];
+ storeTypeMappings["timetz"] = storeTypeMappings["time with time zone"];
+
+ var clrTypeMappings = new Dictionary
+ {
+ { typeof(Instant), LegacyTimestampBehavior ? _legacyTimestampInstant : _timestamptzInstant },
+ { typeof(LocalDateTime), _timestampLocalDateTime },
+ { typeof(ZonedDateTime), _timestamptzZonedDateTime },
+ { typeof(OffsetDateTime), _timestamptzOffsetDateTime },
+ { typeof(LocalDate), _date },
+ { typeof(LocalTime), _time },
+ { typeof(OffsetTime), _timetz },
+ { typeof(Period), _periodInterval },
+ { typeof(Duration), _durationInterval },
+ // See DateTimeZone below
+
+ { typeof(GaussDBRange), LegacyTimestampBehavior ? _legacyTimestampInstantRange : _timestamptzInstantRange },
+ { typeof(GaussDBRange), _timestampLocalDateTimeRange },
+ { typeof(GaussDBRange), _timestamptzZonedDateTimeRange },
+ { typeof(GaussDBRange), _timestamptzOffsetDateTimeRange },
+ { typeof(GaussDBRange), _dateRange },
+ { typeof(DateInterval), _dateIntervalRange },
+ { typeof(Interval), _intervalRange },
+ };
+
+ StoreTypeMappings = new ConcurrentDictionary(storeTypeMappings, StringComparer.OrdinalIgnoreCase);
+ ClrTypeMappings = new ConcurrentDictionary(clrTypeMappings);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual RelationalTypeMapping? FindMapping(in RelationalTypeMappingInfo mappingInfo)
+ => FindBaseMapping(mappingInfo)?.Clone(mappingInfo);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual RelationalTypeMapping? FindBaseMapping(in RelationalTypeMappingInfo mappingInfo)
+ {
+ var clrType = mappingInfo.ClrType;
+ var storeTypeName = mappingInfo.StoreTypeName;
+ var storeTypeNameBase = mappingInfo.StoreTypeNameBase;
+
+ if (storeTypeName is not null)
+ {
+ if (StoreTypeMappings.TryGetValue(storeTypeName, out var mappings))
+ {
+ if (clrType is null)
+ {
+ return mappings[0];
+ }
+
+ foreach (var m in mappings)
+ {
+ if (m.ClrType == clrType)
+ {
+ return m;
+ }
+ }
+
+ return null;
+ }
+
+ if (StoreTypeMappings.TryGetValue(storeTypeNameBase!, out mappings))
+ {
+ if (clrType is null)
+ {
+ return mappings[0];
+ }
+
+ foreach (var m in mappings)
+ {
+ if (m.ClrType == clrType)
+ {
+ return m;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ if (clrType is not null)
+ {
+ if (ClrTypeMappings.TryGetValue(clrType, out var mapping))
+ {
+ return mapping;
+ }
+
+ if (clrType.IsAssignableTo(typeof(DateTimeZone)))
+ {
+ return _timeZone;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs
similarity index 89%
rename from src/EFCore.PG.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs
index c99e8f3224..bb183d05e7 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalMultirangeMapping.cs
@@ -1,7 +1,7 @@
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -9,7 +9,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class IntervalMultirangeMapping : NpgsqlTypeMapping
+public class IntervalMultirangeMapping : GaussDBTypeMapping
{
private readonly IntervalRangeMapping _intervalRangeMapping;
@@ -20,7 +20,7 @@ public class IntervalMultirangeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public IntervalMultirangeMapping(Type clrType, IntervalRangeMapping intervalRangeMapping)
- : base("tstzmultirange", clrType, NpgsqlDbType.TimestampTzMultirange)
+ : base("tstzmultirange", clrType, GaussDBDbType.TimestampTzMultirange)
{
_intervalRangeMapping = intervalRangeMapping;
}
@@ -32,7 +32,7 @@ public IntervalMultirangeMapping(Type clrType, IntervalRangeMapping intervalRang
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected IntervalMultirangeMapping(RelationalTypeMappingParameters parameters, IntervalRangeMapping intervalRangeMapping)
- : base(parameters, NpgsqlDbType.DateMultirange)
+ : base(parameters, GaussDBDbType.DateMultirange)
{
_intervalRangeMapping = intervalRangeMapping;
}
@@ -62,5 +62,5 @@ public override RelationalTypeMapping WithStoreTypeAndSize(string storeType, int
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override string GenerateNonNullSqlLiteral(object value)
- => NpgsqlMultirangeTypeMapping.GenerateNonNullSqlLiteral(value, _intervalRangeMapping, "tstzmultirange");
+ => GaussDBMultirangeTypeMapping.GenerateNonNullSqlLiteral(value, _intervalRangeMapping, "tstzmultirange");
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/IntervalRangeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalRangeMapping.cs
similarity index 95%
rename from src/EFCore.PG.NodaTime/Storage/Internal/IntervalRangeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalRangeMapping.cs
index e98372ccb2..67de953f45 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/IntervalRangeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/IntervalRangeMapping.cs
@@ -3,10 +3,10 @@
using System.Text;
using NodaTime.Text;
// ReSharper disable once CheckNamespace
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -14,7 +14,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class IntervalRangeMapping : NpgsqlTypeMapping
+public class IntervalRangeMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo _constructor =
typeof(Interval).GetConstructor([typeof(Instant), typeof(Instant)])!;
@@ -37,7 +37,7 @@ public class IntervalRangeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public IntervalRangeMapping()
- : base("tstzrange", typeof(Interval), NpgsqlDbType.TimestampTzRange)
+ : base("tstzrange", typeof(Interval), GaussDBDbType.TimestampTzRange)
{
}
@@ -48,7 +48,7 @@ public IntervalRangeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected IntervalRangeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.TimestampTzRange)
+ : base(parameters, GaussDBDbType.TimestampTzRange)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs
similarity index 93%
rename from src/EFCore.PG.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs
index c31a179ec5..8017617dc2 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/LegacyTimestampInstantMapping.cs
@@ -1,8 +1,8 @@
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
// Should only be used only with EnableLegacyTimestampBehavior.
// However, when upgrading to 6.0 with existing migrations, model snapshots still contain old mappings (Instant mapped to timestamp),
// and EF Core's model differ expects type mappings to be found for these. See https://github.com/dotnet/efcore/issues/26168.
-public class LegacyTimestampInstantMapping : NpgsqlTypeMapping
+public class LegacyTimestampInstantMapping : GaussDBTypeMapping
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -30,7 +30,7 @@ public class LegacyTimestampInstantMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public LegacyTimestampInstantMapping()
- : base("timestamp without time zone", typeof(Instant), NpgsqlDbType.Timestamp)
+ : base("timestamp without time zone", typeof(Instant), GaussDBDbType.Timestamp)
{
}
@@ -41,7 +41,7 @@ public LegacyTimestampInstantMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected LegacyTimestampInstantMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Timestamp)
+ : base(parameters, GaussDBDbType.Timestamp)
{
}
@@ -96,7 +96,7 @@ private string GenerateLiteralCore(object value)
{
var instant = (Instant)value;
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
if (instant == Instant.MinValue)
{
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/PeriodIntervalMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/PeriodIntervalMapping.cs
similarity index 96%
rename from src/EFCore.PG.NodaTime/Storage/Internal/PeriodIntervalMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/PeriodIntervalMapping.cs
index 99eba05e12..e41380ff70 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/PeriodIntervalMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/PeriodIntervalMapping.cs
@@ -1,10 +1,10 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -12,7 +12,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class PeriodIntervalMapping : NpgsqlTypeMapping
+public class PeriodIntervalMapping : GaussDBTypeMapping
{
private static readonly MethodInfo FromYears = typeof(Period).GetRuntimeMethod(nameof(Period.FromYears), [typeof(int)])!;
private static readonly MethodInfo FromMonths = typeof(Period).GetRuntimeMethod(nameof(Period.FromMonths), [typeof(int)])!;
@@ -45,7 +45,7 @@ public class PeriodIntervalMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public PeriodIntervalMapping()
- : base("interval", typeof(Period), NpgsqlDbType.Interval, JsonPeriodReaderWriter.Instance)
+ : base("interval", typeof(Period), GaussDBDbType.Interval, JsonPeriodReaderWriter.Instance)
{
}
@@ -56,7 +56,7 @@ public PeriodIntervalMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected PeriodIntervalMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Interval)
+ : base(parameters, GaussDBDbType.Interval)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeMapping.cs
similarity index 94%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeMapping.cs
index 64c8f4c764..2ce5a1d3f1 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeMapping.cs
@@ -1,11 +1,11 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
-using static Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Utilties.Util;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+using static HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties.Util;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimeMapping : NpgsqlTypeMapping
+public class TimeMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo ConstructorWithMinutes =
typeof(LocalTime).GetConstructor([typeof(int), typeof(int)])!;
@@ -41,7 +41,7 @@ public class TimeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimeMapping()
- : base("time", typeof(LocalTime), NpgsqlDbType.Time, JsonLocalTimeReaderWriter.Instance)
+ : base("time", typeof(LocalTime), GaussDBDbType.Time, JsonLocalTimeReaderWriter.Instance)
{
}
@@ -52,7 +52,7 @@ public TimeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Time)
+ : base(parameters, GaussDBDbType.Time)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimeTzMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeTzMapping.cs
similarity index 95%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimeTzMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeTzMapping.cs
index 7ffeb3116b..390a26beef 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimeTzMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimeTzMapping.cs
@@ -1,11 +1,11 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
-using static Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Utilties.Util;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+using static HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties.Util;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimeTzMapping : NpgsqlTypeMapping
+public class TimeTzMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo OffsetTimeConstructor =
typeof(OffsetTime).GetConstructor([typeof(LocalTime), typeof(Offset)])!;
@@ -53,7 +53,7 @@ public class TimeTzMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimeTzMapping()
- : base("time with time zone", typeof(OffsetTime), NpgsqlDbType.TimeTz, JsonOffsetTimeReaderWriter.Instance)
+ : base("time with time zone", typeof(OffsetTime), GaussDBDbType.TimeTz, JsonOffsetTimeReaderWriter.Instance)
{
}
@@ -64,7 +64,7 @@ public TimeTzMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimeTzMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.TimeTz)
+ : base(parameters, GaussDBDbType.TimeTz)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs
similarity index 93%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs
index a3b2b47f5f..104af0ae7d 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampLocalDateTimeMapping.cs
@@ -1,11 +1,11 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
-using static Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Utilties.Util;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+using static HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties.Util;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimestampLocalDateTimeMapping : NpgsqlTypeMapping
+public class TimestampLocalDateTimeMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo ConstructorWithMinutes =
typeof(LocalDateTime).GetConstructor([typeof(int), typeof(int), typeof(int), typeof(int), typeof(int)])!;
@@ -39,7 +39,7 @@ public class TimestampLocalDateTimeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimestampLocalDateTimeMapping()
- : base("timestamp without time zone", typeof(LocalDateTime), NpgsqlDbType.Timestamp, JsonLocalDateTimeReaderWriter.Instance)
+ : base("timestamp without time zone", typeof(LocalDateTime), GaussDBDbType.Timestamp, JsonLocalDateTimeReaderWriter.Instance)
{
}
@@ -50,7 +50,7 @@ public TimestampLocalDateTimeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimestampLocalDateTimeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.Timestamp)
+ : base(parameters, GaussDBDbType.Timestamp)
{
}
@@ -103,7 +103,7 @@ protected override string GenerateEmbeddedNonNullSqlLiteral(object value)
private static string Format(LocalDateTime localDateTime)
{
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
if (localDateTime == LocalDateTime.MinIsoValue)
{
@@ -153,7 +153,7 @@ public override LocalDateTime FromJsonTyped(ref Utf8JsonReaderManager manager, o
{
var s = manager.CurrentReader.GetString()!;
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
switch (s)
{
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs
similarity index 93%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs
index bf6f741b22..bd4785250d 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzInstantMapping.cs
@@ -1,10 +1,10 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -12,7 +12,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimestampTzInstantMapping : NpgsqlTypeMapping
+public class TimestampTzInstantMapping : GaussDBTypeMapping
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -29,7 +29,7 @@ public class TimestampTzInstantMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimestampTzInstantMapping()
- : base("timestamp with time zone", typeof(Instant), NpgsqlDbType.TimestampTz, JsonInstantReaderWriter.Instance)
+ : base("timestamp with time zone", typeof(Instant), GaussDBDbType.TimestampTz, JsonInstantReaderWriter.Instance)
{
}
@@ -40,7 +40,7 @@ public TimestampTzInstantMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimestampTzInstantMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.TimestampTz)
+ : base(parameters, GaussDBDbType.TimestampTz)
{
}
@@ -93,7 +93,7 @@ protected override string GenerateEmbeddedNonNullSqlLiteral(object value)
private static string Format(Instant instant)
{
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
if (instant == Instant.MinValue)
{
@@ -134,7 +134,7 @@ public override Instant FromJsonTyped(ref Utf8JsonReaderManager manager, object?
{
var s = manager.CurrentReader.GetString()!;
- if (!NpgsqlNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
+ if (!GaussDBNodaTimeTypeMappingSourcePlugin.DisableDateTimeInfinityConversions)
{
switch (s)
{
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs
similarity index 95%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs
index 8d54d819bf..c6cbd31425 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzOffsetDateTimeMapping.cs
@@ -1,11 +1,11 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
-using static Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime.Utilties.Util;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+using static HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties.Util;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimestampTzOffsetDateTimeMapping : NpgsqlTypeMapping
+public class TimestampTzOffsetDateTimeMapping : GaussDBTypeMapping
{
private static readonly ConstructorInfo Constructor =
typeof(OffsetDateTime).GetConstructor([typeof(LocalDateTime), typeof(Offset)])!;
@@ -39,7 +39,7 @@ public class TimestampTzOffsetDateTimeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimestampTzOffsetDateTimeMapping()
- : base("timestamp with time zone", typeof(OffsetDateTime), NpgsqlDbType.TimestampTz, JsonOffsetDateTimeReaderWriter.Instance)
+ : base("timestamp with time zone", typeof(OffsetDateTime), GaussDBDbType.TimestampTz, JsonOffsetDateTimeReaderWriter.Instance)
{
}
@@ -50,7 +50,7 @@ public TimestampTzOffsetDateTimeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimestampTzOffsetDateTimeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.TimestampTz)
+ : base(parameters, GaussDBDbType.TimestampTz)
{
}
diff --git a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs
similarity index 96%
rename from src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs
rename to src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs
index 8ed3c499f7..324fb8a43f 100644
--- a/src/EFCore.PG.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs
+++ b/src/EFCore.GaussDB.NodaTime/Storage/Internal/TimestampTzZonedDateTimeMapping.cs
@@ -2,10 +2,10 @@
using Microsoft.EntityFrameworkCore.Storage.Json;
using NodaTime.Text;
using NodaTime.TimeZones;
-using Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
// ReSharper disable once CheckNamespace
-namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -13,7 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public class TimestampTzZonedDateTimeMapping : NpgsqlTypeMapping
+public class TimestampTzZonedDateTimeMapping : GaussDBTypeMapping
{
private static readonly ZonedDateTimePattern Pattern =
ZonedDateTimePattern.CreateWithInvariantCulture(
@@ -35,7 +35,7 @@ public class TimestampTzZonedDateTimeMapping : NpgsqlTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TimestampTzZonedDateTimeMapping()
- : base("timestamp with time zone", typeof(ZonedDateTime), NpgsqlDbType.TimestampTz, JsonZonedDateTimeReaderWriter.Instance)
+ : base("timestamp with time zone", typeof(ZonedDateTime), GaussDBDbType.TimestampTz, JsonZonedDateTimeReaderWriter.Instance)
{
}
@@ -46,7 +46,7 @@ public TimestampTzZonedDateTimeMapping()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected TimestampTzZonedDateTimeMapping(RelationalTypeMappingParameters parameters)
- : base(parameters, NpgsqlDbType.TimestampTz)
+ : base(parameters, GaussDBDbType.TimestampTz)
{
}
diff --git a/src/EFCore.GaussDB.NodaTime/Utilties/Util.cs b/src/EFCore.GaussDB.NodaTime/Utilties/Util.cs
new file mode 100644
index 0000000000..8cdc7695d5
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/Utilties/Util.cs
@@ -0,0 +1,10 @@
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.Utilties;
+
+internal static class Util
+{
+ internal static NewExpression ConstantNew(ConstructorInfo constructor, params object[] parameters)
+ => Expression.New(constructor, parameters.Select(p => Expression.Constant(p)).ToArray());
+
+ internal static MethodCallExpression ConstantCall(MethodInfo method, params object[] parameters)
+ => Expression.Call(method, parameters.Select(p => Expression.Constant(p)).ToArray());
+}
diff --git a/src/EFCore.GaussDB.NodaTime/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.targets b/src/EFCore.GaussDB.NodaTime/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.targets
new file mode 100644
index 0000000000..1f525c0c74
--- /dev/null
+++ b/src/EFCore.GaussDB.NodaTime/build/netstandard2.0/HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime.targets
@@ -0,0 +1,46 @@
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ $(IntermediateOutputPath)EFCoreGaussDBNodaTime$(DefaultLanguageSourceExtension)
+
+
+
+
+
+
+ CompileBefore
+
+
+
+
+ CompileAfter
+
+
+
+
+
+
+ Compile
+
+
+
+
+
+
+ <_Parameter1>HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal.GaussDBNodaTimeDesignTimeServices, HuaweiCloud.EntityFrameworkCore.GaussDB.NodaTime
+ <_Parameter2>HuaweiCloud.EntityFrameworkCore.GaussDB
+
+
+
+
+
+
+
+
diff --git a/src/EFCore.GaussDB/Design/Internal/GaussDBAnnotationCodeGenerator.cs b/src/EFCore.GaussDB/Design/Internal/GaussDBAnnotationCodeGenerator.cs
new file mode 100644
index 0000000000..a5f83ed6ed
--- /dev/null
+++ b/src/EFCore.GaussDB/Design/Internal/GaussDBAnnotationCodeGenerator.cs
@@ -0,0 +1,447 @@
+using System.Diagnostics.CodeAnalysis;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Metadata;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Metadata.Internal;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public class GaussDBAnnotationCodeGenerator : AnnotationCodeGenerator
+{
+ #region MethodInfos
+
+ private static readonly MethodInfo ModelHasPostgresExtensionMethodInfo1
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresExtension), typeof(ModelBuilder), typeof(string));
+
+ private static readonly MethodInfo ModelHasPostgresExtensionMethodInfo2
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresExtension), typeof(ModelBuilder), typeof(string), typeof(string),
+ typeof(string));
+
+ private static readonly MethodInfo ModelHasPostgresEnumMethodInfo1
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresEnum), typeof(ModelBuilder), typeof(string), typeof(string[]));
+
+ private static readonly MethodInfo ModelHasPostgresEnumMethodInfo2
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresEnum), typeof(ModelBuilder), typeof(string), typeof(string), typeof(string[]));
+
+ private static readonly MethodInfo ModelHasPostgresRangeMethodInfo1
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresRange), typeof(ModelBuilder), typeof(string), typeof(string));
+
+ private static readonly MethodInfo ModelHasPostgresRangeMethodInfo2
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.HasPostgresRange), typeof(ModelBuilder), typeof(string), typeof(string), typeof(string),
+ typeof(string), typeof(string), typeof(string), typeof(string));
+
+ private static readonly MethodInfo ModelUseSerialColumnsMethodInfo
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.UseSerialColumns), typeof(ModelBuilder));
+
+ private static readonly MethodInfo ModelUseIdentityAlwaysColumnsMethodInfo
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.UseIdentityAlwaysColumns), typeof(ModelBuilder));
+
+ private static readonly MethodInfo ModelUseIdentityByDefaultColumnsMethodInfo
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.UseIdentityByDefaultColumns), typeof(ModelBuilder));
+
+ private static readonly MethodInfo ModelUseHiLoMethodInfo
+ = typeof(GaussDBModelBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.UseHiLo), typeof(ModelBuilder), typeof(string), typeof(string));
+
+ private static readonly MethodInfo ModelHasAnnotationMethodInfo
+ = typeof(ModelBuilder).GetRequiredRuntimeMethod(
+ nameof(ModelBuilder.HasAnnotation), typeof(string), typeof(object));
+
+ private static readonly MethodInfo ModelUseKeySequencesMethodInfo
+ = typeof(GaussDBModelBuilderExtensions).GetRuntimeMethod(
+ nameof(GaussDBModelBuilderExtensions.UseKeySequences), [typeof(ModelBuilder), typeof(string), typeof(string)])!;
+
+ private static readonly MethodInfo EntityTypeIsUnloggedMethodInfo
+ = typeof(GaussDBEntityTypeBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBEntityTypeBuilderExtensions.IsUnlogged), typeof(EntityTypeBuilder), typeof(bool));
+
+ private static readonly MethodInfo PropertyUseSerialColumnMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.UseSerialColumn), typeof(PropertyBuilder));
+
+ private static readonly MethodInfo PropertyUseIdentityAlwaysColumnMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.UseIdentityAlwaysColumn), typeof(PropertyBuilder));
+
+ private static readonly MethodInfo PropertyUseIdentityByDefaultColumnMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.UseIdentityByDefaultColumn), typeof(PropertyBuilder));
+
+ private static readonly MethodInfo PropertyUseHiLoMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.UseHiLo), typeof(PropertyBuilder), typeof(string), typeof(string));
+
+ private static readonly MethodInfo PropertyHasIdentityOptionsMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.HasIdentityOptions), typeof(PropertyBuilder), typeof(long?), typeof(long?),
+ typeof(long?), typeof(long?), typeof(bool?), typeof(long?));
+
+ private static readonly MethodInfo PropertyUseSequenceMethodInfo
+ = typeof(GaussDBPropertyBuilderExtensions).GetRuntimeMethod(
+ nameof(GaussDBPropertyBuilderExtensions.UseSequence), [typeof(PropertyBuilder), typeof(string), typeof(string)])!;
+
+ private static readonly MethodInfo IndexUseCollationMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.UseCollation), typeof(IndexBuilder), typeof(string[]));
+
+ private static readonly MethodInfo IndexHasMethodMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.HasMethod), typeof(IndexBuilder), typeof(string));
+
+ private static readonly MethodInfo IndexHasOperatorsMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.HasOperators), typeof(IndexBuilder), typeof(string[]));
+
+ private static readonly MethodInfo IndexHasNullSortOrderMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.HasNullSortOrder), typeof(IndexBuilder), typeof(NullSortOrder[]));
+
+ private static readonly MethodInfo IndexIncludePropertiesMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.IncludeProperties), typeof(IndexBuilder), typeof(string[]));
+
+ private static readonly MethodInfo IndexAreNullsDistinctMethodInfo
+ = typeof(GaussDBIndexBuilderExtensions).GetRequiredRuntimeMethod(
+ nameof(GaussDBIndexBuilderExtensions.AreNullsDistinct), typeof(IndexBuilder), typeof(bool));
+
+ #endregion MethodInfos
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBAnnotationCodeGenerator(AnnotationCodeGeneratorDependencies dependencies)
+ : base(dependencies)
+ {
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override bool IsHandledByConvention(IModel model, IAnnotation annotation)
+ {
+ Check.NotNull(model, nameof(model));
+ Check.NotNull(annotation, nameof(annotation));
+
+ if (annotation.Name == RelationalAnnotationNames.DefaultSchema
+ && (string?)annotation.Value == "public")
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override bool IsHandledByConvention(IIndex index, IAnnotation annotation)
+ {
+ Check.NotNull(index, nameof(index));
+ Check.NotNull(annotation, nameof(annotation));
+
+ if (annotation.Name == GaussDBAnnotationNames.IndexMethod
+ && (string?)annotation.Value == "btree")
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override bool IsHandledByConvention(IProperty property, IAnnotation annotation)
+ {
+ Check.NotNull(property, nameof(property));
+ Check.NotNull(annotation, nameof(annotation));
+
+ // The default by-convention value generation strategy is serial in pre-10 GaussDB,
+ // and IdentityByDefault otherwise.
+ if (annotation.Name == GaussDBAnnotationNames.ValueGenerationStrategy)
+ {
+ // Note: both serial and identity-by-default columns are considered by-convention - we don't want
+ // to assume that the GaussDB version of the scaffolded database necessarily determines the
+ // version of the database that the scaffolded model will target. This makes life difficult for
+ // models with mixed strategies but that's an edge case.
+ return (GaussDBValueGenerationStrategy?)annotation.Value switch
+ {
+ GaussDBValueGenerationStrategy.SerialColumn => true,
+ GaussDBValueGenerationStrategy.IdentityByDefaultColumn => true,
+ _ => false
+ };
+ }
+
+ return false;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override IReadOnlyList GenerateFluentApiCalls(
+ IModel model,
+ IDictionary annotations)
+ {
+ var fragments = new List(base.GenerateFluentApiCalls(model, annotations));
+
+ if (GenerateValueGenerationStrategy(annotations, onModel: true) is { } valueGenerationStrategy)
+ {
+ fragments.Add(valueGenerationStrategy);
+ }
+
+ return fragments;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override MethodCallCodeFragment? GenerateFluentApi(IModel model, IAnnotation annotation)
+ {
+ Check.NotNull(model, nameof(model));
+ Check.NotNull(annotation, nameof(annotation));
+
+ if (annotation.Name.StartsWith(GaussDBAnnotationNames.PostgresExtensionPrefix, StringComparison.Ordinal))
+ {
+ var extension = new GaussDBExtension(model, annotation.Name);
+
+ return extension.Schema is "public" or null
+ ? new MethodCallCodeFragment(ModelHasPostgresExtensionMethodInfo1, extension.Name)
+ : new MethodCallCodeFragment(ModelHasPostgresExtensionMethodInfo2, extension.Schema, extension.Name);
+ }
+
+ if (annotation.Name.StartsWith(GaussDBAnnotationNames.EnumPrefix, StringComparison.Ordinal))
+ {
+ var enumTypeDef = new GaussDBEnum(model, annotation.Name);
+
+ return enumTypeDef.Schema is null
+ ? new MethodCallCodeFragment(ModelHasPostgresEnumMethodInfo1, enumTypeDef.Name, enumTypeDef.Labels)
+ : new MethodCallCodeFragment(ModelHasPostgresEnumMethodInfo2, enumTypeDef.Schema, enumTypeDef.Name, enumTypeDef.Labels);
+ }
+
+ if (annotation.Name.StartsWith(GaussDBAnnotationNames.RangePrefix, StringComparison.Ordinal))
+ {
+ var rangeTypeDef = new Metadata.GaussDBRange(model, annotation.Name);
+
+ if (rangeTypeDef.Schema is null
+ && rangeTypeDef.CanonicalFunction is null
+ && rangeTypeDef.SubtypeOpClass is null
+ && rangeTypeDef.Collation is null
+ && rangeTypeDef.SubtypeDiff is null)
+ {
+ return new MethodCallCodeFragment(ModelHasPostgresRangeMethodInfo1, rangeTypeDef.Name, rangeTypeDef.Subtype);
+ }
+
+ return new MethodCallCodeFragment(
+ ModelHasPostgresRangeMethodInfo2,
+ rangeTypeDef.Schema,
+ rangeTypeDef.Name,
+ rangeTypeDef.Subtype,
+ rangeTypeDef.CanonicalFunction,
+ rangeTypeDef.SubtypeOpClass,
+ rangeTypeDef.Collation,
+ rangeTypeDef.SubtypeDiff);
+ }
+
+ return null;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override MethodCallCodeFragment? GenerateFluentApi(IEntityType entityType, IAnnotation annotation)
+ {
+ Check.NotNull(entityType, nameof(entityType));
+ Check.NotNull(annotation, nameof(annotation));
+
+ if (annotation.Name == GaussDBAnnotationNames.UnloggedTable)
+ {
+ return new MethodCallCodeFragment(EntityTypeIsUnloggedMethodInfo, annotation.Value);
+ }
+
+ return null;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override IReadOnlyList GenerateFluentApiCalls(
+ IProperty property,
+ IDictionary annotations)
+ {
+ var fragments = new List(base.GenerateFluentApiCalls(property, annotations));
+
+ if (GenerateValueGenerationStrategy(annotations, onModel: false) is { } valueGenerationStrategy)
+ {
+ fragments.Add(valueGenerationStrategy);
+ }
+
+ if (GenerateIdentityOptions(annotations) is { } identityOptionsFragment)
+ {
+ fragments.Add(identityOptionsFragment);
+ }
+
+ return fragments;
+ }
+
+ private MethodCallCodeFragment? GenerateValueGenerationStrategy(IDictionary annotations, bool onModel)
+ {
+ if (!TryGetAndRemove(annotations, GaussDBAnnotationNames.ValueGenerationStrategy, out GaussDBValueGenerationStrategy strategy))
+ {
+ return null;
+ }
+
+ switch (strategy)
+ {
+ case GaussDBValueGenerationStrategy.SerialColumn:
+ return new MethodCallCodeFragment(onModel ? ModelUseSerialColumnsMethodInfo : PropertyUseSerialColumnMethodInfo);
+
+ case GaussDBValueGenerationStrategy.IdentityAlwaysColumn:
+ return new MethodCallCodeFragment(
+ onModel ? ModelUseIdentityAlwaysColumnsMethodInfo : PropertyUseIdentityAlwaysColumnMethodInfo);
+
+ case GaussDBValueGenerationStrategy.IdentityByDefaultColumn:
+ return new MethodCallCodeFragment(
+ onModel ? ModelUseIdentityByDefaultColumnsMethodInfo : PropertyUseIdentityByDefaultColumnMethodInfo);
+
+ case GaussDBValueGenerationStrategy.SequenceHiLo:
+ {
+ var name = GetAndRemove(GaussDBAnnotationNames.HiLoSequenceName)!;
+ var schema = GetAndRemove(GaussDBAnnotationNames.HiLoSequenceSchema);
+ return new MethodCallCodeFragment(
+ onModel ? ModelUseHiLoMethodInfo : PropertyUseHiLoMethodInfo,
+ (name, schema) switch
+ {
+ (null, null) => [],
+ (_, null) => [name],
+ _ => [name!, schema]
+ });
+ }
+
+ case GaussDBValueGenerationStrategy.Sequence:
+ {
+ var nameOrSuffix = GetAndRemove(
+ onModel ? GaussDBAnnotationNames.SequenceNameSuffix : GaussDBAnnotationNames.SequenceName);
+
+ var schema = GetAndRemove(GaussDBAnnotationNames.SequenceSchema);
+ return new MethodCallCodeFragment(
+ onModel ? ModelUseKeySequencesMethodInfo : PropertyUseSequenceMethodInfo,
+ (name: nameOrSuffix, schema) switch
+ {
+ (null, null) => [],
+ (_, null) => [nameOrSuffix],
+ _ => [nameOrSuffix!, schema]
+ });
+ }
+ case GaussDBValueGenerationStrategy.None:
+ return new MethodCallCodeFragment(
+ ModelHasAnnotationMethodInfo, GaussDBAnnotationNames.ValueGenerationStrategy, GaussDBValueGenerationStrategy.None);
+
+ default:
+ throw new ArgumentOutOfRangeException(strategy.ToString());
+ }
+
+ T? GetAndRemove(string annotationName)
+ => TryGetAndRemove(annotations, annotationName, out T? annotationValue)
+ ? annotationValue
+ : default;
+ }
+
+ private MethodCallCodeFragment? GenerateIdentityOptions(IDictionary annotations)
+ {
+ if (!TryGetAndRemove(
+ annotations, GaussDBAnnotationNames.IdentityOptions,
+ out string? annotationValue))
+ {
+ return null;
+ }
+
+ var identityOptions = IdentitySequenceOptionsData.Deserialize(annotationValue);
+ return new MethodCallCodeFragment(
+ PropertyHasIdentityOptionsMethodInfo,
+ identityOptions.StartValue,
+ identityOptions.IncrementBy == 1 ? null : (long?)identityOptions.IncrementBy,
+ identityOptions.MinValue,
+ identityOptions.MaxValue,
+ identityOptions.IsCyclic ? true : null,
+ identityOptions.NumbersToCache == 1 ? null : (long?)identityOptions.NumbersToCache);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected override MethodCallCodeFragment? GenerateFluentApi(IIndex index, IAnnotation annotation)
+ => annotation.Name switch
+ {
+ RelationalAnnotationNames.Collation
+ => new MethodCallCodeFragment(IndexUseCollationMethodInfo, annotation.Value),
+
+ GaussDBAnnotationNames.IndexMethod
+ => new MethodCallCodeFragment(IndexHasMethodMethodInfo, annotation.Value),
+ GaussDBAnnotationNames.IndexOperators
+ => new MethodCallCodeFragment(IndexHasOperatorsMethodInfo, annotation.Value),
+ GaussDBAnnotationNames.IndexNullSortOrder
+ => new MethodCallCodeFragment(IndexHasNullSortOrderMethodInfo, annotation.Value),
+ GaussDBAnnotationNames.IndexInclude
+ => new MethodCallCodeFragment(IndexIncludePropertiesMethodInfo, annotation.Value),
+ GaussDBAnnotationNames.NullsDistinct
+ => new MethodCallCodeFragment(IndexAreNullsDistinctMethodInfo, annotation.Value),
+ _ => null
+ };
+
+ private static bool TryGetAndRemove(
+ IDictionary annotations,
+ string annotationName,
+ [NotNullWhen(true)] out T? annotationValue)
+ {
+ if (annotations.TryGetValue(annotationName, out var annotation)
+ && annotation.Value is not null)
+ {
+ annotations.Remove(annotationName);
+ annotationValue = (T)annotation.Value;
+ return true;
+ }
+
+ annotationValue = default;
+ return false;
+ }
+}
diff --git a/src/EFCore.GaussDB/Design/Internal/GaussDBCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.GaussDB/Design/Internal/GaussDBCSharpRuntimeAnnotationCodeGenerator.cs
new file mode 100644
index 0000000000..d19ab947e2
--- /dev/null
+++ b/src/EFCore.GaussDB/Design/Internal/GaussDBCSharpRuntimeAnnotationCodeGenerator.cs
@@ -0,0 +1,378 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Design.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Metadata.Internal;
+using HuaweiCloud.EntityFrameworkCore.GaussDB.Storage.Internal.Mapping;
+
+namespace HuaweiCloud.EntityFrameworkCore.GaussDB.Design.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+#pragma warning disable EF1001 // Internal EF Core API usage.
+public class GaussDBCSharpRuntimeAnnotationCodeGenerator
+ : RelationalCSharpRuntimeAnnotationCodeGenerator, ICSharpRuntimeAnnotationCodeGenerator
+{
+ private int _typeMappingNestingCount;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public GaussDBCSharpRuntimeAnnotationCodeGenerator(
+ CSharpRuntimeAnnotationCodeGeneratorDependencies dependencies,
+ RelationalCSharpRuntimeAnnotationCodeGeneratorDependencies relationalDependencies)
+ : base(dependencies, relationalDependencies)
+ {
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override bool Create(
+ CoreTypeMapping typeMapping,
+ CSharpRuntimeAnnotationCodeGeneratorParameters parameters,
+ ValueComparer? valueComparer = null,
+ ValueComparer? keyValueComparer = null,
+ ValueComparer? providerValueComparer = null)
+ {
+ _typeMappingNestingCount++;
+
+ try
+ {
+ var result = base.Create(typeMapping, parameters, valueComparer, keyValueComparer, providerValueComparer);
+ AddGaussDBTypeMappingTweaks(typeMapping, parameters);
+ return result;
+ }
+ finally
+ {
+ _typeMappingNestingCount--;
+ }
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ protected virtual void AddGaussDBTypeMappingTweaks(
+ CoreTypeMapping typeMapping,
+ CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ var mainBuilder = parameters.MainBuilder;
+
+ var npgsqlDbTypeBasedDefaultInstance = typeMapping switch
+ {
+ GaussDBStringTypeMapping => GaussDBStringTypeMapping.Default,
+ GaussDBUIntTypeMapping => GaussDBUIntTypeMapping.Default,
+ GaussDBULongTypeMapping => GaussDBULongTypeMapping.Default,
+ // GaussDBMultirangeTypeMapping => GaussDBMultirangeTypeMapping.Default,
+ _ => (IGaussDBTypeMapping?)null
+ };
+
+ if (npgsqlDbTypeBasedDefaultInstance is not null)
+ {
+ CheckElementTypeMapping();
+
+ var npgsqlDbType = ((IGaussDBTypeMapping)typeMapping).GaussDBDbType;
+
+ if (npgsqlDbType != npgsqlDbTypeBasedDefaultInstance.GaussDBDbType)
+ {
+ mainBuilder.AppendLine(";");
+
+ mainBuilder.Append(
+ $"{parameters.TargetName}.TypeMapping = (({typeMapping.GetType().Name}){parameters.TargetName}.TypeMapping).Clone(npgsqlDbType: ");
+
+ mainBuilder
+ .Append(nameof(GaussDBTypes))
+ .Append(".")
+ .Append(nameof(GaussDBDbType))
+ .Append(".")
+ .Append(npgsqlDbType.ToString());
+
+ mainBuilder
+ .Append(")")
+ .DecrementIndent();
+ }
+
+ }
+
+ switch (typeMapping)
+ {
+ case GaussDBEnumTypeMapping enumTypeMapping:
+ CheckElementTypeMapping();
+
+ var code = Dependencies.CSharpHelper;
+ mainBuilder.AppendLine(";");
+
+ mainBuilder.AppendLine(
+ $"{parameters.TargetName}.TypeMapping = ((GaussDBEnumTypeMapping){parameters.TargetName}.TypeMapping).Clone(")
+ .IncrementIndent();
+
+ mainBuilder
+ .Append("unquotedStoreType: ")
+ .Append(code.Literal(enumTypeMapping.UnquotedStoreType))
+ .AppendLine(",")
+ .AppendLine("labels: new Dictionary