diff --git a/.build/ca.crt b/.build/ca.crt
new file mode 100644
index 0000000000..e5a4081a02
--- /dev/null
+++ b/.build/ca.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIUB/AJgMX+fmeXvBOUWW7WR+XKZ6AwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDExMjAwNDExMjFaFw0zNDEx
+MTgwNDExMjFaMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQC8A5+//15VxRCxpHzl7srYx6uWQi1/7q5VFWFZab+7
+82PLr3pV/zMjSMEPBZdq46NWWNnXIFoFHd5MFnN4fNIQ1GIEsTF0kYy142qllnp3
+vLBVBu24n4dsmI8ygl8+1PuGwk45Mz+vOL+RjNIo6ra9yJzYFnZOGCqlt0kWkCau
+HR/43ms0vhKq8FaDXPdVXn9Z3EZScxRKQwlfAKOUxLQ8dVkzvRuAm0PF74afRYfg
+xiGIX8msFYKzGnWb7ezcag125iEqg+xSplo6QK6vaNURlKwYQ8ZRKz1Hk1oIB4t1
+iEJL2d4nzgTkh/jlVjtTXo6cw96WT9NBT0Rg6JR4PJySlhY+ZwLi6VAxQZ8GyJo4
+YTvx1K3vhXeokKjFTxUtZdx1blX5vCBXv9LCxnjAsBCTRzE425x6UP1gp721gHGW
+sqopvkUgN9vk8oigyWLeGvwsBwFTFnY672iCYXhFHs2oKTIX8yo+A2xRr8tewb9C
+IsqJSC6JkLs5zbVwKdgVx1H21Uwvi7XjKir9pPp/ks12r9GNMmWc265PK1kCqCHa
+oHfgzYMVVFQ3CfYbeeA8/aVf770AfC/1v+VtMse8DEqyep5q0OzOXtWIQlahYiyA
+FLTzCBqcHUuRZtS4gEhOk6/Pk1HP3faUC1xGgxO5c/pd7SVMfs+Z58WJbYGFcAlC
++QIDAQABo1MwUTAdBgNVHQ4EFgQUBeKaoc7AMURxdajJ+CF8YrUsdFgwHwYDVR0j
+BBgwFoAUBeKaoc7AMURxdajJ+CF8YrUsdFgwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAGGpFZm0c36Eh5E8QiAg8+8U22Ao+YoF6nJnIlc/ri1pt
+J5zXRM2DbCCR9uN5yckmCNIJ4PZO49QBflYGPAkF+Vd0RJYoA4k1Cq+eYcJBWtXl
+ESJxeg1QAKAZ4XSasOIijebWlPIZxPGOy8HquKNMDQIm8a7g5zSE4UNJPVY3y9on
+zJT7ZhntIwuM8IP6h6gotJfxBHJRWNe/g0zVITQ7vHnxSpobLbuKfY21GLl6clgI
+WsePKWWo/mZYquqZz72KBUJ66YX4X7nJCvZs1sLgMnXh87n9hsxAdFlRgLuQ4ztp
+mwQbDZ90mJFQLprI4rfyamuloIgOcn05yXfklRAI2P8L2/yf5xNAy+ii0OHRiMVv
+jnYUet8Bca1orh7OQ9ol1XTBoCI1gknrdG5Y2IQvQhWLiS5AjIwwQYwjkSFXELtF
+X8v9Fv758RA9CFlQDnsp9awNjdLss/TdH6+dNYQfTNGigIPM6oCk5nrcQqF/533W
+z2WM0LNHAiQlEn0X38D0wCuRwIVzPG/AFyfsf50vSlH81/uzpyR5q3SJA8OKiCV1
+/OiW7Jv7pOtwqFjxR+m31TqaPM6PLrdasP/CNKSvGuJmtaHK4Wkc3YU9dbtQffzB
+MUFwhi233gvE+nSEixse2KlzsrBVZIdz16bZXaAd20JQdq9Hceku2uVgfN1fycI=
+-----END CERTIFICATE-----
diff --git a/.build/server.crt b/.build/server.crt
index d161ab2652..5a2bfc7b01 100644
--- a/.build/server.crt
+++ b/.build/server.crt
@@ -1,20 +1,30 @@
-----BEGIN CERTIFICATE-----
-MIIDUjCCAjoCFAwuj6RwuZSjCGYHja8m9tbr3nFeMA0GCSqGSIb3DQEBCwUAMGgx
-EzARBgNVBAoTCk15IENvbXBhbnkxCzAJBgNVBAsTAklUMRAwDgYDVQQHEwdNeSBU
-b3duMQ8wDQYDVQQIEwZNb3Njb3cxCzAJBgNVBAYTAlJVMRQwEgYDVQQDEwtsb2Nh
-bGhvc3RDQTAeFw0yMTA0MTAxMzA0MDBaFw0yMjA0MTAxMzA0MDBaMGMxEzARBgNV
-BAoTCk15IENvbXBhbnkxCzAJBgNVBAsTAklUMRAwDgYDVQQHEwdNeSBUb3duMQ8w
-DQYDVQQIEwZNb3Njb3cxCzAJBgNVBAYTAlJVMQ8wDQYDVQQDEwZzZXJ2ZXIwggEi
-MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8LoQbo2DFwC17gZwJ8xrPKHGX
-UKxoo5UcyZ3/2zZ006TYkswssejKksuiICTMI89OD8n55pNTZkXPUH7oR2oIyxTY
-SiWPiNzbEh0FOxH9Kh5gmajqM/4X44OaprmyQ56m4Y2LZO2nZ9hHoe+ZRoan3+pa
-g8weOM/n/wYuXZtdElOxNsB8pg09K4gevHVaLaSBCEeQfHev51vClFdN3+orBi/r
-hnQF3vdw7oMT1JSH75Ray51wRaypLIslAc2DcPFTCQJMmXXMTcAcxmjAVUGrfY+d
-sSCdXnOZtd7yk+0X0bVGKLBkCTOP7QpmfOVu9bOhscDiK5EoAaDKqdHSMUfhAgMB
-AAEwDQYJKoZIhvcNAQELBQADggEBAKCo2Y1uKbudA8JpV6yo35tc7Z6n03++BAdq
-egUBKOiE4ze7xQ7lmlt572ptqXlU/8JuPWa2Qb/wGksR0HpVPTAeU3pbXz1dcCXC
-A9wCtSxapjyCYbkDrDl2FQuK0OfJi0q71JZU66D58Qu0l45nWON30to9dSiw3zPw
-Rdk7X86GHYIBHKsj7mjiy1v8jH1sXeWvThOmU6+rv8UY8VuJiu4MQDdYa0Y5KFh/
-OL3tVsi7zoNu2OXY1cTKuUpKMQPbO+WSdelYromYK2OAXaNqnC27GegPqvCFWJ2I
-9NZuXYj3X+j0ydZSKVjDgCda8H68olBnO0zh44XirCBef7uTVLw=
+MIIFJTCCAw0CFAKjNOhsMTYUuQngy2k291XuKOGGMA0GCSqGSIb3DQEBCwUAMEUx
+CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
+cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQxMTIwMDQxOTE0WhcNMjkxMTE5MDQx
+OTE0WjBZMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
+CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3Qw
+ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlGT9vXb93yoM1YT0GAxJI
+B6/2ExUrdprd049oMVZa4Km0nqwN/xjVvQRIWozmbpvps0mCkFM1ZyL1iqZFwiJG
+WcQvvIffFM1qKRMOSTLNPCbM9mfvRKsCU9gjgatdhy8xUZhz7uFGMGADnZdlNMYW
+GgzMVZo0EyW7Z2QJ+ZCl8wW5IT4iswZWrJsNZU/g7HaNBrXiidDihkmQ8Kt32R0U
+nqJeXMHwkQLxddmcGdDmVCKsAEUu3NcvPeAlSJsNHfGDRsf9fImRqZCsgwI8dJtA
+ke/luMTttQ34aADFTmTbVk4ngVhCxgBkJ6FUDFJcp3t3nFssiisNon9k5FwtJ3hl
+e/QGM9IRdBvGVcOnZZuXXK2lLtakj5UWUik2xWA0hjX+DsFo7TPwKgZy4zmWCRob
+W1e1NX52bqYFWZUKYLqbizllOd98o3yed58PhbF1/IuVEuOoiKu7rNdNgzr8vgRP
+pWHQNXp3maCcZq2kWybADU2LQNUKAZLSw3nClcX8QVRAfvf8IyDZ/280EYRGu99V
+qLqDPLa1+3CNAb93J1ONvVjKgJwQQWy4dYFLHTYdBzXV5SOpH8YHL/1IHs9W5k28
+BdwbeMtJnOaV8rqiA6Xd4Xem111AMAigHExxG3kpSnAq6jiOX0+2V++f7qAunuC6
+B/oJATXLCbBQILr0ARtKuQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCn6R2fvxfs
+R7nN9g6bVNJXkJrDJ+O1suVD0tkZzxZAIAFdhKnSFocJph1bC6bSZEQkhG+0WtfU
+DU7m19VDHpZWZ+8LygIVikIkvj47v1/yl7TgwkhNAKXXxl6bF/AEevMUZoxT3r8S
+UBFURp8QduSQ7sbDRB9qR1EWPjAXgnedzLSGkt5E6VKuVRwsTjv7QUTV8RCbOl9b
+YHtTX3dtvr3PeAB5M3B6qrbpniqJfPxUt658UKrDGFr1MuZZ8ONYpdiGH8uGXZhs
+9BBjp0g0xWha9LYDYRpqzlC1hqV0J/9jz9QdS9HHPsqa8PvB/YwaDGQm/RSRMUbU
+x0wip0me45WU5pLD1djEGQBlxCGgQXIJsebzipdUsayA4MgY3s2lBj2qsPOqyNoP
+dFohMm2+Ypi8UAjEbeGY4XsCODLeCvPx24HyjJUORm9uuPCunSBhtgiEBTJrNwHL
+F7T1+/g9gVSwCsz4MqceO7IooJ2omSpwk7xrzocccFb1HGR/tE9GxRLNHiyTfx9s
+FN9SNOih5DCcOFOiw0vF1qKHk6CAJ0UCBzVWl3YO9OgnFX4FbRYHd3PduWR+fSkd
+icBs2AiOKPbOU8yXR8CE6uZiDoN6A27KOE07adZEWBMwd4us7uBHGgnqqYuwPI3d
+nqC8srMQ07fw8HyXn7ojPxXyCk+2d6zVgA==
-----END CERTIFICATE-----
diff --git a/.build/server.key b/.build/server.key
index b6dd15913f..f2a7e607b2 100644
--- a/.build/server.key
+++ b/.build/server.key
@@ -1,27 +1,52 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEAvC6EG6NgxcAte4GcCfMazyhxl1CsaKOVHMmd/9s2dNOk2JLM
-LLHoypLLoiAkzCPPTg/J+eaTU2ZFz1B+6EdqCMsU2Eolj4jc2xIdBTsR/SoeYJmo
-6jP+F+ODmqa5skOepuGNi2Ttp2fYR6HvmUaGp9/qWoPMHjjP5/8GLl2bXRJTsTbA
-fKYNPSuIHrx1Wi2kgQhHkHx3r+dbwpRXTd/qKwYv64Z0Bd73cO6DE9SUh++UWsud
-cEWsqSyLJQHNg3DxUwkCTJl1zE3AHMZowFVBq32PnbEgnV5zmbXe8pPtF9G1Riiw
-ZAkzj+0KZnzlbvWzobHA4iuRKAGgyqnR0jFH4QIDAQABAoIBADnMS7U1dAao5Q9X
-GrcPnP9dm63vEFU/URA7eLTZ/prZWntOczmTFz4I4lSUbNjqcsS2IsIHqN5nvi9T
-uPbc4Ft9DJT2CR1R2wvKP3GY2AibBCOFbpUojPWHYqeAZ+6xyCvXgSL8R+YwBgTS
-XwYD3F35b0CH1Iy/xFOsR5i8FXj7He8lOBA76fPrH64DEBTB2zUGztu4qpfv57v5
-sfTISi2ZOqPpXc+8Fw0RPeVWQgSRUh7U3lzL8bNBod6lYcjkhF5Yqet4MdHSyWMT
-aKdZ2GRHHdWjpyx6J0cD/bjjaTSDqTD8r265mPzY6bq4t6UQMq4KeDnbeiextDf4
-ELT90YUCgYEA6insCSDJddhFZ51guPPyYE9GL8QQfnzLvFOA4qWsi0u9SAbJ9aS0
-vABaEuot0PyYPwMYq7st07z3DSKno4tisPJ2X7v2nEWxv8MjgczWpltPTPaEdmZE
-WGIwG3pyh5wJk1b3VpBJB5jkjtJfGmUJaezU10bzm4QhPiEawemCjucCgYEAzbri
-/6EZPbJJa9hGtkJEEVLwbQ2U/CE7mZXL+AcPlS3qMSwyz/1OArPxdTRR4S3sYRRO
-fsRDBL8LED/kKUDWNni/zkzmFf/hVkmGd9zc6eif4Zr1gmtHlsHQdaMGxsomzxGL
-qydBqDN+4TMmHmUmp2jR/0LIF5UMlNoCvHcxgfcCgYEAnOBNE6h1j4++n7Yd0IsO
-PFufx+xwqGzvCVJgLHeV6xRo0NJLh1g7BSCvN7DP1Q0E6mImqxaRkyMr2A75hGWj
-TqyBhY2ln/hJJxGSvij/PSA7NnKJN9E3xIazeBVGmXd+Ksm+lq2/X2mc5domgMZj
-0iUqSrdsCSoyIy+Gf5bzMs0CgYBcquG044vLDpOj0DeJwS+H3iQN+yAwsYd3FtJZ
-VlTejV//5ji9Fwwci5EnifmXxGfFErCIyT6m1KbXGvBa5KmYv6sl8d1x62BEzbmU
-JBgeBHp/1JzhshD9BzAuzNAwmr4AZ5bR8UzRxuBP8AorhsRyg/STVjFq7ehM5CZ3
-Xfke4QKBgHCPo3R/oi/E2E7OIM/ELlDpvPQTMrV+rYlMFsy3JRvataIqEGnVbhOR
-4dQHEM3u2bJxN79wUYYmZuymVB78wKxTn6hGWcGoM6Y8mrJjVv9D8V0Gc0sWw5pF
-KZxuCgzjaN2T7i1LsXEV3gaQrKItToEpGPzSI23egFaG6g5SFqBt
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDlGT9vXb93yoM1
+YT0GAxJIB6/2ExUrdprd049oMVZa4Km0nqwN/xjVvQRIWozmbpvps0mCkFM1ZyL1
+iqZFwiJGWcQvvIffFM1qKRMOSTLNPCbM9mfvRKsCU9gjgatdhy8xUZhz7uFGMGAD
+nZdlNMYWGgzMVZo0EyW7Z2QJ+ZCl8wW5IT4iswZWrJsNZU/g7HaNBrXiidDihkmQ
+8Kt32R0UnqJeXMHwkQLxddmcGdDmVCKsAEUu3NcvPeAlSJsNHfGDRsf9fImRqZCs
+gwI8dJtAke/luMTttQ34aADFTmTbVk4ngVhCxgBkJ6FUDFJcp3t3nFssiisNon9k
+5FwtJ3hle/QGM9IRdBvGVcOnZZuXXK2lLtakj5UWUik2xWA0hjX+DsFo7TPwKgZy
+4zmWCRobW1e1NX52bqYFWZUKYLqbizllOd98o3yed58PhbF1/IuVEuOoiKu7rNdN
+gzr8vgRPpWHQNXp3maCcZq2kWybADU2LQNUKAZLSw3nClcX8QVRAfvf8IyDZ/280
+EYRGu99VqLqDPLa1+3CNAb93J1ONvVjKgJwQQWy4dYFLHTYdBzXV5SOpH8YHL/1I
+Hs9W5k28BdwbeMtJnOaV8rqiA6Xd4Xem111AMAigHExxG3kpSnAq6jiOX0+2V++f
+7qAunuC6B/oJATXLCbBQILr0ARtKuQIDAQABAoICAAP97y6VPnPLjgLVJxKbfssa
+afz0IxG+9ZH11xrpUl6itjpNBUte8LN97jaF8DLhf9FJtZ2mWHJtODBfzw4wnldf
+X/O2Y1MZbvHeXA3LHznXX9ROJ9krg/2DCsu/MIZgh5hvQLEmdK6Iw1q7LH5Pz6YA
+Pea/YbPUfWGsVC0rUaBFB/C/oEnk/v0g8VIbFZIvAWrRw6oT0JWESJrGr5b9RYxm
+Ljo0Mt0dyorjP/YAUI6u4R+VOp9g+Dvpv7909vfg/j2u5k20e/lgI1xdXqGnvrIx
++/4V/KwPeob9TIqJ/bTOGaFtF5j3dirImP8Yq6rsvSuqodkSSELeAor2XEsDumby
+PqJY1MIO9DuZSdqf+Cofgzbd6mpeMAwueb+hfBw8AIMG3M9Xj1uDuU+tjsVA79Er
+H9acPxLukGjYP5SY2Mo8hLFLLurpjtcDpYdOP2Wh7PBDwHR8anmPQru2rZXT80NY
+j3fXNqnTTFbHuntmZ2qWJovmOuKocU5GEm/QCW/f6miqR9Hzc2vbWaIoEO54vcF6
+eS4iLEkAOfmakz3Sno2AXS1jJI6+2v1899cBINvgpATCMkmXnwFDwr9gNYujwlpF
+Yl3QM8Vh9dnVt04oyum5x4sz/mTKj5e9O988iqlOkgID4HBVpy/dwYHsHE+XgDDY
+yiFetJ/n0+45QHhyvSwBAoIBAQDnrPz2xCbR03KQwZN2DnZClLVFkZe3tZxR6UsY
+63yDTrA0ZMJ8AtE/tX79/Iu7gPidNTCrVmOuelf5q3y3AMo6nlKMCc3tIKr6QtaC
+99RtHq5p0T3/TS9tWbGjmxEzyx00R3wz5fSypX76qnQLHs6EmrLxFUNmsHIQS2nH
+jWvT1+TdmfmogZ/9RaHyBjHGkDfTmlfEKc7/TleE9XsW+G0cGli3fIO0iY0hJTLd
+b65X5Gm0URCqsZgIzD99enIvee13Gw8aUJUt8tJZXQHtOWBu491MLd2AVPQ/7eZa
+tl/HtjdMj2E3n5NXTie3laRCX+p9mK6087nE7u3JqPqUXU4BAoIBAQD9Jv3hZeii
+0pDgLYgiFVds5n2S4CEB4WOT9wn2vUIrYTSjgjAPfsgeJs6N6+WArwaIJrl4tTK4
+m0VjUG394plvyExU8hNZ7hw0E/33rwsKySnkwUFZtOgbsOgUjajRDfFYsqsDhLK0
+o3dY1M+mdYvU9OBo3EhgFy3fYBhtdGIq/4/3kSM6CARQIjddW2pdbB7pyv3qz0mH
+6fpzPXWLIex+WBzRVEz7VPPD4coV3LEhmtdPju4RqFPbHS+OpECun8pyaNt14DRr
+t216MiyJGNV74zTLELioVHlhlaPvsWnnIeI+2uhhCgQ8UvHn69x2wiAgLlx/e+RD
+qPiINhm/xey5AoIBACCASjSsK+3/xfC8110Whkys5AlQdYJWPgnXuqtSTfN11I5l
+HEudcZGIerpS9Z9mZnpXfe5rfix6CWGDR0m9GKHEmDwBHByKGrJlMgbJkcmFJl69
+9f6c62xhyuPy2yTy97Pf23LEbeGqCfhMdV8iAULlGPltTDlZw4a5ratLEbd0cC0O
+btHO7YzwedmkONNsZAiRfIKOgvWaHfkPHyeHznbE03FaTHfFXEEsIMij5Ed8Sb/8
+J2Rq6bNCRB3sUZyLdF7jMuk0KNl7WTskKyMGi5rC6MbJIGvifymAzHIpZ6Jy06sv
+6imNf3QeCMBeg96z6geYpdnI32TbSAykYhLyTAECggEAOowrCVcdX5LdaMt/AYr4
+BjqkbjShzaKH+i+XQVZyGEBKAUrZvKuwsrB88vvMv187Xn++Q3l8uo9Gk/qFBcPD
+gsPLS5YU/aaBJVY+VWtJXXw60SoU6B9b0xOuCRreIUNdPwtLW+vzvK1Vq9jEEZZ7
++YuM3xObNYYG2POLkrzo+1LRxArwH7q87J+NOG0tA2A/IgkNgqHgOqvVfZOIPN5i
+qLHOMGeTykjSe8obh8Tbvo7mHwNKchEBG9r7Jb09LGXOV3mC0BdDaGoqyqkR/b8d
+mKJqklBStLOcwwHtwUDB4m/GuIy+U7sSUbVJNz8oZNruvSKbx+wqVa+dkzsX529q
+GQKCAQBVzafsrfp3yZKa62R7EMtQh6pHDIKvUzZRwxsj4QzQ1y4Rrb6ceXKxI3EQ
+ZK6f1Lte+/ifRn8ZsxQOnjNzO9meOco/7CSNGCCcqO/XVN9ixDdF8lzjIsuRqfkT
+lsYy7Zo+ZRDUj73UROBvBJtX4jP5It1B/ISKxHxyBFQiB+UtldLl1H+dmGN9LVnF
+583i/vTEcLsj9+8yUU8L46sLKfOhNiSBY8D8oKD9Yht0p9SeDxB/r4Rq8Te5Xp1o
+FobswNohYBj2rj9+d24uMcpI5nx33JoRkW7VyAXsq8t4b7ei5/sbwuL25NUXhIxf
+mMKDxHebdrFY2ADhWLkWus0ik7JA
+-----END PRIVATE KEY-----
diff --git a/.devcontainer/db/Dockerfile b/.devcontainer/db/Dockerfile
index 64cc3febb1..85efd91832 100644
--- a/.devcontainer/db/Dockerfile
+++ b/.devcontainer/db/Dockerfile
@@ -1,3 +1,3 @@
-FROM postgres
+FROM postgres:17
RUN apt-get update && \
- apt-get install -y --no-install-recommends openssl postgresql-16-postgis-3
+ apt-get install -y --no-install-recommends openssl postgresql-17-postgis-3
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 69115b9ad3..1efa473614 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -5,30 +5,37 @@
"workspaceFolder": "/workspace",
- "settings": {
- "terminal.integrated.profiles.linux": {
- "bash": {
- "path": "/bin/bash"
- }
- },
- "terminal.integrated.defaultProfile.linux": "bash",
- "remote.extensionKind": {
- "ms-azuretools.vscode-docker": "workspace"
+ "features": {
+ "ghcr.io/devcontainers/features/dotnet:2": {
+ "version": "10.0",
+ "dotnetRuntimeVersions": "8.0,9.0"
}
},
- "extensions": [
- "ms-dotnettools.csharp",
- "formulahendry.dotnet-test-explorer",
- "ms-azuretools.vscode-docker",
- "mutantdino.resourcemonitor"
- ],
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.profiles.linux": {
+ "bash": {
+ "path": "/bin/bash"
+ }
+ },
+ "terminal.integrated.defaultProfile.linux": "bash",
+ "remote.extensionKind": {
+ "ms-azuretools.vscode-docker": "workspace"
+ }
+ },
- "forwardPorts": [5432, 5050],
-
- "remoteEnv": {
- "DeveloperBuild": "True"
+ "extensions": [
+ "ms-dotnettools.csharp",
+ "ms-dotnettools.csdevkit",
+ "ms-azuretools.vscode-docker",
+ "mutantdino.resourcemonitor"
+ ]
+ }
},
- "postCreateCommand": "dotnet restore Npgsql.sln"
+ "forwardPorts": [5432, 5050],
+
+ "postCreateCommand": "dotnet restore Npgsql.slnx"
}
\ No newline at end of file
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index e956e66c24..3926f919de 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -2,8 +2,7 @@ version: '3'
services:
npgsql-dev:
- # Source for tags: https://mcr.microsoft.com/v2/dotnet/sdk/tags/list
- image: mcr.microsoft.com/dotnet/sdk:8.0.100
+ image: mcr.microsoft.com/devcontainers/base:ubuntu
volumes:
- ..:/workspace:cached
tty: true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 78cf887e9d..29158fb563 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,13 +9,15 @@ on:
- '*'
pull_request:
+permissions:
+ contents: read
+
# Cancel previous PR branch commits (head_ref is only defined on PRs)
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
- dotnet_sdk_version: '8.0.100'
postgis_version: 3
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
# Windows comes with PG pre-installed, and defines the PGPASSWORD environment variable. Remove it as it interferes
@@ -29,35 +31,53 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-22.04, windows-2022]
- pg_major: [16, 15, 14, 13, 12]
+ os: [ubuntu-24.04]
+ pg_major: [18, 17, 16, 15, 14]
config: [Release]
- test_tfm: [net8.0]
+ test_tfm: [net10.0]
include:
- - os: ubuntu-22.04
- pg_major: 16
+ - os: ubuntu-24.04
+ pg_major: 18
config: Debug
- test_tfm: net8.0
- - os: macos-12
- pg_major: 14
+ test_tfm: net10.0
+ - os: macos-15
+ pg_major: 16
config: Release
- test_tfm: net8.0
-# - os: ubuntu-22.04
-# pg_major: 17
+ test_tfm: net10.0
+ - os: windows-2022
+ pg_major: 18
+ config: Release
+ test_tfm: net10.0
+
+ # Minimal support TFM build
+# - os: ubuntu-24.04
+# pg_major: 18
# config: Release
# test_tfm: net8.0
+
+ # PG prerelease build
+# - os: ubuntu-24.04
+# pg_major: 19
+# config: Release
+# test_tfm: net10.0
# pg_prerelease: 'PG Prerelease'
outputs:
is_release: ${{ steps.analyze_tag.outputs.is_release }}
is_prerelease: ${{ steps.analyze_tag.outputs.is_prerelease }}
+ # Installing PostGIS on Windows is complicated/unreliable, so we don't test on it.
+ # The NPGSQL_TEST_POSTGIS environment variable ensures that if PostGIS isn't installed,
+ # the PostGIS tests fail and therefore fail the build.
+ env:
+ NPGSQL_TEST_POSTGIS: ${{ !startsWith(matrix.os, 'windows') }}
+
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: NuGet Cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Build.targets') }}
@@ -65,10 +85,7 @@ jobs:
${{ runner.os }}-nuget-
- name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: |
- ${{ env.dotnet_sdk_version }}
+ uses: actions/setup-dotnet@v5.1.0
- name: Build
run: dotnet build -c ${{ matrix.config }}
@@ -80,17 +97,16 @@ jobs:
# First uninstall any PostgreSQL installed on the image
dpkg-query -W --showformat='${Package}\n' 'postgresql-*' | xargs sudo dpkg -P postgresql
- # Import the repository signing key
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
-
- sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ jammy-pgdg main ${{ matrix.pg_major }}" >> /etc/apt/sources.list.d/pgdg.list'
+ # 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-get update -qq
sudo apt-get install -qq postgresql-${{ matrix.pg_major }}
export PGDATA=/etc/postgresql/${{ matrix.pg_major }}/main
- sudo cp $GITHUB_WORKSPACE/.build/{server.crt,server.key} $PGDATA
- sudo chmod 600 $PGDATA/{server.crt,server.key}
- sudo chown postgres $PGDATA/{server.crt,server.key}
+ sudo cp $GITHUB_WORKSPACE/.build/{server.crt,server.key,ca.crt} $PGDATA
+ sudo chmod 600 $PGDATA/{server.crt,server.key,ca.crt}
+ sudo chown postgres $PGDATA/{server.crt,server.key,ca.crt}
# Create npgsql_tests user with md5 password 'npgsql_tests'
sudo -u postgres psql -c "CREATE USER npgsql_tests SUPERUSER PASSWORD 'md5adf74603a5772843f53e812f03dacb02'"
@@ -99,9 +115,9 @@ jobs:
sudo -u postgres psql -c "CREATE USER npgsql_tests_nossl SUPERUSER PASSWORD 'npgsql_tests_nossl'"
# To disable PostGIS for prereleases (because it usually isn't available until late), surround with the following:
- if [ -z "${{ matrix.pg_prerelease }}" ]; then
+ #if [ -z "${{ matrix.pg_prerelease }}" ]; then
sudo apt-get install -qq postgresql-${{ matrix.pg_major }}-postgis-${{ env.postgis_version }}
- fi
+ #fi
if [ ${{ matrix.pg_major }} -ge 14 ]; then
sudo sed -i "s|unix_socket_directories = '/var/run/postgresql'|unix_socket_directories = '/var/run/postgresql, @/npgsql_unix'|" $PGDATA/postgresql.conf
@@ -109,6 +125,7 @@ jobs:
sudo sed -i 's/max_connections = 100/max_connections = 500/' $PGDATA/postgresql.conf
sudo sed -i 's/#ssl = off/ssl = on/' $PGDATA/postgresql.conf
+ sudo sed -i "s|ssl_ca_file =|ssl_ca_file = '$PGDATA/ca.crt' #|" $PGDATA/postgresql.conf
sudo sed -i "s|ssl_cert_file =|ssl_cert_file = '$PGDATA/server.crt' #|" $PGDATA/postgresql.conf
sudo sed -i "s|ssl_key_file =|ssl_key_file = '$PGDATA/server.key' #|" $PGDATA/postgresql.conf
sudo sed -i 's/#password_encryption = md5/password_encryption = scram-sha-256/' $PGDATA/postgresql.conf
@@ -136,7 +153,7 @@ jobs:
sudo -u postgres psql -c "CREATE USER npgsql_tests_scram SUPERUSER PASSWORD 'npgsql_tests_scram'"
# Uncomment the following to SSH into the agent running the build (https://github.com/mxschmitt/action-tmate)
- #- uses: actions/checkout@v4
+ #- uses: actions/checkout@v6
#- name: Setup tmate session
# uses: mxschmitt/action-tmate@v3
@@ -159,29 +176,7 @@ jobs:
unzip pgsql.zip -x 'pgsql/include/**' 'pgsql/doc/**' 'pgsql/pgAdmin 4/**' 'pgsql/StackBuilder/**'
# Match Npgsql CI Docker image and stash one level up
- cp $GITHUB_WORKSPACE/.build/{server.crt,server.key} pgsql
-
- # 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_PATH}${POSTGIS_FILE}.zip
- unzip postgis.zip -d postgis
- cp -a postgis/$POSTGIS_FILE/. pgsql/
+ cp $GITHUB_WORKSPACE/.build/{server.crt,server.key,ca.crt} pgsql
# Start PostgreSQL
pgsql/bin/initdb -D pgsql/PGDATA -E UTF8 -U postgres
@@ -195,7 +190,7 @@ jobs:
sed -i "s|#synchronous_standby_names =|synchronous_standby_names = 'npgsql_test_sync_standby' #|" pgsql/PGDATA/postgresql.conf
sed -i "s|#synchronous_commit =|synchronous_commit = local #|" pgsql/PGDATA/postgresql.conf
sed -i "s|#max_prepared_transactions = 0|max_prepared_transactions = 100|" pgsql/PGDATA/postgresql.conf
- pgsql/bin/pg_ctl -D pgsql/PGDATA -l logfile -o '-c ssl=true -c ssl_cert_file=../server.crt -c ssl_key_file=../server.key' start
+ pgsql/bin/pg_ctl -D pgsql/PGDATA -l logfile -o '-c ssl=true -c ssl_cert_file=../server.crt -c ssl_key_file=../server.key -c ssl_ca_file=../ca.crt' start
# Create npgsql_tests user with md5 password 'npgsql_tests'
pgsql/bin/psql -U postgres -c "CREATE ROLE npgsql_tests SUPERUSER LOGIN PASSWORD 'md5adf74603a5772843f53e812f03dacb02'"
@@ -210,7 +205,7 @@ jobs:
sed -i "s|#password_encryption = md5|password_encryption = scram-sha-256|" pgsql/PGDATA/postgresql.conf
fi
- pgsql/bin/pg_ctl -D pgsql/PGDATA -l logfile -o '-c ssl=true -c ssl_cert_file=../server.crt -c ssl_key_file=../server.key' restart
+ pgsql/bin/pg_ctl -D pgsql/PGDATA -l logfile -o '-c ssl=true -c ssl_cert_file=../server.crt -c ssl_key_file=../server.key -c ssl_ca_file=../ca.crt' restart
pgsql/bin/psql -U postgres -c "CREATE ROLE npgsql_tests_scram SUPERUSER LOGIN PASSWORD 'npgsql_tests_scram'"
@@ -232,15 +227,20 @@ jobs:
- name: Start PostgreSQL ${{ matrix.pg_major }} (MacOS)
if: startsWith(matrix.os, 'macos')
run: |
- PGDATA=/usr/local/var/postgresql@${{ matrix.pg_major }}
+ brew update
+ brew install postgresql@${{ matrix.pg_major }}
+
+ PGDATA=/opt/homebrew/var/postgresql@${{ matrix.pg_major }}
sudo sed -i '' 's/#ssl = off/ssl = on/' $PGDATA/postgresql.conf
- cp $GITHUB_WORKSPACE/.build/{server.crt,server.key} $PGDATA
- chmod 600 $PGDATA/{server.crt,server.key}
+ sudo sed -i '' "s/#ssl_ca_file =/ssl_ca_file = 'ca.crt' #/" $PGDATA/postgresql.conf
+ cp $GITHUB_WORKSPACE/.build/{server.crt,server.key,ca.crt} $PGDATA
+ chmod 600 $PGDATA/{server.crt,server.key,ca.crt}
- postgreService=$(brew services list | grep -oe "postgresql\S*")
+ postgreService=$(brew services list | grep -oe "postgresql@${{ matrix.pg_major }}\S*")
brew services start $postgreService
+ export PATH="/opt/homebrew/opt/postgresql@${{ matrix.pg_major }}/bin:$PATH"
echo "Check PostgreSQL service is running"
i=5
COMMAND='pg_isready'
@@ -301,10 +301,19 @@ jobs:
# TODO: Once test/Npgsql.Specification.Tests work, switch to just testing on the solution
- name: Test
run: |
- dotnet test -c ${{ matrix.config }} -f ${{ matrix.test_tfm }} test/Npgsql.Tests --logger "GitHubActions;report-warnings=false"
+ dotnet test -c ${{ matrix.config }} -f ${{ matrix.test_tfm }} test/Npgsql.Tests --logger "GitHubActions;report-warnings=false" --blame-crash --blame-hang-timeout 30s
dotnet test -c ${{ matrix.config }} -f ${{ matrix.test_tfm }} test/Npgsql.DependencyInjection.Tests --logger "GitHubActions;report-warnings=false"
shell: bash
+ - name: Upload Test Hang Dumps
+ uses: actions/upload-artifact@v7
+ if: failure()
+ with:
+ name: test-hang-dumps
+ path: |
+ **/*.dmp
+ **/*_Sequence.xml
+
- name: Test Plugins
if: "!startsWith(matrix.os, 'macos')"
run: |
@@ -328,16 +337,16 @@ jobs:
publish-ci:
needs: build
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
if: github.event_name == 'push' && github.repository == 'npgsql/npgsql'
environment: myget
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: NuGet Cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Build.targets') }}
@@ -345,15 +354,13 @@ jobs:
${{ runner.os }}-nuget-
- name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: ${{ env.dotnet_sdk_version }}
+ uses: actions/setup-dotnet@v5.1.0
- name: Pack
- run: dotnet pack Npgsql.sln --configuration Release --property:PackageOutputPath="$PWD/nupkgs" --version-suffix "ci.$(date -u +%Y%m%dT%H%M%S)+sha.${GITHUB_SHA:0:9}" -p:ContinuousIntegrationBuild=true
+ run: dotnet pack --configuration Release --property:PackageOutputPath="$PWD/nupkgs" --version-suffix "ci.$(date -u +%Y%m%dT%H%M%S)+sha.${GITHUB_SHA:0:9}" -p:ContinuousIntegrationBuild=true
- name: Upload artifacts (nupkg)
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Npgsql.CI
path: nupkgs
@@ -370,24 +377,22 @@ jobs:
release:
needs: build
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
if: github.event_name == 'push' && startsWith(github.repository, 'npgsql/') && needs.build.outputs.is_release == 'true'
environment: nuget.org
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: ${{ env.dotnet_sdk_version }}
+ uses: actions/setup-dotnet@v5.1.0
- name: Pack
- run: dotnet pack Npgsql.sln --configuration Release --property:PackageOutputPath="$PWD/nupkgs" -p:ContinuousIntegrationBuild=true
+ run: dotnet pack --configuration Release --property:PackageOutputPath="$PWD/nupkgs" -p:ContinuousIntegrationBuild=true
- name: Upload artifacts
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: Npgsql.Release
path: nupkgs
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index 868ea2418b..0000000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches:
- - main
- - 'hotfix/**'
- - 'release/**'
- pull_request:
- # The branches below must be a subset of the branches above
- branches:
- - main
- - 'hotfix/**'
- - 'release/**'
- schedule:
- - cron: '21 0 * * 4'
-
-# Cancel previous PR branch commits (head_ref is only defined on PRs)
-concurrency:
- group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
- cancel-in-progress: true
-
-env:
- dotnet_sdk_version: '8.0.100'
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'csharp' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
- # Learn more about CodeQL language support at https://git.io/codeql-language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- - name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: ${{ env.dotnet_sdk_version }}
-
- - name: Build
- run: dotnet build -c Release
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- #- name: Autobuild
- # uses: github/codeql-action/autobuild@v2
-
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 https://git.io/JvXDl
-
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/native-aot.yml b/.github/workflows/native-aot.yml
index 97cf2878e7..cdc4d77ab5 100644
--- a/.github/workflows/native-aot.yml
+++ b/.github/workflows/native-aot.yml
@@ -9,54 +9,100 @@ on:
- '*'
pull_request:
+permissions:
+ contents: read
+
# Cancel previous PR branch commits (head_ref is only defined on PRs)
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
- dotnet_sdk_version: '8.0.100'
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ AOT_Compat: |
+ param([string]$targetFramework)
+
+ $publishOutput = dotnet publish test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj -r linux-x64 -c Release -f $targetFramework -p:RootNpgsql=true
+
+ $actualWarningCount = 0
+
+ foreach ($line in $($publishOutput -split "`r`n"))
+ {
+ if ($line -like "*analysis warning IL*")
+ {
+ Write-Host $line
+
+ $actualWarningCount += 1
+ }
+ }
+
+ $testPassed = 0
+
+ $binaryPath = "test/Npgsql.NativeAotTests/bin/Release/$targetFramework/linux-x64/native/"
+ if (-not (Test-Path -LiteralPath $binaryPath))
+ {
+ $testPassed = 1
+ Write-Host "Could not publish app, output was:"
+ foreach ($line in $($publishOutput -split "`r`n"))
+ {
+ Write-Host $line
+ }
+ }
+
+ Write-Host "Actual warning count is:", $actualWarningCount
+ $expectedWarningCount = 0
+
+ if ($actualWarningCount -ne $expectedWarningCount)
+ {
+ $testPassed = 2
+ Write-Host "Actual warning count:", $actualWarningCount, "is not as expected. Expected warning count is:", $expectedWarningCount
+ }
+
+ Exit $testPassed
# Uncomment and edit the following to use nightly/preview builds
-# nuget_config: |
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
-#
+ # nuget_config: |
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
+ #
jobs:
- build:
+ full:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
- os: [ubuntu-22.04]
- pg_major: [15]
+ os: [ ubuntu-24.04 ]
+ pg_major: [ 18 ]
+ tfm: [ net10.0 ]
steps:
- name: Checkout
- uses: actions/checkout@v4
-
+ uses: actions/checkout@v6
+
+ # - name: Setup nuget config
+ # run: echo "$nuget_config" > NuGet.config
+
- name: NuGet Cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Build.targets') }}
@@ -64,71 +110,95 @@ jobs:
${{ runner.os }}-nuget-
- name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: |
- ${{ env.dotnet_sdk_version }}
+ uses: actions/setup-dotnet@v5.1.0
+
+ - name: Write script
+ run: echo "$AOT_Compat" > test-aot-compatibility.ps1
+
+ - name: Publish and check for trimmer warnings
+ run: ./test-aot-compatibility.ps1 ${{ matrix.tfm }}
+ shell: pwsh
+ trimmed:
+ runs-on: ${{ matrix.os }}
-# - name: Setup nuget config
-# run: echo "$nuget_config" > NuGet.config
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-24.04]
+ pg_major: [ 18 ]
+ tfm: [ net10.0 ]
- - name: Setup Native AOT prerequisites
- run: sudo apt-get install clang zlib1g-dev
- shell: bash
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
- - name: Build
- run: dotnet publish test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj -r linux-x64 -c Release -f net8.0 -p:OptimizationPreference=Size
- shell: bash
+ # - name: Setup nuget config
+ # run: echo "$nuget_config" > NuGet.config
- # Uncomment the following to SSH into the agent running the build (https://github.com/mxschmitt/action-tmate)
- #- uses: actions/checkout@v4
- #- name: Setup tmate session
- # uses: mxschmitt/action-tmate@v3
+ - name: NuGet Cache
+ uses: actions/cache@v5
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Build.targets') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
+
+ - name: Setup .NET Core SDK
+ uses: actions/setup-dotnet@v5.1.0
- name: Start PostgreSQL
run: |
sudo systemctl start postgresql.service
sudo -u postgres psql -c "CREATE USER npgsql_tests SUPERUSER PASSWORD 'npgsql_tests'"
sudo -u postgres psql -c "CREATE DATABASE npgsql_tests OWNER npgsql_tests"
+
+ - name: Build
+ run: dotnet publish test/Npgsql.NativeAotTests/Npgsql.NativeAotTests.csproj -r linux-x64 -c Release -f ${{ matrix.tfm }} -p:OptimizationPreference=Size
+ shell: bash
+
+ # Uncomment the following to SSH into the agent running the build (https://github.com/mxschmitt/action-tmate)
+ #- uses: actions/checkout@v6
+ #- name: Setup tmate session
+ # uses: mxschmitt/action-tmate@v3
- name: Run
- run: test/Npgsql.NativeAotTests/bin/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests
+ run: test/Npgsql.NativeAotTests/bin/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests
- name: Write binary size to summary
run: |
- size="$(ls -l test/Npgsql.NativeAotTests/bin/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests | cut -d ' ' -f 5)"
+ size="$(ls -l test/Npgsql.NativeAotTests/bin/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests | cut -d ' ' -f 5)"
echo "Binary size is $size bytes ($((size / (1024 * 1024))) mb)" >> $GITHUB_STEP_SUMMARY
- name: Dump mstat
- run: dotnet run --project test/MStatDumper/MStatDumper.csproj -c release -f net8.0 -- "test/Npgsql.NativeAotTests/obj/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests.mstat" md >> $GITHUB_STEP_SUMMARY
+ run: dotnet run --project test/MStatDumper/MStatDumper.csproj -c release -f ${{ matrix.tfm }} -- "test/Npgsql.NativeAotTests/obj/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests.mstat" md >> $GITHUB_STEP_SUMMARY
- name: Upload mstat
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: npgsql.mstat
- path: "test/Npgsql.NativeAotTests/obj/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests.mstat"
+ path: "test/Npgsql.NativeAotTests/obj/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests.mstat"
retention-days: 3
- name: Upload codedgen dgml
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: npgsql.codegen.dgml.xml
- path: "test/Npgsql.NativeAotTests/obj/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests.codegen.dgml.xml"
+ path: "test/Npgsql.NativeAotTests/obj/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests.codegen.dgml.xml"
retention-days: 3
- name: Upload scan dgml
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: npgsql.scan.dgml.xml
- path: "test/Npgsql.NativeAotTests/obj/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests.scan.dgml.xml"
+ path: "test/Npgsql.NativeAotTests/obj/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests.scan.dgml.xml"
retention-days: 3
- name: Assert binary size
run: |
- size="$(ls -l test/Npgsql.NativeAotTests/bin/Release/net8.0/linux-x64/native/Npgsql.NativeAotTests | cut -d ' ' -f 5)"
+ size="$(ls -l test/Npgsql.NativeAotTests/bin/Release/${{ matrix.tfm }}/linux-x64/native/Npgsql.NativeAotTests | cut -d ' ' -f 5)"
echo "Binary size is $size bytes ($((size / (1024 * 1024))) mb)"
- if (( size > 7340032 )); then
- echo "Binary size exceeds 7mb threshold"
+ if (( size > 5242880 )); then
+ echo "Binary size exceeds 5MB threshold"
exit 1
fi
diff --git a/.github/workflows/rich-code-nav.yml b/.github/workflows/rich-code-nav.yml
deleted file mode 100644
index 7ee82bfeb9..0000000000
--- a/.github/workflows/rich-code-nav.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: Rich Code Navigation
-
-on:
- workflow_dispatch:
-
-env:
- dotnet_sdk_version: '8.0.100'
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
-
-jobs:
- build:
- runs-on: windows-latest
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
-
- - name: NuGet Cache
- uses: actions/cache@v4
- with:
- path: ~/.nuget/packages
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Build.targets') }}
- restore-keys: |
- ${{ runner.os }}-nuget-
-
- - name: Setup .NET Core SDK
- uses: actions/setup-dotnet@v4.0.0
- with:
- dotnet-version: ${{ env.dotnet_sdk_version }}
-
- - name: Build
- run: dotnet build Npgsql.sln --configuration Debug
- shell: bash
-
- - name: Rich Navigation Indexing
- uses: microsoft/RichCodeNavIndexer@v0
- with:
- languages: csharp
- repo-token: ${{ github.token }}
diff --git a/.github/workflows/trigger-doc-build.yml b/.github/workflows/trigger-doc-build.yml
index dfbe89601e..e8783c9e16 100644
--- a/.github/workflows/trigger-doc-build.yml
+++ b/.github/workflows/trigger-doc-build.yml
@@ -8,9 +8,12 @@ on:
branches:
- docs
+permissions:
+ contents: read
+
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-latest
steps:
- name: Trigger documentation build
run: |
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 6bf5ff40c5..a505eb8cfc 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -2,10 +2,6 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-dotnettools.csharp",
- "formulahendry.dotnet-test-explorer",
- ],
- // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
- "unwantedRecommendations": [
-
+ "ms-dotnettools.csdevkit"
]
}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 3f641af41f..22993a3100 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,3 @@
{
- "omnisharp.defaultLaunchSolution": "Npgsql.sln",
- "dotnet-test-explorer.testProjectPath": "**/*.Tests.csproj"
+ "dotnet.defaultSolution": "Npgsql.slnx"
}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 57494750c7..482dbaf297 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,6 +1,6 @@
- 9.0.0-preview.1
+ 11.0.0-preview.1
latest
true
enable
@@ -10,7 +10,7 @@
true
true
- Copyright 2023 © The Npgsql Development Team
+ Copyright 2026 © The Npgsql Development Team
Npgsql
PostgreSQL
https://github.com/npgsql/npgsql
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 132bbd43e8..a197b8c29e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,52 +1,53 @@
-
- 8.0.0
- $(SystemVersion)
+
+ 10.0.3
+ 10.0.3
+
+
+ 10.0.3
+ 10.0.3
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
+
+
+
-
+
diff --git a/LICENSE b/LICENSE
index efec310cda..5f0d26b868 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2002-2023, Npgsql
+Copyright (c) 2002-2026, Npgsql
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
diff --git a/Npgsql.sln b/Npgsql.sln
deleted file mode 100644
index 80ef02c3a8..0000000000
--- a/Npgsql.sln
+++ /dev/null
@@ -1,204 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28822.285
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{ED612DB1-AB32-4603-95E7-891BACA71C39}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql", "src\Npgsql\Npgsql.csproj", "{9D13B739-62B1-4190-B386-7A9547304EB3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.Tests", "test\Npgsql.Tests\Npgsql.Tests.csproj", "{E9C258D7-0D8E-4E6A-9857-5C6438591755}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.Benchmarks", "test\Npgsql.Benchmarks\Npgsql.Benchmarks.csproj", "{8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.Json.NET", "src\Npgsql.Json.NET\Npgsql.Json.NET.csproj", "{9CBE603F-6746-411D-A5FD-CB2C948CD7D0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.NodaTime", "src\Npgsql.NodaTime\Npgsql.NodaTime.csproj", "{D8DF12D6-FA70-4653-BD8F-C188944836DE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.PluginTests", "test\Npgsql.PluginTests\Npgsql.PluginTests.csproj", "{9BD7FC3D-6956-42A8-A586-2558C499EBA2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.NetTopologySuite", "src\Npgsql.NetTopologySuite\Npgsql.NetTopologySuite.csproj", "{6CB12050-DC9B-4155-BADD-BFDD54CDD70F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.GeoJSON", "src\Npgsql.GeoJSON\Npgsql.GeoJSON.csproj", "{F7C53EBD-0075-474F-A083-419257D04080}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Npgsql.Specification.Tests", "test\Npgsql.Specification.Tests\Npgsql.Specification.Tests.csproj", "{A77E5FAF-D775-4AB4-8846-8965C2104E60}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{004A2E0F-D34A-44D4-8DF0-D2BC63B57073}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- Directory.Build.props = Directory.Build.props
- Directory.Packages.props = Directory.Packages.props
- README.md = README.md
- global.json = global.json
- NuGet.config = NuGet.config
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.SourceGenerators", "src\Npgsql.SourceGenerators\Npgsql.SourceGenerators.csproj", "{63026A19-60B8-4906-81CB-216F30E8094B}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.OpenTelemetry", "src\Npgsql.OpenTelemetry\Npgsql.OpenTelemetry.csproj", "{DA29F063-1828-47D8-B051-800AF7C9A0BE}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Github", "Github", "{BA7B6F53-D24D-45AC-927A-266857EA8D1E}"
- ProjectSection(SolutionItems) = preProject
- .github\workflows\build.yml = .github\workflows\build.yml
- .github\dependabot.yml = .github\dependabot.yml
- .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
- .github\workflows\rich-code-nav.yml = .github\workflows\rich-code-nav.yml
- .github\workflows\native-aot.yml = .github\workflows\native-aot.yml
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.DependencyInjection", "src\Npgsql.DependencyInjection\Npgsql.DependencyInjection.csproj", "{B58E12EB-E43D-4D77-894E-5157D2269836}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.DependencyInjection.Tests", "test\Npgsql.DependencyInjection.Tests\Npgsql.DependencyInjection.Tests.csproj", "{EB2530FC-69F7-4DCB-A8B3-3671A157ED32}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.NativeAotTests", "test\Npgsql.NativeAotTests\Npgsql.NativeAotTests.csproj", "{20F2E9D6-A69E-4BAE-9236-574B0AA59139}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Debug|x86.Build.0 = Debug|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Release|Any CPU.Build.0 = Release|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Release|x86.ActiveCfg = Release|Any CPU
- {9D13B739-62B1-4190-B386-7A9547304EB3}.Release|x86.Build.0 = Release|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Debug|x86.ActiveCfg = Debug|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Debug|x86.Build.0 = Debug|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Release|Any CPU.Build.0 = Release|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Release|x86.ActiveCfg = Release|Any CPU
- {E9C258D7-0D8E-4E6A-9857-5C6438591755}.Release|x86.Build.0 = Release|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Debug|x86.ActiveCfg = Debug|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Debug|x86.Build.0 = Debug|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Release|Any CPU.Build.0 = Release|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Release|x86.ActiveCfg = Release|Any CPU
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8}.Release|x86.Build.0 = Release|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Debug|x86.Build.0 = Debug|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Release|Any CPU.Build.0 = Release|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Release|x86.ActiveCfg = Release|Any CPU
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0}.Release|x86.Build.0 = Release|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Debug|x86.Build.0 = Debug|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Release|Any CPU.Build.0 = Release|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Release|x86.ActiveCfg = Release|Any CPU
- {D8DF12D6-FA70-4653-BD8F-C188944836DE}.Release|x86.Build.0 = Release|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Debug|x86.Build.0 = Debug|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|Any CPU.Build.0 = Release|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|x86.ActiveCfg = Release|Any CPU
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2}.Release|x86.Build.0 = Release|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Debug|x86.Build.0 = Debug|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Release|Any CPU.Build.0 = Release|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Release|x86.ActiveCfg = Release|Any CPU
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F}.Release|x86.Build.0 = Release|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Debug|x86.Build.0 = Debug|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Release|Any CPU.Build.0 = Release|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Release|x86.ActiveCfg = Release|Any CPU
- {F7C53EBD-0075-474F-A083-419257D04080}.Release|x86.Build.0 = Release|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Debug|x86.Build.0 = Debug|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Release|Any CPU.Build.0 = Release|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Release|x86.ActiveCfg = Release|Any CPU
- {A77E5FAF-D775-4AB4-8846-8965C2104E60}.Release|x86.Build.0 = Release|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Debug|x86.Build.0 = Debug|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Release|Any CPU.Build.0 = Release|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Release|x86.ActiveCfg = Release|Any CPU
- {63026A19-60B8-4906-81CB-216F30E8094B}.Release|x86.Build.0 = Release|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Debug|x86.Build.0 = Debug|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Release|Any CPU.Build.0 = Release|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Release|x86.ActiveCfg = Release|Any CPU
- {DA29F063-1828-47D8-B051-800AF7C9A0BE}.Release|x86.Build.0 = Release|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Debug|x86.ActiveCfg = Debug|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Debug|x86.Build.0 = Debug|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Release|Any CPU.Build.0 = Release|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Release|x86.ActiveCfg = Release|Any CPU
- {B58E12EB-E43D-4D77-894E-5157D2269836}.Release|x86.Build.0 = Release|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Debug|x86.ActiveCfg = Debug|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Debug|x86.Build.0 = Debug|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Release|Any CPU.Build.0 = Release|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Release|x86.ActiveCfg = Release|Any CPU
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32}.Release|x86.Build.0 = Release|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Debug|x86.ActiveCfg = Debug|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Debug|x86.Build.0 = Debug|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Release|Any CPU.Build.0 = Release|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Release|x86.ActiveCfg = Release|Any CPU
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {9D13B739-62B1-4190-B386-7A9547304EB3} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {E9C258D7-0D8E-4E6A-9857-5C6438591755} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- {8B4AE9B6-CDAC-44DD-A5CD-28A470D363B8} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- {9CBE603F-6746-411D-A5FD-CB2C948CD7D0} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {D8DF12D6-FA70-4653-BD8F-C188944836DE} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {9BD7FC3D-6956-42A8-A586-2558C499EBA2} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- {6CB12050-DC9B-4155-BADD-BFDD54CDD70F} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {F7C53EBD-0075-474F-A083-419257D04080} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {A77E5FAF-D775-4AB4-8846-8965C2104E60} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- {63026A19-60B8-4906-81CB-216F30E8094B} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {DA29F063-1828-47D8-B051-800AF7C9A0BE} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {BA7B6F53-D24D-45AC-927A-266857EA8D1E} = {004A2E0F-D34A-44D4-8DF0-D2BC63B57073}
- {B58E12EB-E43D-4D77-894E-5157D2269836} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
- {EB2530FC-69F7-4DCB-A8B3-3671A157ED32} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- {20F2E9D6-A69E-4BAE-9236-574B0AA59139} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {C90AEECD-DB4C-4BE6-B506-16A449852FB8}
- EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = Npgsql.csproj
- EndGlobalSection
-EndGlobal
diff --git a/Npgsql.slnx b/Npgsql.slnx
new file mode 100644
index 0000000000..e69a6728c8
--- /dev/null
+++ b/Npgsql.slnx
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Npgsql.sln.DotSettings b/Npgsql.slnx.DotSettings
similarity index 100%
rename from Npgsql.sln.DotSettings
rename to Npgsql.slnx.DotSettings
diff --git a/global.json b/global.json
index c4fc1c4611..6a288505a1 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "10.0.100",
"rollForward": "latestMajor",
- "allowPrerelease": "false"
+ "allowPrerelease": false
}
}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 169a5988a2..6e8c5bb19f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,15 +2,10 @@
+ true
true
-
- true
-
-
-
-
diff --git a/src/Npgsql.DependencyInjection/Npgsql.DependencyInjection.csproj b/src/Npgsql.DependencyInjection/Npgsql.DependencyInjection.csproj
index 357003cf07..3c10503037 100644
--- a/src/Npgsql.DependencyInjection/Npgsql.DependencyInjection.csproj
+++ b/src/Npgsql.DependencyInjection/Npgsql.DependencyInjection.csproj
@@ -2,10 +2,7 @@
Shay Rojansky
-
-
- net6.0;net8.0
- net8.0
+ net10.0
npgsql;postgresql;postgres;ado;ado.net;database;sql;di;dependency injection
README.md
diff --git a/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.Obsolete.cs b/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.Obsolete.cs
new file mode 100644
index 0000000000..6e2b4e7d4f
--- /dev/null
+++ b/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.Obsolete.cs
@@ -0,0 +1,220 @@
+using System;
+using System.ComponentModel;
+using Npgsql;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static partial class NpgsqlServiceCollectionExtensions
+{
+ ///
+ /// Registers an and an in the .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddNpgsqlDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddNpgsqlDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
+
+ ///
+ /// Registers an and an in the .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddNpgsqlDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ Action dataSourceBuilderAction,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddNpgsqlDataSourceCore(serviceCollection, serviceKey: null, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
+
+ ///
+ /// Registers an and an in the .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddNpgsqlSlimDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddNpgsqlSlimDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
+
+ ///
+ /// Registers an and an in the .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddNpgsqlSlimDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ Action dataSourceBuilderAction,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddNpgsqlSlimDataSourceCore(serviceCollection, serviceKey: null, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
+
+ ///
+ /// Registers an and an in the
+ /// .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddMultiHostNpgsqlDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddMultiHostNpgsqlDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
+
+ ///
+ /// Registers an and an in the
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddMultiHostNpgsqlDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ Action dataSourceBuilderAction,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddMultiHostNpgsqlDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
+
+ ///
+ /// Registers an and an in the
+ /// .
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddMultiHostNpgsqlSlimDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
+
+ ///
+ /// Registers an and an in the
+ ///
+ /// The to add services to.
+ /// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
+ ///
+ /// The lifetime with which to register the in the container.
+ /// Defaults to .
+ ///
+ ///
+ /// The lifetime with which to register the service in the container.
+ /// Defaults to .
+ ///
+ /// The same service collection so that multiple calls can be chained.
+ [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
+ public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
+ this IServiceCollection serviceCollection,
+ string connectionString,
+ Action dataSourceBuilderAction,
+ ServiceLifetime connectionLifetime,
+ ServiceLifetime dataSourceLifetime)
+ => AddMultiHostNpgsqlSlimDataSourceCore(
+ serviceCollection, serviceKey: null, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
+}
diff --git a/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.cs b/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.cs
index 755d6b1357..7e22029a40 100644
--- a/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.cs
+++ b/src/Npgsql.DependencyInjection/NpgsqlServiceCollectionExtensions.cs
@@ -1,5 +1,4 @@
using System;
-using System.ComponentModel;
using System.Data.Common;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
@@ -11,16 +10,13 @@ namespace Microsoft.Extensions.DependencyInjection;
///
/// Extension method for setting up Npgsql services in an .
///
-public static class NpgsqlServiceCollectionExtensions
+public static partial class NpgsqlServiceCollectionExtensions
{
///
/// Registers an and an in the .
///
/// The to add services to.
/// An Npgsql connection string.
- ///
- /// An action to configure the for further customizations of the .
- ///
///
/// The lifetime with which to register the in the container.
/// Defaults to .
@@ -34,11 +30,12 @@ public static class NpgsqlServiceCollectionExtensions
public static IServiceCollection AddNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
- Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
- => AddNpgsqlDataSourceCore(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ => AddNpgsqlDataSourceCore(
+ serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
///
/// Registers an and an in the .
@@ -56,21 +53,27 @@ public static IServiceCollection AddNpgsqlDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
+ /// The of the data source.
/// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
public static IServiceCollection AddNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
Action dataSourceBuilderAction,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddNpgsqlDataSourceCore(serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+ ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
+ object? serviceKey = null)
+ => AddNpgsqlDataSourceCore(serviceCollection, serviceKey, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder)
+ , connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the .
///
/// The to add services to.
/// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
///
/// The lifetime with which to register the in the container.
/// Defaults to .
@@ -84,11 +87,13 @@ public static IServiceCollection AddNpgsqlDataSource(
public static IServiceCollection AddNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
+ Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
- => AddNpgsqlDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ => AddNpgsqlDataSourceCore(serviceCollection, serviceKey, connectionString,
+ static (sp, builder, state) => ((Action)state!)(sp, builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the .
@@ -103,15 +108,17 @@ public static IServiceCollection AddNpgsqlDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
+ /// The of the data source.
/// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddNpgsqlDataSource(
+ public static IServiceCollection AddNpgsqlSlimDataSource(
this IServiceCollection serviceCollection,
string connectionString,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddNpgsqlDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+ ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
+ object? serviceKey = null)
+ => AddNpgsqlSlimDataSourceCore(
+ serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
///
/// Registers an and an in the .
@@ -138,7 +145,9 @@ public static IServiceCollection AddNpgsqlSlimDataSource(
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
- => AddNpgsqlSlimDataSourceCore(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ => AddNpgsqlSlimDataSourceCore(serviceCollection, serviceKey, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the .
@@ -156,71 +165,25 @@ public static IServiceCollection AddNpgsqlSlimDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
- /// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddNpgsqlSlimDataSource(
- this IServiceCollection serviceCollection,
- string connectionString,
- Action dataSourceBuilderAction,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddNpgsqlSlimDataSourceCore(serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
-
- ///
- /// Registers an and an in the .
- ///
- /// The to add services to.
- /// An Npgsql connection string.
- ///
- /// The lifetime with which to register the in the container.
- /// Defaults to .
- ///
- ///
- /// The lifetime with which to register the service in the container.
- /// Defaults to .
- ///
/// The of the data source.
/// The same service collection so that multiple calls can be chained.
public static IServiceCollection AddNpgsqlSlimDataSource(
this IServiceCollection serviceCollection,
string connectionString,
+ Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
- => AddNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
-
- ///
- /// Registers an and an in the .
- ///
- /// The to add services to.
- /// An Npgsql connection string.
- ///
- /// The lifetime with which to register the in the container.
- /// Defaults to .
- ///
- ///
- /// The lifetime with which to register the service in the container.
- /// Defaults to .
- ///
- /// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddNpgsqlSlimDataSource(
- this IServiceCollection serviceCollection,
- string connectionString,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ => AddNpgsqlSlimDataSourceCore(serviceCollection, serviceKey, connectionString,
+ static (sp, builder, state) => ((Action)state!)(sp, builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the
+ /// .
///
/// The to add services to.
/// An Npgsql connection string.
- ///
- /// An action to configure the for further customizations of the .
- ///
///
/// The lifetime with which to register the in the container.
/// Defaults to .
@@ -234,12 +197,12 @@ public static IServiceCollection AddNpgsqlSlimDataSource(
public static IServiceCollection AddMultiHostNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
- Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
=> AddMultiHostNpgsqlDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
///
/// Registers an and an in the
@@ -257,23 +220,28 @@ public static IServiceCollection AddMultiHostNpgsqlDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
+ /// The of the data source.
/// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
public static IServiceCollection AddMultiHostNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
Action dataSourceBuilderAction,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
+ ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+ ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
+ object? serviceKey = null)
=> AddMultiHostNpgsqlDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ serviceCollection, serviceKey, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the
- /// .
///
/// The to add services to.
/// An Npgsql connection string.
+ ///
+ /// An action to configure the for further customizations of the .
+ ///
///
/// The lifetime with which to register the in the container.
/// Defaults to .
@@ -287,11 +255,14 @@ public static IServiceCollection AddMultiHostNpgsqlDataSource(
public static IServiceCollection AddMultiHostNpgsqlDataSource(
this IServiceCollection serviceCollection,
string connectionString,
+ Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
=> AddMultiHostNpgsqlDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ serviceCollection, serviceKey, connectionString,
+ static (sp, builder, state) => ((Action)state!)(sp, builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the
@@ -307,15 +278,17 @@ public static IServiceCollection AddMultiHostNpgsqlDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
+ /// The of the data source.
/// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddMultiHostNpgsqlDataSource(
+ public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
this IServiceCollection serviceCollection,
string connectionString,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddMultiHostNpgsqlDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+ ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
+ object? serviceKey = null)
+ => AddMultiHostNpgsqlSlimDataSourceCore(
+ serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null,
+ connectionLifetime, dataSourceLifetime, state: null);
///
/// Registers an and an in the
@@ -343,7 +316,9 @@ public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
=> AddMultiHostNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+ serviceCollection, serviceKey, connectionString,
+ static (_, builder, state) => ((Action)state!)(builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
///
/// Registers an and an in the
@@ -361,73 +336,28 @@ public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
/// The lifetime with which to register the service in the container.
/// Defaults to .
///
- /// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
- this IServiceCollection serviceCollection,
- string connectionString,
- Action dataSourceBuilderAction,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddMultiHostNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
-
- ///
- /// Registers an and an in the
- /// .
- ///
- /// The to add services to.
- /// An Npgsql connection string.
- ///
- /// The lifetime with which to register the in the container.
- /// Defaults to .
- ///
- ///
- /// The lifetime with which to register the service in the container.
- /// Defaults to .
- ///
/// The of the data source.
/// The same service collection so that multiple calls can be chained.
public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
this IServiceCollection serviceCollection,
string connectionString,
+ Action dataSourceBuilderAction,
ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton,
object? serviceKey = null)
=> AddMultiHostNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
-
- ///
- /// Registers an and an in the
- /// .
- ///
- /// The to add services to.
- /// An Npgsql connection string.
- ///
- /// The lifetime with which to register the in the container.
- /// Defaults to .
- ///
- ///
- /// The lifetime with which to register the service in the container.
- /// Defaults to .
- ///
- /// The same service collection so that multiple calls can be chained.
- [EditorBrowsable(EditorBrowsableState.Never), Obsolete("Defined for binary compatibility with 7.0")]
- public static IServiceCollection AddMultiHostNpgsqlSlimDataSource(
- this IServiceCollection serviceCollection,
- string connectionString,
- ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
- => AddMultiHostNpgsqlSlimDataSourceCore(
- serviceCollection, serviceKey: null, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+ serviceCollection, serviceKey, connectionString,
+ static (sp, builder, state) => ((Action)state!)(sp, builder),
+ connectionLifetime, dataSourceLifetime, state: dataSourceBuilderAction);
static IServiceCollection AddNpgsqlDataSourceCore(
this IServiceCollection serviceCollection,
object? serviceKey,
string connectionString,
- Action? dataSourceBuilderAction,
+ Action? dataSourceBuilderAction,
ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
+ ServiceLifetime dataSourceLifetime,
+ object? state)
{
serviceCollection.TryAdd(
new ServiceDescriptor(
@@ -437,7 +367,7 @@ static IServiceCollection AddNpgsqlDataSourceCore(
{
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.UseLoggerFactory(sp.GetService());
- dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+ dataSourceBuilderAction?.Invoke(sp, dataSourceBuilder, state);
return dataSourceBuilder.Build();
},
dataSourceLifetime));
@@ -451,9 +381,10 @@ static IServiceCollection AddNpgsqlSlimDataSourceCore(
this IServiceCollection serviceCollection,
object? serviceKey,
string connectionString,
- Action? dataSourceBuilderAction,
+ Action? dataSourceBuilderAction,
ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
+ ServiceLifetime dataSourceLifetime,
+ object? state)
{
serviceCollection.TryAdd(
new ServiceDescriptor(
@@ -463,7 +394,7 @@ static IServiceCollection AddNpgsqlSlimDataSourceCore(
{
var dataSourceBuilder = new NpgsqlSlimDataSourceBuilder(connectionString);
dataSourceBuilder.UseLoggerFactory(sp.GetService());
- dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+ dataSourceBuilderAction?.Invoke(sp, dataSourceBuilder, state);
return dataSourceBuilder.Build();
},
dataSourceLifetime));
@@ -477,9 +408,10 @@ static IServiceCollection AddMultiHostNpgsqlDataSourceCore(
this IServiceCollection serviceCollection,
object? serviceKey,
string connectionString,
- Action? dataSourceBuilderAction,
+ Action? dataSourceBuilderAction,
ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
+ ServiceLifetime dataSourceLifetime,
+ object? state)
{
serviceCollection.TryAdd(
new ServiceDescriptor(
@@ -489,7 +421,7 @@ static IServiceCollection AddMultiHostNpgsqlDataSourceCore(
{
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.UseLoggerFactory(sp.GetService());
- dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+ dataSourceBuilderAction?.Invoke(sp, dataSourceBuilder, state);
return dataSourceBuilder.BuildMultiHost();
},
dataSourceLifetime));
@@ -522,9 +454,10 @@ static IServiceCollection AddMultiHostNpgsqlSlimDataSourceCore(
this IServiceCollection serviceCollection,
object? serviceKey,
string connectionString,
- Action? dataSourceBuilderAction,
+ Action? dataSourceBuilderAction,
ServiceLifetime connectionLifetime,
- ServiceLifetime dataSourceLifetime)
+ ServiceLifetime dataSourceLifetime,
+ object? state)
{
serviceCollection.TryAdd(
new ServiceDescriptor(
@@ -534,7 +467,7 @@ static IServiceCollection AddMultiHostNpgsqlSlimDataSourceCore(
{
var dataSourceBuilder = new NpgsqlSlimDataSourceBuilder(connectionString);
dataSourceBuilder.UseLoggerFactory(sp.GetService());
- dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+ dataSourceBuilderAction?.Invoke(sp, dataSourceBuilder, state);
return dataSourceBuilder.BuildMultiHost();
},
dataSourceLifetime));
diff --git a/src/Npgsql.DependencyInjection/PublicAPI.Unshipped.txt b/src/Npgsql.DependencyInjection/PublicAPI.Unshipped.txt
index ab058de62d..34f2d889e9 100644
--- a/src/Npgsql.DependencyInjection/PublicAPI.Unshipped.txt
+++ b/src/Npgsql.DependencyInjection/PublicAPI.Unshipped.txt
@@ -1 +1,5 @@
#nullable enable
+static Microsoft.Extensions.DependencyInjection.NpgsqlServiceCollectionExtensions.AddMultiHostNpgsqlDataSource(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, string! connectionString, System.Action! dataSourceBuilderAction, Microsoft.Extensions.DependencyInjection.ServiceLifetime connectionLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient, Microsoft.Extensions.DependencyInjection.ServiceLifetime dataSourceLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton, object? serviceKey = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+static Microsoft.Extensions.DependencyInjection.NpgsqlServiceCollectionExtensions.AddMultiHostNpgsqlSlimDataSource(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, string! connectionString, System.Action! dataSourceBuilderAction, Microsoft.Extensions.DependencyInjection.ServiceLifetime connectionLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient, Microsoft.Extensions.DependencyInjection.ServiceLifetime dataSourceLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton, object? serviceKey = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+static Microsoft.Extensions.DependencyInjection.NpgsqlServiceCollectionExtensions.AddNpgsqlDataSource(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, string! connectionString, System.Action! dataSourceBuilderAction, Microsoft.Extensions.DependencyInjection.ServiceLifetime connectionLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient, Microsoft.Extensions.DependencyInjection.ServiceLifetime dataSourceLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton, object? serviceKey = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+static Microsoft.Extensions.DependencyInjection.NpgsqlServiceCollectionExtensions.AddNpgsqlSlimDataSource(this Microsoft.Extensions.DependencyInjection.IServiceCollection! serviceCollection, string! connectionString, System.Action! dataSourceBuilderAction, Microsoft.Extensions.DependencyInjection.ServiceLifetime connectionLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Transient, Microsoft.Extensions.DependencyInjection.ServiceLifetime dataSourceLifetime = Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton, object? serviceKey = null) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
diff --git a/src/Npgsql.GeoJSON/CrsMap.WellKnown.cs b/src/Npgsql.GeoJSON/CrsMap.WellKnown.cs
index 14da2f893e..dda11bd1d7 100644
--- a/src/Npgsql.GeoJSON/CrsMap.WellKnown.cs
+++ b/src/Npgsql.GeoJSON/CrsMap.WellKnown.cs
@@ -5,10 +5,10 @@ public partial class CrsMap
///
/// These entries came from spatial_res_sys. They are used to elide memory allocations
/// if they are identical to the entries for the current connection. Otherwise,
- /// memory allocated for overrided entries only (added, removed, or modified).
+ /// memory allocated for overridden entries only (added, removed, or modified).
///
internal static readonly CrsMapEntry[] WellKnown =
- {
+ [
new(2000, 2180, "EPSG"),
new(2188, 2217, "EPSG"),
new(2219, 2220, "EPSG"),
@@ -584,6 +584,6 @@ public partial class CrsMap
new(32601, 32667, "EPSG"),
new(32701, 32761, "EPSG"),
new(32766, 32766, "EPSG"),
- new(900913, 900913, "spatialreferencing.org"),
- };
+ new(900913, 900913, "spatialreferencing.org")
+ ];
}
diff --git a/src/Npgsql.GeoJSON/CrsMap.cs b/src/Npgsql.GeoJSON/CrsMap.cs
index dd556d9b33..602387a911 100644
--- a/src/Npgsql.GeoJSON/CrsMap.cs
+++ b/src/Npgsql.GeoJSON/CrsMap.cs
@@ -6,13 +6,13 @@ namespace Npgsql.GeoJSON;
///
public partial class CrsMap
{
- readonly CrsMapEntry[]? _overriden;
+ readonly CrsMapEntry[]? _overridden;
- internal CrsMap(CrsMapEntry[]? overriden)
- => _overriden = overriden;
+ internal CrsMap(CrsMapEntry[]? overridden)
+ => _overridden = overridden;
internal string? GetAuthority(int srid)
- => GetAuthority(_overriden, srid) ?? GetAuthority(WellKnown, srid);
+ => GetAuthority(_overridden, srid) ?? GetAuthority(WellKnown, srid);
static string? GetAuthority(CrsMapEntry[]? entries, int srid)
{
diff --git a/src/Npgsql.GeoJSON/Internal/BoundingBoxBuilder.cs b/src/Npgsql.GeoJSON/Internal/BoundingBoxBuilder.cs
index 7702a7e0b3..c3ea8f271f 100644
--- a/src/Npgsql.GeoJSON/Internal/BoundingBoxBuilder.cs
+++ b/src/Npgsql.GeoJSON/Internal/BoundingBoxBuilder.cs
@@ -48,6 +48,6 @@ internal void Accumulate(Position position)
internal double[] Build()
=> _hasAltitude
- ? new[] { _minLongitude, _minLatitude, _minAltitude, _maxLongitude, _maxLatitude, _maxAltitude }
- : new[] { _minLongitude, _minLatitude, _maxLongitude, _maxLatitude };
+ ? [_minLongitude, _minLatitude, _minAltitude, _maxLongitude, _maxLatitude, _maxAltitude]
+ : [_minLongitude, _minLatitude, _maxLongitude, _maxLatitude];
}
\ No newline at end of file
diff --git a/src/Npgsql.GeoJSON/Internal/CrsMapBuilder.cs b/src/Npgsql.GeoJSON/Internal/CrsMapBuilder.cs
index 44829761c9..95f45d5db3 100644
--- a/src/Npgsql.GeoJSON/Internal/CrsMapBuilder.cs
+++ b/src/Npgsql.GeoJSON/Internal/CrsMapBuilder.cs
@@ -5,7 +5,7 @@ namespace Npgsql.GeoJSON.Internal;
struct CrsMapBuilder
{
CrsMapEntry[] _overrides;
- int _overridenIndex;
+ int _overriddenIndex;
int _wellKnownIndex;
internal void Add(in CrsMapEntry entry)
@@ -33,21 +33,21 @@ internal void Add(in CrsMapEntry entry)
void AddCore(in CrsMapEntry entry)
{
- var index = _overridenIndex + 1;
+ var index = _overriddenIndex + 1;
if (_overrides == null)
_overrides = new CrsMapEntry[4];
else
if (_overrides.Length == index)
Array.Resize(ref _overrides, _overrides.Length << 1);
- _overrides[_overridenIndex] = entry;
- _overridenIndex = index;
+ _overrides[_overriddenIndex] = entry;
+ _overriddenIndex = index;
}
internal CrsMap Build()
{
- if (_overrides != null && _overrides.Length < _overridenIndex)
- Array.Resize(ref _overrides, _overridenIndex);
+ if (_overrides != null && _overrides.Length < _overriddenIndex)
+ Array.Resize(ref _overrides, _overriddenIndex);
return new CrsMap(_overrides);
}
diff --git a/src/Npgsql.GeoJSON/Internal/GeoJSONConverter.cs b/src/Npgsql.GeoJSON/Internal/GeoJSONConverter.cs
index 755c8acc19..5d54d16194 100644
--- a/src/Npgsql.GeoJSON/Internal/GeoJSONConverter.cs
+++ b/src/Npgsql.GeoJSON/Internal/GeoJSONConverter.cs
@@ -278,16 +278,9 @@ static Position ReadPosition(PgReader reader, EwkbGeometryType type, bool little
return position;
double ReadDouble(bool littleEndian)
- {
- if (littleEndian)
- {
- var doubleValue = reader.ReadDouble();
- var value = BinaryPrimitives.ReverseEndianness(Unsafe.As(ref doubleValue));
- return Unsafe.As(ref value);
- }
-
- return reader.ReadDouble();
- }
+ => littleEndian
+ ? BitConverter.Int64BitsToDouble(BinaryPrimitives.ReverseEndianness(BitConverter.DoubleToInt64Bits(reader.ReadDouble())))
+ : reader.ReadDouble();
}
}
@@ -330,7 +323,7 @@ static Size GetSize(LineString value)
{
var coordinates = value.Coordinates;
if (NotValid(coordinates, out var hasZ))
- throw AllOrNoneCoordiantesMustHaveZ(nameof(LineString));
+ throw AllOrNoneCoordinatesMustHaveZ(nameof(LineString));
var length = Size.Create(SizeOfHeaderWithLength + coordinates.Count * SizeOfPoint(hasZ));
if (GetSrid(value.CRS) != 0)
@@ -351,12 +344,12 @@ static Size GetSize(Polygon value)
{
var coordinates = lines[i].Coordinates;
if (NotValid(coordinates, out var lineHasZ))
- throw AllOrNoneCoordiantesMustHaveZ(nameof(Polygon));
+ throw AllOrNoneCoordinatesMustHaveZ(nameof(Polygon));
if (hasZ != lineHasZ)
{
if (i == 0) hasZ = lineHasZ;
- else throw AllOrNoneCoordiantesMustHaveZ(nameof(LineString));
+ else throw AllOrNoneCoordinatesMustHaveZ(nameof(LineString));
}
length = length.Combine(coordinates.Count * SizeOfPoint(hasZ));
@@ -685,7 +678,7 @@ static int SizeOfPoint(EwkbGeometryType type)
static Exception UnknownPostGisType()
=> throw new InvalidOperationException("Invalid PostGIS type");
- static Exception AllOrNoneCoordiantesMustHaveZ(string typeName)
+ static Exception AllOrNoneCoordinatesMustHaveZ(string typeName)
=> new ArgumentException($"The Z coordinate must be specified for all or none elements of {typeName}");
static int GetSrid(ICRSObject crs)
diff --git a/src/Npgsql.GeoJSON/Internal/GeoJSONTypeInfoResolverFactory.cs b/src/Npgsql.GeoJSON/Internal/GeoJSONTypeInfoResolverFactory.cs
index c25118f1d7..f1b56000f2 100644
--- a/src/Npgsql.GeoJSON/Internal/GeoJSONTypeInfoResolverFactory.cs
+++ b/src/Npgsql.GeoJSON/Internal/GeoJSONTypeInfoResolverFactory.cs
@@ -6,37 +6,17 @@
namespace Npgsql.GeoJSON.Internal;
-sealed class GeoJSONTypeInfoResolverFactory : PgTypeInfoResolverFactory
+sealed class GeoJSONTypeInfoResolverFactory(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
+ : PgTypeInfoResolverFactory
{
- readonly GeoJSONOptions _options;
- readonly bool _geographyAsDefault;
- readonly CrsMap? _crsMap;
+ public override IPgTypeInfoResolver CreateResolver() => new Resolver(options, geographyAsDefault, crsMap);
+ public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(options, geographyAsDefault, crsMap);
- public GeoJSONTypeInfoResolverFactory(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
+ class Resolver(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
+ : IPgTypeInfoResolver
{
- _options = options;
- _geographyAsDefault = geographyAsDefault;
- _crsMap = crsMap;
- }
-
- public override IPgTypeInfoResolver CreateResolver() => new Resolver(_options, _geographyAsDefault, _crsMap);
- public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(_options, _geographyAsDefault, _crsMap);
-
- class Resolver : IPgTypeInfoResolver
- {
- readonly GeoJSONOptions _options;
- readonly bool _geographyAsDefault;
- readonly CrsMap? _crsMap;
-
TypeInfoMappingCollection? _mappings;
- protected TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(), _options, _geographyAsDefault, _crsMap);
-
- public Resolver(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
- {
- _options = options;
- _geographyAsDefault = geographyAsDefault;
- _crsMap = crsMap;
- }
+ protected TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(), options, geographyAsDefault, crsMap);
public PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
=> Mappings.Find(type, dataTypeName, options);
@@ -83,16 +63,12 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings,
}
}
- sealed class ArrayResolver : Resolver, IPgTypeInfoResolver
+ sealed class ArrayResolver(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
+ : Resolver(options, geographyAsDefault, crsMap), IPgTypeInfoResolver
{
TypeInfoMappingCollection? _mappings;
new TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(base.Mappings));
- public ArrayResolver(GeoJSONOptions options, bool geographyAsDefault, CrsMap? crsMap = null)
- : base(options, geographyAsDefault, crsMap)
- {
- }
-
public new PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
=> Mappings.Find(type, dataTypeName, options);
diff --git a/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj b/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj
index a802ca5653..824c5b79e6 100644
--- a/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj
+++ b/src/Npgsql.GeoJSON/Npgsql.GeoJSON.csproj
@@ -3,8 +3,8 @@
Yoh Deadfall;Shay Rojansky
GeoJSON plugin for Npgsql, allowing mapping of PostGIS geometry types to GeoJSON types.
npgsql;postgresql;postgres;postgis;geojson;spatial;ado;ado.net;database;sql
- net6.0
- net8.0
+ net10.0
+ $(NoWarn);NPG9001
diff --git a/src/Npgsql.GeoJSON/NpgsqlGeoJSONExtensions.cs b/src/Npgsql.GeoJSON/NpgsqlGeoJSONExtensions.cs
index b47a9b211f..9651004a86 100644
--- a/src/Npgsql.GeoJSON/NpgsqlGeoJSONExtensions.cs
+++ b/src/Npgsql.GeoJSON/NpgsqlGeoJSONExtensions.cs
@@ -10,6 +10,7 @@ namespace Npgsql;
///
public static class NpgsqlGeoJSONExtensions
{
+ // Note: defined for binary compatibility and NpgsqlConnection.GlobalTypeMapper.
///
/// Sets up GeoJSON mappings for the PostGIS types.
///
@@ -22,6 +23,7 @@ public static INpgsqlTypeMapper UseGeoJson(this INpgsqlTypeMapper mapper, GeoJSO
return mapper;
}
+ // Note: defined for binary compatibility and NpgsqlConnection.GlobalTypeMapper.
///
/// Sets up GeoJSON mappings for the PostGIS types.
///
@@ -34,4 +36,31 @@ public static INpgsqlTypeMapper UseGeoJson(this INpgsqlTypeMapper mapper, CrsMap
mapper.AddTypeInfoResolverFactory(new GeoJSONTypeInfoResolverFactory(options, geographyAsDefault, crsMap));
return mapper;
}
+
+ ///
+ /// Sets up GeoJSON mappings for the PostGIS types.
+ ///
+ /// The type mapper to set up (global or connection-specific)
+ /// Options to use when constructing objects.
+ /// Specifies that the geography type is used for mapping by default.
+ public static TMapper UseGeoJson(this TMapper mapper, GeoJSONOptions options = GeoJSONOptions.None, bool geographyAsDefault = false)
+ where TMapper : INpgsqlTypeMapper
+ {
+ mapper.AddTypeInfoResolverFactory(new GeoJSONTypeInfoResolverFactory(options, geographyAsDefault, crsMap: null));
+ return mapper;
+ }
+
+ ///
+ /// Sets up GeoJSON mappings for the PostGIS types.
+ ///
+ /// The type mapper to set up (global or connection-specific)
+ /// A custom crs map that might contain more or less entries than the default well-known crs map.
+ /// Options to use when constructing objects.
+ /// Specifies that the geography type is used for mapping by default.
+ public static TMapper UseGeoJson(this TMapper mapper, CrsMap crsMap, GeoJSONOptions options = GeoJSONOptions.None, bool geographyAsDefault = false)
+ where TMapper : INpgsqlTypeMapper
+ {
+ mapper.AddTypeInfoResolverFactory(new GeoJSONTypeInfoResolverFactory(options, geographyAsDefault, crsMap));
+ return mapper;
+ }
}
diff --git a/src/Npgsql.GeoJSON/PublicAPI.Unshipped.txt b/src/Npgsql.GeoJSON/PublicAPI.Unshipped.txt
index ab058de62d..34de07f0d7 100644
--- a/src/Npgsql.GeoJSON/PublicAPI.Unshipped.txt
+++ b/src/Npgsql.GeoJSON/PublicAPI.Unshipped.txt
@@ -1 +1,3 @@
#nullable enable
+static Npgsql.NpgsqlGeoJSONExtensions.UseGeoJson(this TMapper mapper, Npgsql.GeoJSON.CrsMap! crsMap, Npgsql.GeoJSONOptions options = Npgsql.GeoJSONOptions.None, bool geographyAsDefault = false) -> TMapper
+static Npgsql.NpgsqlGeoJSONExtensions.UseGeoJson(this TMapper mapper, Npgsql.GeoJSONOptions options = Npgsql.GeoJSONOptions.None, bool geographyAsDefault = false) -> TMapper
diff --git a/src/Npgsql.Json.NET/Internal/JsonNetJsonConverter.cs b/src/Npgsql.Json.NET/Internal/JsonNetJsonConverter.cs
index 42b7c88e0d..10126d25f9 100644
--- a/src/Npgsql.Json.NET/Internal/JsonNetJsonConverter.cs
+++ b/src/Npgsql.Json.NET/Internal/JsonNetJsonConverter.cs
@@ -10,35 +10,24 @@
namespace Npgsql.Json.NET.Internal;
-sealed class JsonNetJsonConverter : PgStreamingConverter
+sealed class JsonNetJsonConverter(bool jsonb, Encoding textEncoding, JsonSerializerSettings settings) : PgStreamingConverter
{
- readonly bool _jsonb;
- readonly Encoding _textEncoding;
- readonly JsonSerializerSettings _settings;
-
- public JsonNetJsonConverter(bool jsonb, Encoding textEncoding, JsonSerializerSettings settings)
- {
- _jsonb = jsonb;
- _textEncoding = textEncoding;
- _settings = settings;
- }
-
public override T? Read(PgReader reader)
- => (T?)JsonNetJsonConverter.Read(async: false, _jsonb, reader, typeof(T), _settings, _textEncoding, CancellationToken.None).GetAwaiter().GetResult();
+ => (T?)JsonNetJsonConverter.Read(async: false, jsonb, reader, typeof(T), settings, textEncoding, CancellationToken.None).GetAwaiter().GetResult();
public override async ValueTask ReadAsync(PgReader reader, CancellationToken cancellationToken = default)
- => (T?)await JsonNetJsonConverter.Read(async: true, _jsonb, reader, typeof(T), _settings, _textEncoding, cancellationToken).ConfigureAwait(false);
+ => (T?)await JsonNetJsonConverter.Read(async: true, jsonb, reader, typeof(T), settings, textEncoding, cancellationToken).ConfigureAwait(false);
public override Size GetSize(SizeContext context, T? value, ref object? writeState)
- => JsonNetJsonConverter.GetSize(_jsonb, context, typeof(T), _settings, _textEncoding, value, ref writeState);
+ => JsonNetJsonConverter.GetSize(jsonb, context, typeof(T), settings, textEncoding, value, ref writeState);
public override void Write(PgWriter writer, T? value)
- => JsonNetJsonConverter.Write(_jsonb, async: false, writer, CancellationToken.None).GetAwaiter().GetResult();
+ => JsonNetJsonConverter.Write(jsonb, async: false, writer, CancellationToken.None).GetAwaiter().GetResult();
public override ValueTask WriteAsync(PgWriter writer, T? value, CancellationToken cancellationToken = default)
- => JsonNetJsonConverter.Write(_jsonb, async: true, writer, cancellationToken);
+ => JsonNetJsonConverter.Write(jsonb, async: true, writer, cancellationToken);
}
-// Split out to avoid unneccesary code duplication.
+// Split out to avoid unnecessary code duplication.
static class JsonNetJsonConverter
{
public const byte JsonbProtocolVersion = 1;
@@ -62,7 +51,7 @@ static class JsonNetJsonConverter
using var stream = reader.GetStream();
var mem = new MemoryStream();
if (async)
- await stream.CopyToAsync(mem, Math.Min((int)mem.Length, 81920), cancellationToken).ConfigureAwait(false);
+ await stream.CopyToAsync(mem, Math.Min((int)stream.Length, 81920), cancellationToken).ConfigureAwait(false);
else
stream.CopyTo(mem);
mem.Position = 0;
diff --git a/src/Npgsql.Json.NET/Internal/JsonNetPocoTypeInfoResolverFactory.cs b/src/Npgsql.Json.NET/Internal/JsonNetPocoTypeInfoResolverFactory.cs
index 27f719deca..c038f17aab 100644
--- a/src/Npgsql.Json.NET/Internal/JsonNetPocoTypeInfoResolverFactory.cs
+++ b/src/Npgsql.Json.NET/Internal/JsonNetPocoTypeInfoResolverFactory.cs
@@ -9,43 +9,29 @@ namespace Npgsql.Json.NET.Internal;
[RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")]
[RequiresDynamicCode("Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")]
-sealed class JsonNetPocoTypeInfoResolverFactory : PgTypeInfoResolverFactory
+sealed class JsonNetPocoTypeInfoResolverFactory(
+ Type[]? jsonbClrTypes = null,
+ Type[]? jsonClrTypes = null,
+ JsonSerializerSettings? serializerSettings = null)
+ : PgTypeInfoResolverFactory
{
- readonly Type[]? _jsonbClrTypes;
- readonly Type[]? _jsonClrTypes;
- readonly JsonSerializerSettings? _serializerSettings;
-
- public JsonNetPocoTypeInfoResolverFactory(Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null, JsonSerializerSettings? serializerSettings = null)
- {
- _jsonbClrTypes = jsonbClrTypes;
- _jsonClrTypes = jsonClrTypes;
- _serializerSettings = serializerSettings;
- }
-
- public override IPgTypeInfoResolver CreateResolver() => new Resolver(_jsonbClrTypes, _jsonClrTypes, _serializerSettings);
- public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(_jsonbClrTypes, _jsonClrTypes, _serializerSettings);
+ public override IPgTypeInfoResolver CreateResolver() => new Resolver(jsonbClrTypes, jsonClrTypes, serializerSettings);
+ public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(jsonbClrTypes, jsonClrTypes, serializerSettings);
[RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")]
[RequiresDynamicCode("Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")]
- class Resolver : DynamicTypeInfoResolver, IPgTypeInfoResolver
+ class Resolver(Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null, JsonSerializerSettings? serializerSettings = null)
+ : DynamicTypeInfoResolver, IPgTypeInfoResolver
{
- readonly Type[]? _jsonbClrTypes;
- readonly Type[]? _jsonClrTypes;
- readonly JsonSerializerSettings _serializerSettings;
+ readonly JsonSerializerSettings _serializerSettings = serializerSettings ?? JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
TypeInfoMappingCollection? _mappings;
- protected TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(), _jsonbClrTypes ?? Array.Empty(), _jsonClrTypes ?? Array.Empty(), _serializerSettings);
+ protected TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(), jsonbClrTypes ?? [], jsonClrTypes ?? [], _serializerSettings);
const string JsonDataTypeName = "pg_catalog.json";
const string JsonbDataTypeName = "pg_catalog.jsonb";
- public Resolver(Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null, JsonSerializerSettings? serializerSettings = null)
- {
- _jsonbClrTypes = jsonbClrTypes;
- _jsonClrTypes = jsonClrTypes;
- // Capture default settings during construction.
- _serializerSettings = serializerSettings ?? JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
- }
+ // Capture default settings during construction.
TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings, Type[] jsonbClrTypes, Type[] jsonClrTypes, JsonSerializerSettings serializerSettings)
{
@@ -96,16 +82,12 @@ static PgConverter CreateConverter(Type valueType, bool jsonb, Encoding textEnco
[RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")]
[RequiresDynamicCode("Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")]
- sealed class ArrayResolver : Resolver, IPgTypeInfoResolver
+ sealed class ArrayResolver(Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null, JsonSerializerSettings? serializerSettings = null)
+ : Resolver(jsonbClrTypes, jsonClrTypes, serializerSettings), IPgTypeInfoResolver
{
TypeInfoMappingCollection? _mappings;
new TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(base.Mappings), base.Mappings);
- public ArrayResolver(Type[]? jsonbClrTypes = null, Type[]? jsonClrTypes = null, JsonSerializerSettings? serializerSettings = null)
- : base(jsonbClrTypes, jsonClrTypes, serializerSettings)
- {
- }
-
public new PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
=> Mappings.Find(type, dataTypeName, options) ?? base.GetTypeInfo(type, dataTypeName, options);
diff --git a/src/Npgsql.Json.NET/Internal/JsonNetTypeInfoResolverFactory.cs b/src/Npgsql.Json.NET/Internal/JsonNetTypeInfoResolverFactory.cs
index 1f07bf0252..be2e0a3ba7 100644
--- a/src/Npgsql.Json.NET/Internal/JsonNetTypeInfoResolverFactory.cs
+++ b/src/Npgsql.Json.NET/Internal/JsonNetTypeInfoResolverFactory.cs
@@ -6,26 +6,18 @@
namespace Npgsql.Json.NET.Internal;
-sealed class JsonNetTypeInfoResolverFactory : PgTypeInfoResolverFactory
+sealed class JsonNetTypeInfoResolverFactory(JsonSerializerSettings? settings = null) : PgTypeInfoResolverFactory
{
- readonly JsonSerializerSettings? _settings;
+ public override IPgTypeInfoResolver CreateResolver() => new Resolver(settings);
+ public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(settings);
- public JsonNetTypeInfoResolverFactory(JsonSerializerSettings? settings = null) => _settings = settings;
-
- public override IPgTypeInfoResolver CreateResolver() => new Resolver(_settings);
- public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(_settings);
-
- class Resolver : IPgTypeInfoResolver
+ class Resolver(JsonSerializerSettings? settings = null) : IPgTypeInfoResolver
{
TypeInfoMappingCollection? _mappings;
- readonly JsonSerializerSettings _serializerSettings;
+ readonly JsonSerializerSettings _serializerSettings = settings ?? JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
protected TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(), _serializerSettings);
- public Resolver(JsonSerializerSettings? settings = null)
- {
- // Capture default settings during construction.
- _serializerSettings = settings ?? JsonConvert.DefaultSettings?.Invoke() ?? new JsonSerializerSettings();
- }
+ // Capture default settings during construction.
static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings, JsonSerializerSettings settings)
{
@@ -34,7 +26,7 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings,
{
var jsonb = dataTypeName == "jsonb";
mappings.AddType(dataTypeName, (options, mapping, _) =>
- mapping.CreateInfo(options, new JsonNetJsonConverter(jsonb, options.TextEncoding, settings)), isDefault: true);
+ mapping.CreateInfo(options, new JsonNetJsonConverter(jsonb, options.TextEncoding, settings)));
mappings.AddType(dataTypeName, (options, mapping, _) =>
mapping.CreateInfo(options, new JsonNetJsonConverter(jsonb, options.TextEncoding, settings)));
mappings.AddType(dataTypeName, (options, mapping, _) =>
@@ -50,13 +42,11 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings,
=> Mappings.Find(type, dataTypeName, options);
}
- sealed class ArrayResolver : Resolver, IPgTypeInfoResolver
+ sealed class ArrayResolver(JsonSerializerSettings? settings = null) : Resolver(settings), IPgTypeInfoResolver
{
TypeInfoMappingCollection? _mappings;
new TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(base.Mappings));
- public ArrayResolver(JsonSerializerSettings? settings = null) : base(settings) {}
-
public new PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
=> Mappings.Find(type, dataTypeName, options);
diff --git a/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj b/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj
index 49707eb02f..df6b2ccef6 100644
--- a/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj
+++ b/src/Npgsql.Json.NET/Npgsql.Json.NET.csproj
@@ -3,9 +3,10 @@
Shay Rojansky
Json.NET plugin for Npgsql, allowing transparent serialization/deserialization of JSON objects directly to and from the database.
npgsql;postgresql;json;postgres;ado;ado.net;database;sql
- net6.0
- net8.0
+ net10.0
enable
+ false
+ $(NoWarn);NPG9001
diff --git a/src/Npgsql.Json.NET/NpgsqlJsonNetExtensions.cs b/src/Npgsql.Json.NET/NpgsqlJsonNetExtensions.cs
index f2b33933b8..89c8d21603 100644
--- a/src/Npgsql.Json.NET/NpgsqlJsonNetExtensions.cs
+++ b/src/Npgsql.Json.NET/NpgsqlJsonNetExtensions.cs
@@ -13,6 +13,7 @@ namespace Npgsql;
///
public static class NpgsqlJsonNetExtensions
{
+ // Note: defined for binary compatibility and NpgsqlConnection.GlobalTypeMapper.
///
/// Sets up JSON.NET mappings for the PostgreSQL json and jsonb types.
///
@@ -37,4 +38,30 @@ public static INpgsqlTypeMapper UseJsonNet(
mapper.AddTypeInfoResolverFactory(new JsonNetTypeInfoResolverFactory(settings));
return mapper;
}
+
+ ///
+ /// Sets up JSON.NET mappings for the PostgreSQL json and jsonb types.
+ ///
+ /// The type mapper to set up.
+ /// Optional settings to customize JSON serialization.
+ ///
+ /// A list of CLR types to map to PostgreSQL jsonb (no need to specify ).
+ ///
+ ///
+ /// A list of CLR types to map to PostgreSQL json (no need to specify ).
+ ///
+ [RequiresUnreferencedCode("Json serializer may perform reflection on trimmed types.")]
+ [RequiresDynamicCode("Serializing arbitrary types to json can require creating new generic types or methods, which requires creating code at runtime. This may not work when AOT compiling.")]
+ public static TMapper UseJsonNet(
+ this TMapper mapper,
+ JsonSerializerSettings? settings = null,
+ Type[]? jsonbClrTypes = null,
+ Type[]? jsonClrTypes = null)
+ where TMapper : INpgsqlTypeMapper
+ {
+ // Reverse order
+ mapper.AddTypeInfoResolverFactory(new JsonNetPocoTypeInfoResolverFactory(jsonbClrTypes, jsonClrTypes, settings));
+ mapper.AddTypeInfoResolverFactory(new JsonNetTypeInfoResolverFactory(settings));
+ return mapper;
+ }
}
diff --git a/src/Npgsql.Json.NET/PublicAPI.Unshipped.txt b/src/Npgsql.Json.NET/PublicAPI.Unshipped.txt
index ab058de62d..f4557570e1 100644
--- a/src/Npgsql.Json.NET/PublicAPI.Unshipped.txt
+++ b/src/Npgsql.Json.NET/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
#nullable enable
+static Npgsql.NpgsqlJsonNetExtensions.UseJsonNet(this TMapper mapper, Newtonsoft.Json.JsonSerializerSettings? settings = null, System.Type![]? jsonbClrTypes = null, System.Type![]? jsonClrTypes = null) -> TMapper
diff --git a/src/Npgsql.NetTopologySuite/Internal/NetTopologySuiteTypeInfoResolverFactory.cs b/src/Npgsql.NetTopologySuite/Internal/NetTopologySuiteTypeInfoResolverFactory.cs
index b9a559c12f..e533d62207 100644
--- a/src/Npgsql.NetTopologySuite/Internal/NetTopologySuiteTypeInfoResolverFactory.cs
+++ b/src/Npgsql.NetTopologySuite/Internal/NetTopologySuiteTypeInfoResolverFactory.cs
@@ -7,24 +7,15 @@
namespace Npgsql.NetTopologySuite.Internal;
-sealed class NetTopologySuiteTypeInfoResolverFactory : PgTypeInfoResolverFactory
+sealed class NetTopologySuiteTypeInfoResolverFactory(
+ CoordinateSequenceFactory? coordinateSequenceFactory,
+ PrecisionModel? precisionModel,
+ Ordinates handleOrdinates,
+ bool geographyAsDefault)
+ : PgTypeInfoResolverFactory
{
- readonly CoordinateSequenceFactory? _coordinateSequenceFactory;
- readonly PrecisionModel? _precisionModel;
- readonly Ordinates _handleOrdinates;
- readonly bool _geographyAsDefault;
-
- public NetTopologySuiteTypeInfoResolverFactory(CoordinateSequenceFactory? coordinateSequenceFactory, PrecisionModel? precisionModel,
- Ordinates handleOrdinates, bool geographyAsDefault)
- {
- _coordinateSequenceFactory = coordinateSequenceFactory;
- _precisionModel = precisionModel;
- _handleOrdinates = handleOrdinates;
- _geographyAsDefault = geographyAsDefault;
- }
-
- public override IPgTypeInfoResolver CreateResolver() => new Resolver(_coordinateSequenceFactory, _precisionModel, _handleOrdinates, _geographyAsDefault);
- public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(_coordinateSequenceFactory, _precisionModel, _handleOrdinates, _geographyAsDefault);
+ public override IPgTypeInfoResolver CreateResolver() => new Resolver(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault);
+ public override IPgTypeInfoResolver? CreateArrayResolver() => new ArrayResolver(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault);
class Resolver : IPgTypeInfoResolver
{
@@ -54,7 +45,7 @@ public Resolver(
static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings, PostGisReader reader, PostGisWriter writer,
bool geographyAsDefault)
{
- foreach (var dataTypeName in geographyAsDefault ? new[] {"geography", "geometry"} : new[] { "geometry", "geography" })
+ foreach (var dataTypeName in geographyAsDefault ? ["geography", "geometry"] : new[] { "geometry", "geography" })
{
mappings.AddType(dataTypeName,
(options, mapping, _) => mapping.CreateInfo(options, new NetTopologySuiteConverter(reader, writer)),
@@ -82,23 +73,22 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings,
}
}
- sealed class ArrayResolver : Resolver, IPgTypeInfoResolver
+ sealed class ArrayResolver(
+ CoordinateSequenceFactory? coordinateSequenceFactory,
+ PrecisionModel? precisionModel,
+ Ordinates handleOrdinates,
+ bool geographyAsDefault)
+ : Resolver(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault), IPgTypeInfoResolver
{
TypeInfoMappingCollection? _mappings;
new TypeInfoMappingCollection Mappings => _mappings ??= AddMappings(new(base.Mappings), _geographyAsDefault);
- public ArrayResolver(CoordinateSequenceFactory? coordinateSequenceFactory, PrecisionModel? precisionModel,
- Ordinates handleOrdinates, bool geographyAsDefault)
- : base(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault)
- {
- }
-
public new PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
=> Mappings.Find(type, dataTypeName, options);
static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings, bool geographyAsDefault)
{
- foreach (var dataTypeName in geographyAsDefault ? new[] { "geography", "geometry" } : new[] { "geometry", "geography" })
+ foreach (var dataTypeName in geographyAsDefault ? ["geography", "geometry"] : new[] { "geometry", "geography" })
{
mappings.AddArrayType(dataTypeName);
mappings.AddArrayType(dataTypeName);
diff --git a/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj b/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj
index fd2342614b..91a4c268a0 100644
--- a/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj
+++ b/src/Npgsql.NetTopologySuite/Npgsql.NetTopologySuite.csproj
@@ -4,9 +4,9 @@
NetTopologySuite plugin for Npgsql, allowing mapping of PostGIS geometry types to NetTopologySuite types.
npgsql;postgresql;postgres;postgis;spatial;nettopologysuite;nts;ado;ado.net;database;sql
README.md
- net6.0
- net8.0
+ net10.0
$(NoWarn);NU5104
+ $(NoWarn);NPG9001
diff --git a/src/Npgsql.NetTopologySuite/NpgsqlNetTopologySuiteExtensions.cs b/src/Npgsql.NetTopologySuite/NpgsqlNetTopologySuiteExtensions.cs
index a30d023891..76afcf886c 100644
--- a/src/Npgsql.NetTopologySuite/NpgsqlNetTopologySuiteExtensions.cs
+++ b/src/Npgsql.NetTopologySuite/NpgsqlNetTopologySuiteExtensions.cs
@@ -10,6 +10,7 @@ namespace Npgsql;
///
public static class NpgsqlNetTopologySuiteExtensions
{
+ // Note: defined for binary compatibility and NpgsqlConnection.GlobalTypeMapper.
///
/// Sets up NetTopologySuite mappings for the PostGIS types.
///
@@ -30,4 +31,26 @@ public static INpgsqlTypeMapper UseNetTopologySuite(
mapper.AddTypeInfoResolverFactory(new NetTopologySuiteTypeInfoResolverFactory(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault));
return mapper;
}
+
+ ///
+ /// Sets up NetTopologySuite mappings for the PostGIS types.
+ ///
+ /// The type mapper to set up (global or connection-specific).
+ /// The factory which knows how to build a particular implementation of ICoordinateSequence from an array of Coordinates.
+ /// Specifies the grid of allowable points.
+ /// Specifies the ordinates which will be handled. Not specified ordinates will be ignored.
+ /// If is specified, an actual value will be taken from
+ /// the property of .
+ /// Specifies that the geography type is used for mapping by default.
+ public static TMapper UseNetTopologySuite(
+ this TMapper mapper,
+ CoordinateSequenceFactory? coordinateSequenceFactory = null,
+ PrecisionModel? precisionModel = null,
+ Ordinates handleOrdinates = Ordinates.None,
+ bool geographyAsDefault = false)
+ where TMapper : INpgsqlTypeMapper
+ {
+ mapper.AddTypeInfoResolverFactory(new NetTopologySuiteTypeInfoResolverFactory(coordinateSequenceFactory, precisionModel, handleOrdinates, geographyAsDefault));
+ return mapper;
+ }
}
diff --git a/src/Npgsql.NetTopologySuite/PublicAPI.Unshipped.txt b/src/Npgsql.NetTopologySuite/PublicAPI.Unshipped.txt
index ab058de62d..ab78bca1af 100644
--- a/src/Npgsql.NetTopologySuite/PublicAPI.Unshipped.txt
+++ b/src/Npgsql.NetTopologySuite/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
#nullable enable
+static Npgsql.NpgsqlNetTopologySuiteExtensions.UseNetTopologySuite(this TMapper mapper, NetTopologySuite.Geometries.CoordinateSequenceFactory? coordinateSequenceFactory = null, NetTopologySuite.Geometries.PrecisionModel? precisionModel = null, NetTopologySuite.Geometries.Ordinates handleOrdinates = NetTopologySuite.Geometries.Ordinates.None, bool geographyAsDefault = false) -> TMapper
diff --git a/src/Npgsql.NodaTime/Internal/DateIntervalConverter.cs b/src/Npgsql.NodaTime/Internal/DateIntervalConverter.cs
index 5e25d8bfcc..1bf2d027df 100644
--- a/src/Npgsql.NodaTime/Internal/DateIntervalConverter.cs
+++ b/src/Npgsql.NodaTime/Internal/DateIntervalConverter.cs
@@ -6,17 +6,9 @@
namespace Npgsql.NodaTime.Internal;
-public class DateIntervalConverter : PgStreamingConverter
+public class DateIntervalConverter(PgConverter> rangeConverter, bool dateTimeInfinityConversions)
+ : PgStreamingConverter
{
- readonly bool _dateTimeInfinityConversions;
- readonly PgConverter> _rangeConverter;
-
- public DateIntervalConverter(PgConverter> rangeConverter, bool dateTimeInfinityConversions)
- {
- _rangeConverter = rangeConverter;
- _dateTimeInfinityConversions = dateTimeInfinityConversions;
- }
-
public override DateInterval Read(PgReader reader)
=> Read(async: false, reader, CancellationToken.None).GetAwaiter().GetResult();
@@ -26,24 +18,24 @@ public override ValueTask ReadAsync(PgReader reader, CancellationT
async ValueTask Read(bool async, PgReader reader, CancellationToken cancellationToken)
{
var range = async
- ? await _rangeConverter.ReadAsync(reader, cancellationToken).ConfigureAwait(false)
+ ? await rangeConverter.ReadAsync(reader, cancellationToken).ConfigureAwait(false)
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
- : _rangeConverter.Read(reader);
+ : rangeConverter.Read(reader);
var upperBound = range.UpperBound;
- if (upperBound != LocalDate.MaxIsoValue || !_dateTimeInfinityConversions)
+ if (upperBound != LocalDate.MaxIsoValue || !dateTimeInfinityConversions)
upperBound -= Period.FromDays(1);
return new(range.LowerBound, upperBound);
}
public override Size GetSize(SizeContext context, DateInterval value, ref object? writeState)
- => _rangeConverter.GetSize(context, new NpgsqlRange(value.Start, value.End), ref writeState);
+ => rangeConverter.GetSize(context, new NpgsqlRange(value.Start, value.End), ref writeState);
public override void Write(PgWriter writer, DateInterval value)
- => _rangeConverter.Write(writer, new NpgsqlRange(value.Start, value.End));
+ => rangeConverter.Write(writer, new NpgsqlRange(value.Start, value.End));
public override ValueTask WriteAsync(PgWriter writer, DateInterval value, CancellationToken cancellationToken = default)
- => _rangeConverter.WriteAsync(writer, new NpgsqlRange(value.Start, value.End), cancellationToken);
+ => rangeConverter.WriteAsync(writer, new NpgsqlRange(value.Start, value.End), cancellationToken);
}
diff --git a/src/Npgsql.NodaTime/Internal/IntervalConverter.cs b/src/Npgsql.NodaTime/Internal/IntervalConverter.cs
index 3ca9ca9ab0..f062079a4a 100644
--- a/src/Npgsql.NodaTime/Internal/IntervalConverter.cs
+++ b/src/Npgsql.NodaTime/Internal/IntervalConverter.cs
@@ -6,13 +6,8 @@
namespace Npgsql.NodaTime.Internal;
-public class IntervalConverter : PgStreamingConverter
+sealed class IntervalConverter(PgConverter> rangeConverter, bool dateTimeInfinityConversions) : PgStreamingConverter
{
- readonly PgConverter> _rangeConverter;
-
- public IntervalConverter(PgConverter> rangeConverter)
- => _rangeConverter = rangeConverter;
-
public override Interval Read(PgReader reader)
=> Read(async: false, reader, CancellationToken.None).GetAwaiter().GetResult();
@@ -22,9 +17,9 @@ public override ValueTask ReadAsync(PgReader reader, CancellationToken
async ValueTask Read(bool async, PgReader reader, CancellationToken cancellationToken)
{
var range = async
- ? await _rangeConverter.ReadAsync(reader, cancellationToken).ConfigureAwait(false)
+ ? await rangeConverter.ReadAsync(reader, cancellationToken).ConfigureAwait(false)
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
- : _rangeConverter.Read(reader);
+ : rangeConverter.Read(reader);
// NodaTime Interval includes the start instant and excludes the end instant.
Instant? start = range.LowerBoundInfinite
@@ -32,7 +27,12 @@ async ValueTask Read(bool async, PgReader reader, CancellationToken ca
: range.LowerBoundIsInclusive
? range.LowerBound
: range.LowerBound + Duration.Epsilon;
- Instant? end = range.UpperBoundInfinite
+ // For ranges with element types with infinity values (datetime, date etc.) an
+ // inclusive lower/upper bound causes their -/+ infinity (respectively) to fall within the range.
+ // If those values are returned for such a range postgres will not mark the affected bound as infinite accordingly.
+ // This is documented in https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-INFINITE
+ // As NodaTime uses an exclusive upper bound we must consider this case as being another form of infinity (null).
+ Instant? end = range.UpperBoundInfinite || (dateTimeInfinityConversions && range.UpperBoundIsInclusive && range.UpperBound == Instant.MaxValue)
? null
: range.UpperBoundIsInclusive
? range.UpperBound + Duration.Epsilon
@@ -42,13 +42,13 @@ async ValueTask Read(bool async, PgReader reader, CancellationToken ca
}
public override Size GetSize(SizeContext context, Interval value, ref object? writeState)
- => _rangeConverter.GetSize(context, IntervalToNpgsqlRange(value), ref writeState);
+ => rangeConverter.GetSize(context, IntervalToNpgsqlRange(value), ref writeState);
public override void Write(PgWriter writer, Interval value)
- => _rangeConverter.Write(writer, IntervalToNpgsqlRange(value));
+ => rangeConverter.Write(writer, IntervalToNpgsqlRange(value));
public override ValueTask WriteAsync(PgWriter writer, Interval value, CancellationToken cancellationToken = default)
- => _rangeConverter.WriteAsync(writer, IntervalToNpgsqlRange(value), cancellationToken);
+ => rangeConverter.WriteAsync(writer, IntervalToNpgsqlRange(value), cancellationToken);
static NpgsqlRange IntervalToNpgsqlRange(Interval interval)
=> new(
diff --git a/src/Npgsql.NodaTime/Internal/LegacyConverters.cs b/src/Npgsql.NodaTime/Internal/LegacyConverters.cs
index 54393a4821..c0b4b82268 100644
--- a/src/Npgsql.NodaTime/Internal/LegacyConverters.cs
+++ b/src/Npgsql.NodaTime/Internal/LegacyConverters.cs
@@ -5,17 +5,9 @@
namespace Npgsql.NodaTime.Internal;
-sealed class LegacyTimestampTzZonedDateTimeConverter : PgBufferedConverter
+sealed class LegacyTimestampTzZonedDateTimeConverter(DateTimeZone dateTimeZone, bool dateTimeInfinityConversions)
+ : PgBufferedConverter
{
- readonly DateTimeZone _dateTimeZone;
- readonly bool _dateTimeInfinityConversions;
-
- public LegacyTimestampTzZonedDateTimeConverter(DateTimeZone dateTimeZone, bool dateTimeInfinityConversions)
- {
- _dateTimeZone = dateTimeZone;
- _dateTimeInfinityConversions = dateTimeInfinityConversions;
- }
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -24,34 +16,26 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
protected override ZonedDateTime ReadCore(PgReader reader)
{
- var instant = DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions);
- if (_dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
+ var instant = DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions);
+ if (dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
throw new InvalidCastException("Infinity values not supported for timestamp with time zone");
- return instant.InZone(_dateTimeZone);
+ return instant.InZone(dateTimeZone);
}
protected override void WriteCore(PgWriter writer, ZonedDateTime value)
{
var instant = value.ToInstant();
- if (_dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
+ if (dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
throw new ArgumentException("Infinity values not supported for timestamp with time zone");
- writer.WriteInt64(EncodeInstant(instant, _dateTimeInfinityConversions));
+ writer.WriteInt64(EncodeInstant(instant, dateTimeInfinityConversions));
}
}
-sealed class LegacyTimestampTzOffsetDateTimeConverter : PgBufferedConverter
+sealed class LegacyTimestampTzOffsetDateTimeConverter(DateTimeZone dateTimeZone, bool dateTimeInfinityConversions)
+ : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
- readonly DateTimeZone _dateTimeZone;
-
- public LegacyTimestampTzOffsetDateTimeConverter(DateTimeZone dateTimeZone, bool dateTimeInfinityConversions)
- {
- _dateTimeInfinityConversions = dateTimeInfinityConversions;
- _dateTimeZone = dateTimeZone;
- }
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -60,17 +44,17 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
protected override OffsetDateTime ReadCore(PgReader reader)
{
- var instant = DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions);
- if (_dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
+ var instant = DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions);
+ if (dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
throw new InvalidCastException("Infinity values not supported for timestamp with time zone");
- return instant.InZone(_dateTimeZone).ToOffsetDateTime();
+ return instant.InZone(dateTimeZone).ToOffsetDateTime();
}
protected override void WriteCore(PgWriter writer, OffsetDateTime value)
{
var instant = value.ToInstant();
- if (_dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
+ if (dateTimeInfinityConversions && (instant == Instant.MaxValue || instant == Instant.MinValue))
throw new ArgumentException("Infinity values not supported for timestamp with time zone");
writer.WriteInt64(EncodeInstant(instant, true));
diff --git a/src/Npgsql.NodaTime/Internal/LocalDateConverter.cs b/src/Npgsql.NodaTime/Internal/LocalDateConverter.cs
index e6be7fe69b..ffaa6e8d45 100644
--- a/src/Npgsql.NodaTime/Internal/LocalDateConverter.cs
+++ b/src/Npgsql.NodaTime/Internal/LocalDateConverter.cs
@@ -5,13 +5,8 @@
namespace Npgsql.NodaTime.Internal;
-sealed class LocalDateConverter : PgBufferedConverter
+sealed class LocalDateConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
-
- public LocalDateConverter(bool dateTimeInfinityConversions)
- => _dateTimeInfinityConversions = dateTimeInfinityConversions;
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(int));
@@ -21,10 +16,10 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
protected override LocalDate ReadCore(PgReader reader)
=> reader.ReadInt32() switch
{
- int.MaxValue => _dateTimeInfinityConversions
+ int.MaxValue => dateTimeInfinityConversions
? LocalDate.MaxIsoValue
: throw new InvalidCastException(NpgsqlNodaTimeStrings.CannotReadInfinityValue),
- int.MinValue => _dateTimeInfinityConversions
+ int.MinValue => dateTimeInfinityConversions
? LocalDate.MinIsoValue
: throw new InvalidCastException(NpgsqlNodaTimeStrings.CannotReadInfinityValue),
var value => new LocalDate().PlusDays(value + 730119)
@@ -32,7 +27,7 @@ protected override LocalDate ReadCore(PgReader reader)
protected override void WriteCore(PgWriter writer, LocalDate value)
{
- if (_dateTimeInfinityConversions)
+ if (dateTimeInfinityConversions)
{
if (value == LocalDate.MaxIsoValue)
{
diff --git a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Multirange.cs b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Multirange.cs
index 42c6360dad..fdd8d4c78f 100644
--- a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Multirange.cs
+++ b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Multirange.cs
@@ -31,12 +31,12 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings)
mappings.AddType(TimestampTzMultirangeDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, CreateArrayMultirangeConverter(new IntervalConverter(
- CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options)), options)),
+ CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options), options.EnableDateTimeInfinityConversions), options)),
isDefault: true);
mappings.AddType>(TimestampTzMultirangeDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, CreateListMultirangeConverter(new IntervalConverter(
- CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options)), options)));
+ CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options), options.EnableDateTimeInfinityConversions), options)));
mappings.AddType[]>(TimestampTzMultirangeDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options,
diff --git a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Range.cs b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Range.cs
index f62669333c..8958f88846 100644
--- a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Range.cs
+++ b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.Range.cs
@@ -31,7 +31,7 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings)
static (options, mapping, _) =>
mapping.CreateInfo(options,
new IntervalConverter(
- CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options))),
+ CreateRangeConverter(new InstantConverter(options.EnableDateTimeInfinityConversions), options), options.EnableDateTimeInfinityConversions)),
isDefault: true);
mappings.AddStructType>(TimestampTzRangeDataTypeName,
static (options, mapping, _) => mapping.CreateInfo(options,
diff --git a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.cs b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.cs
index dce258b453..b010ce58a6 100644
--- a/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.cs
+++ b/src/Npgsql.NodaTime/Internal/NodaTimeTypeInfoResolverFactory.cs
@@ -31,47 +31,47 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings)
// timestamp and timestamptz, legacy and non-legacy modes
if (LegacyTimestampBehavior)
{
+ // timestamp is the default for writing an Instant.
+
+ // timestamp
+ mappings.AddStructType(TimestampDataTypeName,
+ static (options, mapping, _) =>
+ mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)), isDefault: true);
+ mappings.AddStructType(TimestampDataTypeName,
+ static (options, mapping, _) =>
+ mapping.CreateInfo(options, new LocalDateTimeConverter(options.EnableDateTimeInfinityConversions)));
+
// timestamptz
- mappings.AddStructType(new DataTypeName("pg_catalog.timestamptz"),
+ mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
- mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)), isDefault: false);
- mappings.AddStructType(new DataTypeName("pg_catalog.timestamptz"),
+ mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)), isDefault: true);
+ mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new LegacyTimestampTzZonedDateTimeConverter(
DateTimeZoneProviders.Tzdb[options.TimeZone], options.EnableDateTimeInfinityConversions)));
- mappings.AddStructType(new DataTypeName("pg_catalog.timestamptz"),
+ mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new LegacyTimestampTzOffsetDateTimeConverter(
DateTimeZoneProviders.Tzdb[options.TimeZone], options.EnableDateTimeInfinityConversions)));
-
+ }
+ else
+ {
// timestamp
- mappings.AddStructType(TimestampDataTypeName,
- static (options, mapping, _) =>
- mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: true);
mappings.AddStructType(TimestampDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new LocalDateTimeConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: false);
- }
- else
- {
+ isDefault: true);
+
// timestamptz
mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)), isDefault: true);
- mappings.AddStructType(new DataTypeName("pg_catalog.timestamptz"),
+ mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new ZonedDateTimeConverter(options.EnableDateTimeInfinityConversions)));
- mappings.AddStructType(new DataTypeName("pg_catalog.timestamptz"),
+ mappings.AddStructType(TimestampTzDataTypeName,
static (options, mapping, _) =>
mapping.CreateInfo(options, new OffsetDateTimeConverter(options.EnableDateTimeInfinityConversions)));
-
- // timestamp
- mappings.AddStructType(TimestampDataTypeName,
- static (options, mapping, _) =>
- mapping.CreateInfo(options, new LocalDateTimeConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: true);
}
// date
@@ -89,7 +89,7 @@ static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings)
// interval
mappings.AddType(IntervalDataTypeName,
- static (options, mapping, _) => mapping.CreateInfo(options, new PeriodConverter()), isDefault: true);
+ static (options, mapping, _) => mapping.CreateInfo(options, new PeriodConverter(options.EnableDateTimeInfinityConversions)), isDefault: true);
mappings.AddStructType(IntervalDataTypeName,
static (options, mapping, _) => mapping.CreateInfo(options, new DurationConverter()));
@@ -107,34 +107,27 @@ sealed class ArrayResolver : Resolver, IPgTypeInfoResolver
static TypeInfoMappingCollection AddMappings(TypeInfoMappingCollection mappings)
{
- // timestamptz
- mappings.AddStructArrayType(TimestampTzDataTypeName);
- mappings.AddStructArrayType(TimestampTzDataTypeName);
- mappings.AddStructArrayType(TimestampTzDataTypeName);
-
- // timestamp
if (LegacyTimestampBehavior)
{
+ // timestamp
mappings.AddStructArrayType(TimestampDataTypeName);
+ mappings.AddStructArrayType(TimestampDataTypeName);
- mappings.AddStructType(TimestampDataTypeName,
- static (options, mapping, _) =>
- mapping.CreateInfo(options, new InstantConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: true);
- mappings.AddStructType(TimestampDataTypeName,
- static (options, mapping, _) =>
- mapping.CreateInfo(options, new LocalDateTimeConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: false);
+ // timestamptz
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
}
else
{
- mappings.AddStructType(TimestampDataTypeName,
- static (options, mapping, _) =>
- mapping.CreateInfo(options, new LocalDateTimeConverter(options.EnableDateTimeInfinityConversions)),
- isDefault: true);
- }
+ // timestamp
+ mappings.AddStructArrayType(TimestampDataTypeName);
- mappings.AddStructArrayType(TimestampDataTypeName);
+ // timestamptz
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
+ mappings.AddStructArrayType(TimestampTzDataTypeName);
+ }
// other
mappings.AddStructArrayType(DateDataTypeName);
diff --git a/src/Npgsql.NodaTime/Internal/PeriodConverter.cs b/src/Npgsql.NodaTime/Internal/PeriodConverter.cs
index 4dbde48dbc..1d768109c4 100644
--- a/src/Npgsql.NodaTime/Internal/PeriodConverter.cs
+++ b/src/Npgsql.NodaTime/Internal/PeriodConverter.cs
@@ -1,9 +1,11 @@
+using System;
using NodaTime;
using Npgsql.Internal;
+using Npgsql.NodaTime.Properties;
namespace Npgsql.NodaTime.Internal;
-sealed class PeriodConverter : PgBufferedConverter
+sealed class PeriodConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
@@ -17,6 +19,15 @@ protected override Period ReadCore(PgReader reader)
var days = reader.ReadInt32();
var totalMonths = reader.ReadInt32();
+ if (microsecondsInDay == long.MaxValue && days == int.MaxValue && totalMonths == int.MaxValue)
+ return dateTimeInfinityConversions
+ ? Period.MaxValue
+ : throw new InvalidCastException(NpgsqlNodaTimeStrings.CannotReadInfinityValue);
+ if (microsecondsInDay == long.MinValue && days == int.MinValue && totalMonths == int.MinValue)
+ return dateTimeInfinityConversions
+ ? Period.MinValue
+ : throw new InvalidCastException(NpgsqlNodaTimeStrings.CannotReadInfinityValue);
+
// NodaTime will normalize most things (i.e. nanoseconds to milliseconds, seconds...)
// but it will not normalize months to years.
var months = totalMonths % 12;
@@ -33,14 +44,45 @@ protected override Period ReadCore(PgReader reader)
protected override void WriteCore(PgWriter writer, Period value)
{
- // Note that the end result must be long
- // see #3438
- var microsecondsInDay =
- (((value.Hours * NodaConstants.MinutesPerHour + value.Minutes) * NodaConstants.SecondsPerMinute + value.Seconds) * NodaConstants.MillisecondsPerSecond + value.Milliseconds) * 1000 +
- value.Nanoseconds / 1000; // Take the microseconds, discard the nanosecond remainder
-
- writer.WriteInt64(microsecondsInDay);
- writer.WriteInt32(value.Weeks * 7 + value.Days); // days
- writer.WriteInt32(value.Years * 12 + value.Months); // months
+ if (dateTimeInfinityConversions)
+ {
+ if (value == Period.MaxValue)
+ {
+ writer.WriteInt64(long.MaxValue); // microseconds
+ writer.WriteInt32(int.MaxValue); // days
+ writer.WriteInt32(int.MaxValue); // months
+ return;
+ }
+
+ if (value == Period.MinValue)
+ {
+ writer.WriteInt64(long.MinValue); // microseconds
+ writer.WriteInt32(int.MinValue); // days
+ writer.WriteInt32(int.MinValue); // months
+ return;
+ }
+ }
+
+ // We have to normalize the value as otherwise we might get a value with 0 everything except for ticks, which we ignore
+ value = value.Normalize();
+
+ try
+ {
+ checked
+ {
+ // Note that the end result must be long
+ // see #3438
+ var microsecondsInDay =
+ (((value.Hours * NodaConstants.MinutesPerHour + value.Minutes) * NodaConstants.SecondsPerMinute + value.Seconds) * NodaConstants.MillisecondsPerSecond + value.Milliseconds) * 1000 +
+ value.Nanoseconds / 1000; // Take the microseconds, discard the nanosecond remainder
+ writer.WriteInt64(microsecondsInDay);
+ writer.WriteInt32(value.Weeks * 7 + value.Days); // days
+ writer.WriteInt32(value.Years * 12 + value.Months); // months
+ }
+ }
+ catch (OverflowException ex)
+ {
+ throw new ArgumentException(NpgsqlNodaTimeStrings.CannotWritePeriodDueToOverflow, ex);
+ }
}
}
diff --git a/src/Npgsql.NodaTime/Internal/TimestampConverters.cs b/src/Npgsql.NodaTime/Internal/TimestampConverters.cs
index 6808503638..4ac841c80e 100644
--- a/src/Npgsql.NodaTime/Internal/TimestampConverters.cs
+++ b/src/Npgsql.NodaTime/Internal/TimestampConverters.cs
@@ -5,13 +5,8 @@
namespace Npgsql.NodaTime.Internal;
-sealed class InstantConverter : PgBufferedConverter
+sealed class InstantConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
-
- public InstantConverter(bool dateTimeInfinityConversions)
- => _dateTimeInfinityConversions = dateTimeInfinityConversions;
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -19,19 +14,14 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
}
protected override Instant ReadCore(PgReader reader)
- => DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions);
+ => DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions);
protected override void WriteCore(PgWriter writer, Instant value)
- => writer.WriteInt64(EncodeInstant(value, _dateTimeInfinityConversions));
+ => writer.WriteInt64(EncodeInstant(value, dateTimeInfinityConversions));
}
-sealed class ZonedDateTimeConverter : PgBufferedConverter
+sealed class ZonedDateTimeConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
-
- public ZonedDateTimeConverter(bool dateTimeInfinityConversions)
- => _dateTimeInfinityConversions = dateTimeInfinityConversions;
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -39,7 +29,7 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
}
protected override ZonedDateTime ReadCore(PgReader reader)
- => DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions).InUtc();
+ => DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions).InUtc();
protected override void WriteCore(PgWriter writer, ZonedDateTime value)
{
@@ -51,17 +41,12 @@ protected override void WriteCore(PgWriter writer, ZonedDateTime value)
"See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.");
}
- writer.WriteInt64(EncodeInstant(value.ToInstant(), _dateTimeInfinityConversions));
+ writer.WriteInt64(EncodeInstant(value.ToInstant(), dateTimeInfinityConversions));
}
}
-sealed class OffsetDateTimeConverter : PgBufferedConverter
+sealed class OffsetDateTimeConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
-
- public OffsetDateTimeConverter(bool dateTimeInfinityConversions)
- => _dateTimeInfinityConversions = dateTimeInfinityConversions;
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -69,7 +54,7 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
}
protected override OffsetDateTime ReadCore(PgReader reader)
- => DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions).WithOffset(Offset.Zero);
+ => DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions).WithOffset(Offset.Zero);
protected override void WriteCore(PgWriter writer, OffsetDateTime value)
{
@@ -81,17 +66,12 @@ protected override void WriteCore(PgWriter writer, OffsetDateTime value)
"See the Npgsql.EnableLegacyTimestampBehavior AppContext switch to enable legacy behavior.");
}
- writer.WriteInt64(EncodeInstant(value.ToInstant(), _dateTimeInfinityConversions));
+ writer.WriteInt64(EncodeInstant(value.ToInstant(), dateTimeInfinityConversions));
}
}
-sealed class LocalDateTimeConverter : PgBufferedConverter
+sealed class LocalDateTimeConverter(bool dateTimeInfinityConversions) : PgBufferedConverter
{
- readonly bool _dateTimeInfinityConversions;
-
- public LocalDateTimeConverter(bool dateTimeInfinityConversions)
- => _dateTimeInfinityConversions = dateTimeInfinityConversions;
-
public override bool CanConvert(DataFormat format, out BufferRequirements bufferRequirements)
{
bufferRequirements = BufferRequirements.CreateFixedSize(sizeof(long));
@@ -99,8 +79,8 @@ public override bool CanConvert(DataFormat format, out BufferRequirements buffer
}
protected override LocalDateTime ReadCore(PgReader reader)
- => DecodeInstant(reader.ReadInt64(), _dateTimeInfinityConversions).InUtc().LocalDateTime;
+ => DecodeInstant(reader.ReadInt64(), dateTimeInfinityConversions).InUtc().LocalDateTime;
protected override void WriteCore(PgWriter writer, LocalDateTime value)
- => writer.WriteInt64(EncodeInstant(value.InUtc().ToInstant(), _dateTimeInfinityConversions));
+ => writer.WriteInt64(EncodeInstant(value.InUtc().ToInstant(), dateTimeInfinityConversions));
}
diff --git a/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj b/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj
index 4ac9e068fa..1fd5d4b767 100644
--- a/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj
+++ b/src/Npgsql.NodaTime/Npgsql.NodaTime.csproj
@@ -4,8 +4,8 @@
NodaTime plugin for Npgsql, allowing mapping of PostgreSQL date/time types to NodaTime types.
npgsql;postgresql;postgres;nodatime;date;time;ado;ado;net;database;sql
README.md
- net6.0
- net8.0
+ net10.0
+ $(NoWarn);NPG9001
diff --git a/src/Npgsql.NodaTime/NpgsqlNodaTimeExtensions.cs b/src/Npgsql.NodaTime/NpgsqlNodaTimeExtensions.cs
index 9ebf42e83f..585143f3fe 100644
--- a/src/Npgsql.NodaTime/NpgsqlNodaTimeExtensions.cs
+++ b/src/Npgsql.NodaTime/NpgsqlNodaTimeExtensions.cs
@@ -9,6 +9,7 @@ namespace Npgsql;
///
public static class NpgsqlNodaTimeExtensions
{
+ // Note: defined for binary compatibility and NpgsqlConnection.GlobalTypeMapper.
///
/// Sets up NodaTime mappings for the PostgreSQL date/time types.
///
@@ -18,4 +19,14 @@ public static INpgsqlTypeMapper UseNodaTime(this INpgsqlTypeMapper mapper)
mapper.AddTypeInfoResolverFactory(new NodaTimeTypeInfoResolverFactory());
return mapper;
}
+
+ ///
+ /// Sets up NodaTime mappings for the PostgreSQL date/time types.
+ ///
+ /// The type mapper to set up (global or connection-specific)
+ public static TMapper UseNodaTime(this TMapper mapper) where TMapper : INpgsqlTypeMapper
+ {
+ mapper.AddTypeInfoResolverFactory(new NodaTimeTypeInfoResolverFactory());
+ return mapper;
+ }
}
diff --git a/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.Designer.cs b/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.Designer.cs
index bc6511ea9a..ab29289106 100644
--- a/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.Designer.cs
+++ b/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.Designer.cs
@@ -11,32 +11,46 @@ namespace Npgsql.NodaTime.Properties {
using System;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class NpgsqlNodaTimeStrings {
- private static System.Resources.ResourceManager resourceMan;
+ private static global::System.Resources.ResourceManager resourceMan;
- private static System.Globalization.CultureInfo resourceCulture;
+ private static global::System.Globalization.CultureInfo resourceCulture;
- [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal NpgsqlNodaTimeStrings() {
}
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Resources.ResourceManager ResourceManager {
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
get {
- if (object.Equals(null, resourceMan)) {
- System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Npgsql.NodaTime.Properties.NpgsqlNodaTimeStrings", typeof(NpgsqlNodaTimeStrings).Assembly);
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Npgsql.NodaTime.Properties.NpgsqlNodaTimeStrings", typeof(NpgsqlNodaTimeStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Globalization.CultureInfo Culture {
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@@ -45,16 +59,31 @@ internal static System.Globalization.CultureInfo Culture {
}
}
+ ///
+ /// Looks up a localized string similar to Cannot read infinity value since Npgsql.DisableDateTimeInfinityConversions is enabled..
+ ///
internal static string CannotReadInfinityValue {
get {
return ResourceManager.GetString("CannotReadInfinityValue", resourceCulture);
}
}
+ ///
+ /// Looks up a localized string similar to Cannot read PostgreSQL interval with non-zero months to NodaTime Duration. Try reading as a NodaTime Period instead..
+ ///
internal static string CannotReadIntervalWithMonthsAsDuration {
get {
return ResourceManager.GetString("CannotReadIntervalWithMonthsAsDuration", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Cannot write NodaTime's Period because it's out of range for the PG interval type..
+ ///
+ internal static string CannotWritePeriodDueToOverflow {
+ get {
+ return ResourceManager.GetString("CannotWritePeriodDueToOverflow", resourceCulture);
+ }
+ }
}
}
diff --git a/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.resx b/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.resx
index d3329f2a80..f0090afb83 100644
--- a/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.resx
+++ b/src/Npgsql.NodaTime/Properties/NpgsqlNodaTimeStrings.resx
@@ -24,4 +24,7 @@
Cannot read PostgreSQL interval with non-zero months to NodaTime Duration. Try reading as a NodaTime Period instead.
+
+ Cannot write NodaTime's Period because it's out of range for the PG interval type.
+
diff --git a/src/Npgsql.NodaTime/PublicAPI.Unshipped.txt b/src/Npgsql.NodaTime/PublicAPI.Unshipped.txt
index ab058de62d..f1ab4e3c0c 100644
--- a/src/Npgsql.NodaTime/PublicAPI.Unshipped.txt
+++ b/src/Npgsql.NodaTime/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
#nullable enable
+static Npgsql.NpgsqlNodaTimeExtensions.UseNodaTime(this TMapper mapper) -> TMapper
diff --git a/src/Npgsql.OpenTelemetry/MeterProviderBuilderExtensions.cs b/src/Npgsql.OpenTelemetry/MeterProviderBuilderExtensions.cs
new file mode 100644
index 0000000000..90f81c4cc3
--- /dev/null
+++ b/src/Npgsql.OpenTelemetry/MeterProviderBuilderExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+using OpenTelemetry.Metrics;
+
+// ReSharper disable once CheckNamespace
+namespace Npgsql;
+
+///
+/// Extension method for setting up Npgsql OpenTelemetry metrics.
+///
+public static class MeterProviderBuilderExtensions
+{
+ ///
+ /// Subscribes to the Npgsql metrics reporter to enable OpenTelemetry metrics.
+ ///
+ public static MeterProviderBuilder AddNpgsqlInstrumentation(
+ this MeterProviderBuilder builder,
+ Action? options = null)
+ => builder.AddMeter("Npgsql");
+}
diff --git a/src/Npgsql.OpenTelemetry/Npgsql.OpenTelemetry.csproj b/src/Npgsql.OpenTelemetry/Npgsql.OpenTelemetry.csproj
index 7aff759251..18592f8a5f 100644
--- a/src/Npgsql.OpenTelemetry/Npgsql.OpenTelemetry.csproj
+++ b/src/Npgsql.OpenTelemetry/Npgsql.OpenTelemetry.csproj
@@ -2,8 +2,7 @@
Shay Rojansky
- net6.0
- net8.0
+ net10.0
npgsql;postgresql;postgres;ado;ado.net;database;sql;opentelemetry;tracing;diagnostics;instrumentation
README.md
diff --git a/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs b/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs
index 0c34138278..1568d2d080 100644
--- a/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs
+++ b/src/Npgsql.OpenTelemetry/TracerProviderBuilderExtensions.cs
@@ -12,8 +12,6 @@ public static class TracerProviderBuilderExtensions
///
/// Subscribes to the Npgsql activity source to enable OpenTelemetry tracing.
///
- public static TracerProviderBuilder AddNpgsql(
- this TracerProviderBuilder builder,
- Action? options = null)
+ public static TracerProviderBuilder AddNpgsql(this TracerProviderBuilder builder)
=> builder.AddSource("Npgsql");
-}
\ No newline at end of file
+}
diff --git a/src/Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj b/src/Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj
index bc0f37e9bb..4f5c1eb42d 100644
--- a/src/Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj
+++ b/src/Npgsql.SourceGenerators/Npgsql.SourceGenerators.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ false
1591
true
diff --git a/src/Npgsql.SourceGenerators/NpgsqlConnectionStringBuilderSourceGenerator.cs b/src/Npgsql.SourceGenerators/NpgsqlConnectionStringBuilderSourceGenerator.cs
index 665789e74e..c7c7228321 100644
--- a/src/Npgsql.SourceGenerators/NpgsqlConnectionStringBuilderSourceGenerator.cs
+++ b/src/Npgsql.SourceGenerators/NpgsqlConnectionStringBuilderSourceGenerator.cs
@@ -9,7 +9,7 @@
namespace Npgsql.SourceGenerators;
[Generator]
-public class NpgsqlConnectionStringBuilderSourceGenerator : ISourceGenerator
+public class NpgsqlConnectionStringBuilderSourceGenerator : IIncrementalGenerator
{
static readonly DiagnosticDescriptor InternalError = new DiagnosticDescriptor(
id: "PGXXXX",
@@ -19,106 +19,107 @@ public class NpgsqlConnectionStringBuilderSourceGenerator : ISourceGenerator
DiagnosticSeverity.Error,
isEnabledByDefault: true);
- public void Initialize(GeneratorInitializationContext context) {}
-
- public void Execute(GeneratorExecutionContext context)
+ public void Initialize(IncrementalGeneratorInitializationContext context)
{
- if (context.Compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringBuilder") is not { } type)
- return;
-
- if (context.Compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringPropertyAttribute") is not
- { } connectionStringPropertyAttribute)
- {
- context.ReportDiagnostic(Diagnostic.Create(
- InternalError,
- location: null,
- "Could not find Npgsql.NpgsqlConnectionStringPropertyAttribute"));
- return;
- }
-
- var obsoleteAttribute = context.Compilation.GetTypeByMetadataName("System.ObsoleteAttribute");
- var displayNameAttribute = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DisplayNameAttribute");
- var defaultValueAttribute = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DefaultValueAttribute");
-
- if (obsoleteAttribute is null || displayNameAttribute is null || defaultValueAttribute is null)
+ var compilationProvider = context.CompilationProvider;
+ context.RegisterSourceOutput(compilationProvider, (spc, compilation) =>
{
- context.ReportDiagnostic(Diagnostic.Create(
- InternalError,
- location: null,
- "Could not find ObsoleteAttribute, DisplayNameAttribute or DefaultValueAttribute"));
- return;
- }
-
- var properties = new List();
- var propertiesByKeyword = new Dictionary();
- foreach (var member in type.GetMembers())
- {
- if (member is not IPropertySymbol property ||
- property.GetAttributes().FirstOrDefault(a => connectionStringPropertyAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)) is not { } propertyAttribute ||
- property.GetAttributes()
- .FirstOrDefault(a => displayNameAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
- ?.ConstructorArguments[0].Value is not string displayName)
+ var type = compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringBuilder");
+ if (type is null)
+ return;
+
+ var connectionStringPropertyAttribute = compilation.Assembly.GetTypeByMetadataName("Npgsql.NpgsqlConnectionStringPropertyAttribute");
+ if (connectionStringPropertyAttribute is null)
{
- continue;
+ spc.ReportDiagnostic(Diagnostic.Create(
+ InternalError,
+ location: null,
+ "Could not find Npgsql.NpgsqlConnectionStringPropertyAttribute"));
+ return;
}
- var explicitDefaultValue = property.GetAttributes()
- .FirstOrDefault(a => defaultValueAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
- ?.ConstructorArguments[0].Value;
-
- if (explicitDefaultValue is string s)
- explicitDefaultValue = '"' + s.Replace("\"", "\"\"") + '"';
+ var obsoleteAttribute = compilation.GetTypeByMetadataName("System.ObsoleteAttribute");
+ var displayNameAttribute = compilation.GetTypeByMetadataName("System.ComponentModel.DisplayNameAttribute");
+ var defaultValueAttribute = compilation.GetTypeByMetadataName("System.ComponentModel.DefaultValueAttribute");
- if (explicitDefaultValue is not null && property.Type.TypeKind == TypeKind.Enum)
+ if (obsoleteAttribute is null || displayNameAttribute is null || defaultValueAttribute is null)
{
- explicitDefaultValue = $"({property.Type.Name}){explicitDefaultValue}";
- // var foo = property.Type.Name;
- // explicitDefaultValue += $"/* {foo} */";
+ spc.ReportDiagnostic(Diagnostic.Create(
+ InternalError,
+ location: null,
+ "Could not find ObsoleteAttribute, DisplayNameAttribute or DefaultValueAttribute"));
+ return;
}
- var propertyDetails = new PropertyDetails
+ var properties = new List();
+ var propertiesByKeyword = new Dictionary();
+ foreach (var member in type.GetMembers())
{
- Name = property.Name,
- CanonicalName = displayName,
- TypeName = property.Type.Name,
- IsEnum = property.Type.TypeKind == TypeKind.Enum,
- IsObsolete = property.GetAttributes().Any(a => obsoleteAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)),
- DefaultValue = explicitDefaultValue
- };
+ if (member is not IPropertySymbol property ||
+ property.GetAttributes().FirstOrDefault(a => connectionStringPropertyAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)) is not { } propertyAttribute ||
+ property.GetAttributes()
+ .FirstOrDefault(a => displayNameAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
+ ?.ConstructorArguments[0].Value is not string displayName)
+ {
+ continue;
+ }
- properties.Add(propertyDetails);
+ var explicitDefaultValue = property.GetAttributes()
+ .FirstOrDefault(a => defaultValueAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default))
+ ?.ConstructorArguments[0].Value;
- propertiesByKeyword[displayName.ToUpperInvariant()] = propertyDetails;
- if (property.Name != displayName)
- {
- var propertyName = property.Name.ToUpperInvariant();
- if (!propertiesByKeyword.ContainsKey(propertyName))
- propertyDetails.Alternatives.Add(propertyName);
- }
+ if (explicitDefaultValue is string s)
+ explicitDefaultValue = '"' + s.Replace("\"", "\"\"") + '"';
- if (propertyAttribute.ConstructorArguments.Length == 1)
- {
- foreach (var synonymArg in propertyAttribute.ConstructorArguments[0].Values)
+ if (explicitDefaultValue is not null && property.Type.TypeKind == TypeKind.Enum)
{
- if (synonymArg.Value is string synonym)
+ explicitDefaultValue = $"({property.Type.Name}){explicitDefaultValue}";
+ }
+
+ var propertyDetails = new PropertyDetails
+ {
+ Name = property.Name,
+ CanonicalName = displayName,
+ TypeName = property.Type.Name,
+ IsEnum = property.Type.TypeKind == TypeKind.Enum,
+ IsObsolete = property.GetAttributes().Any(a => obsoleteAttribute.Equals(a.AttributeClass, SymbolEqualityComparer.Default)),
+ DefaultValue = explicitDefaultValue
+ };
+
+ properties.Add(propertyDetails);
+
+ propertiesByKeyword[displayName.ToUpperInvariant()] = propertyDetails;
+ if (property.Name != displayName)
+ {
+ var propertyName = property.Name.ToUpperInvariant();
+ if (!propertiesByKeyword.ContainsKey(propertyName))
+ propertyDetails.Alternatives.Add(propertyName);
+ }
+
+ if (propertyAttribute.ConstructorArguments.Length == 1)
+ {
+ foreach (var synonymArg in propertyAttribute.ConstructorArguments[0].Values)
{
- var synonymName = synonym.ToUpperInvariant();
- if (!propertiesByKeyword.ContainsKey(synonymName))
- propertyDetails.Alternatives.Add(synonymName);
+ if (synonymArg.Value is string synonym)
+ {
+ var synonymName = synonym.ToUpperInvariant();
+ if (!propertiesByKeyword.ContainsKey(synonymName))
+ propertyDetails.Alternatives.Add(synonymName);
+ }
}
}
}
- }
- var template = Template.Parse(EmbeddedResource.GetContent("NpgsqlConnectionStringBuilder.snbtxt"), "NpgsqlConnectionStringBuilder.snbtxt");
+ var template = Template.Parse(EmbeddedResource.GetContent("NpgsqlConnectionStringBuilder.snbtxt"), "NpgsqlConnectionStringBuilder.snbtxt");
- var output = template.Render(new
- {
- Properties = properties,
- PropertiesByKeyword = propertiesByKeyword
- });
+ var output = template.Render(new
+ {
+ Properties = properties,
+ PropertiesByKeyword = propertiesByKeyword
+ });
- context.AddSource(type.Name + ".Generated.cs", SourceText.From(output, Encoding.UTF8));
+ spc.AddSource(type.Name + ".Generated.cs", SourceText.From(output, Encoding.UTF8));
+ });
}
sealed class PropertyDetails
diff --git a/src/Npgsql/BackendMessages/AuthenticationMessages.cs b/src/Npgsql/BackendMessages/AuthenticationMessages.cs
index b6320e87b8..c52da80d33 100644
--- a/src/Npgsql/BackendMessages/AuthenticationMessages.cs
+++ b/src/Npgsql/BackendMessages/AuthenticationMessages.cs
@@ -13,23 +13,15 @@ abstract class AuthenticationRequestMessage : IBackendMessage
sealed class AuthenticationOkMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationOk;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.Ok;
internal static readonly AuthenticationOkMessage Instance = new();
AuthenticationOkMessage() { }
}
-sealed class AuthenticationKerberosV5Message : AuthenticationRequestMessage
-{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationKerberosV5;
-
- internal static readonly AuthenticationKerberosV5Message Instance = new();
- AuthenticationKerberosV5Message() { }
-}
-
sealed class AuthenticationCleartextPasswordMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationCleartextPassword;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.CleartextPassword;
internal static readonly AuthenticationCleartextPasswordMessage Instance = new();
AuthenticationCleartextPasswordMessage() { }
@@ -37,7 +29,7 @@ sealed class AuthenticationCleartextPasswordMessage : AuthenticationRequestMess
sealed class AuthenticationMD5PasswordMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationMD5Password;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.MD5Password;
internal byte[] Salt { get; }
@@ -49,22 +41,12 @@ internal static AuthenticationMD5PasswordMessage Load(NpgsqlReadBuffer buf)
}
AuthenticationMD5PasswordMessage(byte[] salt)
- {
- Salt = salt;
- }
-}
-
-sealed class AuthenticationSCMCredentialMessage : AuthenticationRequestMessage
-{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSCMCredential;
-
- internal static readonly AuthenticationSCMCredentialMessage Instance = new();
- AuthenticationSCMCredentialMessage() { }
+ => Salt = salt;
}
sealed class AuthenticationGSSMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationGSS;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.GSS;
internal static readonly AuthenticationGSSMessage Instance = new();
AuthenticationGSSMessage() { }
@@ -72,7 +54,7 @@ sealed class AuthenticationGSSMessage : AuthenticationRequestMessage
sealed class AuthenticationGSSContinueMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationGSSContinue;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.GSSContinue;
internal byte[] AuthenticationData { get; }
@@ -85,14 +67,12 @@ internal static AuthenticationGSSContinueMessage Load(NpgsqlReadBuffer buf, int
}
AuthenticationGSSContinueMessage(byte[] authenticationData)
- {
- AuthenticationData = authenticationData;
- }
+ => AuthenticationData = authenticationData;
}
sealed class AuthenticationSSPIMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSSPI;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SSPI;
internal static readonly AuthenticationSSPIMessage Instance = new();
AuthenticationSSPIMessage() { }
@@ -102,8 +82,8 @@ sealed class AuthenticationSSPIMessage : AuthenticationRequestMessage
sealed class AuthenticationSASLMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASL;
- internal List Mechanisms { get; } = new();
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASL;
+ internal List Mechanisms { get; } = [];
internal AuthenticationSASLMessage(NpgsqlReadBuffer buf)
{
@@ -117,7 +97,7 @@ internal AuthenticationSASLMessage(NpgsqlReadBuffer buf)
sealed class AuthenticationSASLContinueMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASLContinue;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASLContinue;
internal byte[] Payload { get; }
internal AuthenticationSASLContinueMessage(NpgsqlReadBuffer buf, int len)
@@ -171,7 +151,7 @@ internal static AuthenticationSCRAMServerFirstMessage Load(byte[] bytes, ILogger
sealed class AuthenticationSASLFinalMessage : AuthenticationRequestMessage
{
- internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.AuthenticationSASLFinal;
+ internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASLFinal;
internal byte[] Payload { get; }
internal AuthenticationSASLFinalMessage(NpgsqlReadBuffer buf, int len)
@@ -210,20 +190,15 @@ internal AuthenticationSCRAMServerFinalMessage(string serverSignature)
#endregion SASL
-// TODO: Remove Authentication prefix from everything
enum AuthenticationRequestType
{
- AuthenticationOk = 0,
- AuthenticationKerberosV4 = 1,
- AuthenticationKerberosV5 = 2,
- AuthenticationCleartextPassword = 3,
- AuthenticationCryptPassword = 4,
- AuthenticationMD5Password = 5,
- AuthenticationSCMCredential = 6,
- AuthenticationGSS = 7,
- AuthenticationGSSContinue = 8,
- AuthenticationSSPI = 9,
- AuthenticationSASL = 10,
- AuthenticationSASLContinue = 11,
- AuthenticationSASLFinal = 12
+ Ok = 0,
+ CleartextPassword = 3,
+ MD5Password = 5,
+ GSS = 7,
+ GSSContinue = 8,
+ SSPI = 9,
+ SASL = 10,
+ SASLContinue = 11,
+ SASLFinal = 12
}
diff --git a/src/Npgsql/BackendMessages/CopyMessages.cs b/src/Npgsql/BackendMessages/CopyMessages.cs
index 1aa8aec0c2..e7d4d6935c 100644
--- a/src/Npgsql/BackendMessages/CopyMessages.cs
+++ b/src/Npgsql/BackendMessages/CopyMessages.cs
@@ -13,9 +13,7 @@ abstract class CopyResponseMessageBase : IBackendMessage
internal List ColumnFormatCodes { get; }
internal CopyResponseMessageBase()
- {
- ColumnFormatCodes = new List();
- }
+ => ColumnFormatCodes = [];
internal void Load(NpgsqlReadBuffer buf)
{
diff --git a/src/Npgsql/BackendMessages/ParameterDescriptionMessage.cs b/src/Npgsql/BackendMessages/ParameterDescriptionMessage.cs
index ebda485331..16c4687da5 100644
--- a/src/Npgsql/BackendMessages/ParameterDescriptionMessage.cs
+++ b/src/Npgsql/BackendMessages/ParameterDescriptionMessage.cs
@@ -9,9 +9,7 @@ sealed class ParameterDescriptionMessage : IBackendMessage
internal List TypeOIDs { get; }
internal ParameterDescriptionMessage()
- {
- TypeOIDs = new List();
- }
+ => TypeOIDs = [];
internal ParameterDescriptionMessage Load(NpgsqlReadBuffer buf)
{
@@ -23,4 +21,4 @@ internal ParameterDescriptionMessage Load(NpgsqlReadBuffer buf)
}
public BackendMessageCode Code => BackendMessageCode.ParameterDescription;
-}
\ No newline at end of file
+}
diff --git a/src/Npgsql/BackendMessages/RowDescriptionMessage.cs b/src/Npgsql/BackendMessages/RowDescriptionMessage.cs
index 1dd1045e21..fd04ddfdaf 100644
--- a/src/Npgsql/BackendMessages/RowDescriptionMessage.cs
+++ b/src/Npgsql/BackendMessages/RowDescriptionMessage.cs
@@ -11,18 +11,11 @@
namespace Npgsql.BackendMessages;
-readonly struct ColumnInfo
+readonly struct ColumnInfo(PgConverterInfo converterInfo, DataFormat dataFormat, bool asObject)
{
- public ColumnInfo(PgConverterInfo converterInfo, DataFormat dataFormat, bool asObject)
- {
- ConverterInfo = converterInfo;
- DataFormat = dataFormat;
- AsObject = asObject;
- }
-
- public PgConverterInfo ConverterInfo { get; }
- public DataFormat DataFormat { get; }
- public bool AsObject { get; }
+ public PgConverterInfo ConverterInfo { get; } = converterInfo;
+ public DataFormat DataFormat { get; } = dataFormat;
+ public bool AsObject { get; } = asObject;
}
///
@@ -126,15 +119,19 @@ internal static RowDescriptionMessage CreateForReplication(
return msg;
}
- public FieldDescription this[int index]
+ public FieldDescription this[int ordinal]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- Debug.Assert(index < Count);
- Debug.Assert(_fields[index] != null);
+ if ((uint)ordinal < (uint)Count)
+ {
+ Debug.Assert(_fields[ordinal] != null);
+ return _fields[ordinal]!;
+ }
- return _fields[index]!;
+ ThrowHelper.ThrowIndexOutOfRangeException("Ordinal must be between 0 and " + (Count - 1));
+ return default!;
}
}
@@ -235,7 +232,7 @@ internal FieldDescription(FieldDescription source)
DataFormat = source.DataFormat;
PostgresType = source.PostgresType;
Field = source.Field;
- _objectOrDefaultInfo = source._objectOrDefaultInfo;
+ _objectInfo = source._objectInfo;
}
internal void Populate(
@@ -253,7 +250,7 @@ internal void Populate(
DataFormat = dataFormat;
PostgresType = _serializerOptions.DatabaseInfo.FindPostgresType((Oid)TypeOID)?.GetRepresentationalType() ?? UnknownBackendType.Instance;
Field = new(Name, _serializerOptions.ToCanonicalTypeId(PostgresType), TypeModifier);
- _objectOrDefaultInfo = default;
+ _objectInfo = default;
}
///
@@ -299,18 +296,18 @@ internal void Populate(
internal PostgresType PostgresType { get; private set; }
- internal Type FieldType => ObjectOrDefaultInfo.TypeToConvert;
+ internal Type FieldType => ObjectInfo.TypeToConvert;
- ColumnInfo _objectOrDefaultInfo;
- internal PgConverterInfo ObjectOrDefaultInfo
+ ColumnInfo _objectInfo;
+ internal PgConverterInfo ObjectInfo
{
get
{
- if (!_objectOrDefaultInfo.ConverterInfo.IsDefault)
- return _objectOrDefaultInfo.ConverterInfo;
+ if (!_objectInfo.ConverterInfo.IsDefault)
+ return _objectInfo.ConverterInfo;
- ref var info = ref _objectOrDefaultInfo;
- GetInfo(null, ref _objectOrDefaultInfo);
+ ref var info = ref _objectInfo;
+ GetInfoCore(null, ref _objectInfo);
return info.ConverterInfo;
}
}
@@ -323,29 +320,33 @@ internal FieldDescription Clone()
return field;
}
- internal void GetInfo(Type? type, ref ColumnInfo lastColumnInfo)
+ internal void GetInfo(Type type, ref ColumnInfo lastColumnInfo) => GetInfoCore(type, ref lastColumnInfo);
+ void GetInfoCore(Type? type, ref ColumnInfo lastColumnInfo)
{
Debug.Assert(lastColumnInfo.ConverterInfo.IsDefault || (
- ReferenceEquals(_serializerOptions, lastColumnInfo.ConverterInfo.TypeInfo.Options) &&
- lastColumnInfo.ConverterInfo.TypeInfo.PgTypeId == _serializerOptions.ToCanonicalTypeId(PostgresType)), "Cache is bleeding over");
+ ReferenceEquals(_serializerOptions, lastColumnInfo.ConverterInfo.TypeInfo.Options) && (
+ IsUnknownResultType() && lastColumnInfo.ConverterInfo.TypeInfo.PgTypeId == _serializerOptions.TextPgTypeId ||
+ // Normal resolution
+ lastColumnInfo.ConverterInfo.TypeInfo.PgTypeId == _serializerOptions.ToCanonicalTypeId(PostgresType))
+ ), "Cache is bleeding over");
if (!lastColumnInfo.ConverterInfo.IsDefault && lastColumnInfo.ConverterInfo.TypeToConvert == type)
return;
- var odfInfo = DataFormat is DataFormat.Text && type is not null ? ObjectOrDefaultInfo : _objectOrDefaultInfo.ConverterInfo;
- if (odfInfo is { IsDefault: false })
+ var objectInfo = DataFormat is DataFormat.Text && type is not null ? ObjectInfo : _objectInfo.ConverterInfo;
+ if (objectInfo is { IsDefault: false })
{
if (typeof(object) == type)
{
- lastColumnInfo = new(odfInfo, DataFormat, true);
+ lastColumnInfo = new(objectInfo, DataFormat, true);
return;
}
- if (odfInfo.TypeToConvert == type)
+ if (objectInfo.TypeToConvert == type)
{
// As TypeInfoMappingCollection is always adding object mappings for
// default/datatypename mappings, we'll also check Converter.TypeToConvert.
// If we have an exact match we are still able to use e.g. a converter for ints in an unboxed fashion.
- lastColumnInfo = new(odfInfo, DataFormat, odfInfo.IsBoxingConverter && odfInfo.Converter.TypeToConvert != type);
+ lastColumnInfo = new(objectInfo, DataFormat, objectInfo.IsBoxingConverter && objectInfo.Converter.TypeToConvert != type);
return;
}
}
@@ -355,33 +356,48 @@ internal void GetInfo(Type? type, ref ColumnInfo lastColumnInfo)
[MethodImpl(MethodImplOptions.NoInlining)]
void GetInfoSlow(Type? type, out ColumnInfo lastColumnInfo)
{
- var typeInfo = AdoSerializerHelpers.GetTypeInfoForReading(type ?? typeof(object), PostgresType, _serializerOptions);
PgConverterInfo converterInfo;
switch (DataFormat)
{
- case DataFormat.Binary:
- // If we don't support binary we'll just throw.
+ case DataFormat.Text when IsUnknownResultType():
+ {
+ // Try to resolve some 'pg_catalog.text' type info for the expected clr type.
+ var typeInfo = AdoSerializerHelpers.GetTypeInfoForReading(type ?? typeof(string), _serializerOptions.TextPgTypeId, _serializerOptions);
+
+ // We start binding to DataFormat.Binary as it's the broadest supported format.
+ // The format however is irrelevant as 'pg_catalog.text' data is identical across either.
+ // Given we did a resolution against 'pg_catalog.text' and not the actual field type we're in reinterpretation territory anyway.
+ if (!typeInfo.TryBind(Field, DataFormat.Binary, out converterInfo))
+ converterInfo = typeInfo.Bind(Field, DataFormat.Text);
+
+ lastColumnInfo = new(converterInfo, DataFormat, type != converterInfo.TypeToConvert || converterInfo.IsBoxingConverter);
+
+ break;
+ }
+ case DataFormat.Binary or DataFormat.Text:
+ {
+ var typeInfo = AdoSerializerHelpers.GetTypeInfoForReading(type ?? typeof(object), _serializerOptions.ToCanonicalTypeId(PostgresType), _serializerOptions);
+
+ // If we don't support the DataFormat we'll just throw.
converterInfo = typeInfo.Bind(Field, DataFormat);
- lastColumnInfo = new(converterInfo, DataFormat.Binary, typeof(object) == type || converterInfo.IsBoxingConverter);
+ lastColumnInfo = new(converterInfo, DataFormat, typeof(object) == type || converterInfo.IsBoxingConverter);
break;
+ }
default:
- // For text we'll fall back to any available text converter for the expected clr type or throw.
- if (!typeInfo.TryBind(Field, DataFormat, out converterInfo))
- {
- typeInfo = AdoSerializerHelpers.GetTypeInfoForReading(type ?? typeof(string), _serializerOptions.TextPgType, _serializerOptions);
- converterInfo = typeInfo.Bind(Field, DataFormat);
- lastColumnInfo = new(converterInfo, DataFormat, type != converterInfo.TypeToConvert || converterInfo.IsBoxingConverter);
- }
- else
- lastColumnInfo = new(converterInfo, DataFormat, typeof(object) == type || converterInfo.IsBoxingConverter);
+ ThrowHelper.ThrowUnreachableException("Unknown data format {0}", DataFormat);
+ lastColumnInfo = default;
break;
}
// We delay initializing ObjectOrDefaultInfo until after the first lookup (unless it is itself the first lookup).
// When passed in an unsupported type it allows the error to be more specific, instead of just having object/null to deal with.
- if (_objectOrDefaultInfo.ConverterInfo.IsDefault && type is not null)
- _ = ObjectOrDefaultInfo;
+ if (_objectInfo.ConverterInfo.IsDefault && type is not null)
+ _ = ObjectInfo;
}
+
+ // DataFormat.Text today exclusively signals that we executed with an UnknownResultTypeList.
+ // If we ever want to fully support DataFormat.Text we'll need to flow UnknownResultType status separately.
+ bool IsUnknownResultType() => DataFormat is DataFormat.Text;
}
///
diff --git a/src/Npgsql/Internal/AdoSerializerHelpers.cs b/src/Npgsql/Internal/AdoSerializerHelpers.cs
index d0ea19c7a8..21010b3f99 100644
--- a/src/Npgsql/Internal/AdoSerializerHelpers.cs
+++ b/src/Npgsql/Internal/AdoSerializerHelpers.cs
@@ -2,31 +2,36 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Npgsql.Internal.Postgres;
-using Npgsql.PostgresTypes;
using NpgsqlTypes;
namespace Npgsql.Internal;
static class AdoSerializerHelpers
{
- public static PgTypeInfo GetTypeInfoForReading(Type type, PostgresType postgresType, PgSerializerOptions options)
+ public static PgTypeInfo GetTypeInfoForReading(Type type, PgTypeId pgTypeId, PgSerializerOptions options)
{
PgTypeInfo? typeInfo = null;
Exception? inner = null;
try
{
- typeInfo = type == typeof(object) ? options.GetObjectOrDefaultTypeInfo(postgresType) : options.GetTypeInfo(type, postgresType);
+ typeInfo = options.GetTypeInfoInternal(type, pgTypeId);
+ if (typeInfo is { SupportsReading: false })
+ typeInfo = null;
}
catch (Exception ex)
{
inner = ex;
}
- return typeInfo ?? ThrowReadingNotSupported(type, postgresType.DisplayName, inner);
+ return typeInfo ?? ThrowReadingNotSupported(type, options, pgTypeId, inner);
// InvalidCastException thrown to align with ADO.NET convention.
[DoesNotReturn]
- static PgTypeInfo ThrowReadingNotSupported(Type? type, string displayName, Exception? inner = null)
- => throw new InvalidCastException($"Reading{(type is null ? "" : $" as '{type.FullName}'")} is not supported for fields having DataTypeName '{displayName}'", inner);
+ static PgTypeInfo ThrowReadingNotSupported(Type? type, PgSerializerOptions options, PgTypeId pgTypeId, Exception? inner = null)
+ {
+ throw new InvalidCastException(
+ $"Reading{(type is null ? "" : $" as '{type.FullName}'")} is not supported for fields having DataTypeName '{options.DatabaseInfo.FindPostgresType(pgTypeId)?.DisplayName ?? "unknown"}'",
+ inner);
+ }
}
public static PgTypeInfo GetTypeInfoForWriting(Type? type, PgTypeId? pgTypeId, PgSerializerOptions options, NpgsqlDbType? npgsqlDbType = null)
@@ -37,7 +42,9 @@ public static PgTypeInfo GetTypeInfoForWriting(Type? type, PgTypeId? pgTypeId, P
Exception? inner = null;
try
{
- typeInfo = type is null ? options.GetDefaultTypeInfo(pgTypeId!.Value) : options.GetTypeInfo(type, pgTypeId);
+ typeInfo = options.GetTypeInfoInternal(type, pgTypeId);
+ if (typeInfo is { SupportsWriting: false })
+ typeInfo = null;
}
catch (Exception ex)
{
diff --git a/src/Npgsql/Internal/BufferRequirements.cs b/src/Npgsql/Internal/BufferRequirements.cs
index cd32c0cbd1..14ffabc52b 100644
--- a/src/Npgsql/Internal/BufferRequirements.cs
+++ b/src/Npgsql/Internal/BufferRequirements.cs
@@ -1,7 +1,9 @@
using System;
+using System.Diagnostics.CodeAnalysis;
namespace Npgsql.Internal;
+[Experimental(NpgsqlDiagnostics.ConvertersExperimental)]
public readonly struct BufferRequirements : IEquatable
{
readonly Size _read;
diff --git a/src/Npgsql/Internal/ChainDbTypeResolver.cs b/src/Npgsql/Internal/ChainDbTypeResolver.cs
new file mode 100644
index 0000000000..16f3c229ee
--- /dev/null
+++ b/src/Npgsql/Internal/ChainDbTypeResolver.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using Npgsql.Internal.Postgres;
+
+namespace Npgsql.Internal;
+
+sealed class ChainDbTypeResolver(IEnumerable resolvers) : IDbTypeResolver
+{
+ readonly IDbTypeResolver[] _resolvers = new List(resolvers).ToArray();
+
+ public string? GetDataTypeName(DbType dbType, Type? type)
+ {
+ foreach (var resolver in _resolvers)
+ {
+ if (resolver.GetDataTypeName(dbType, type) is { } dataTypeName)
+ return dataTypeName;
+ }
+
+ return null;
+ }
+
+ public DbType? GetDbType(DataTypeName dataTypeName)
+ {
+ foreach (var resolver in _resolvers)
+ {
+ if (resolver.GetDbType(dataTypeName) is { } dbType)
+ return dbType;
+ }
+
+ return null;
+ }
+}
diff --git a/src/Npgsql/Internal/ChainTypeInfoResolver.cs b/src/Npgsql/Internal/ChainTypeInfoResolver.cs
index 18c39d80b6..4c7f56e454 100644
--- a/src/Npgsql/Internal/ChainTypeInfoResolver.cs
+++ b/src/Npgsql/Internal/ChainTypeInfoResolver.cs
@@ -4,12 +4,9 @@
namespace Npgsql.Internal;
-sealed class ChainTypeInfoResolver : IPgTypeInfoResolver
+sealed class ChainTypeInfoResolver(IEnumerable resolvers) : IPgTypeInfoResolver
{
- readonly IPgTypeInfoResolver[] _resolvers;
-
- public ChainTypeInfoResolver(IEnumerable resolvers)
- => _resolvers = new List(resolvers).ToArray();
+ readonly IPgTypeInfoResolver[] _resolvers = new List(resolvers).ToArray();
public PgTypeInfo? GetTypeInfo(Type? type, DataTypeName? dataTypeName, PgSerializerOptions options)
{
diff --git a/src/Npgsql/Internal/Composites/Metadata/CompositeBuilder.cs b/src/Npgsql/Internal/Composites/Metadata/CompositeBuilder.cs
index c51c0dafa0..0917dfd834 100644
--- a/src/Npgsql/Internal/Composites/Metadata/CompositeBuilder.cs
+++ b/src/Npgsql/Internal/Composites/Metadata/CompositeBuilder.cs
@@ -1,18 +1,18 @@
using System;
using System.Buffers;
+using System.Collections.Generic;
using Npgsql.Util;
namespace Npgsql.Internal.Composites;
-abstract class CompositeBuilder
+abstract class CompositeBuilder(StrongBox[] tempBoxes, IReadOnlyList fields)
{
- protected StrongBox[] _tempBoxes;
+ protected readonly StrongBox[] _tempBoxes = tempBoxes;
+ protected readonly IReadOnlyList _fields = fields;
protected int _currentField;
-
- protected CompositeBuilder(StrongBox[] tempBoxes) => _tempBoxes = tempBoxes;
+ protected object? _boxedInstance;
protected abstract void Construct();
- protected abstract void SetField(TValue value);
public void AddValue(TValue value)
{
@@ -32,78 +32,72 @@ public void AddValue(TValue value)
}
_currentField++;
+
+ void SetField(TValue value)
+ {
+ if (_boxedInstance is null)
+ ThrowHelper.ThrowInvalidOperationException("Not constructed yet, or no more fields were expected.");
+
+ var currentField = _currentField;
+ var fields = _fields;
+ if (currentField > fields.Count - 1)
+ ThrowHelper.ThrowIndexOutOfRangeException($"Cannot set field {value} at position {currentField} - all fields have already been set");
+
+ ((CompositeFieldInfo)fields[currentField]).Set(_boxedInstance, value);
+ }
}
}
-sealed class CompositeBuilder : CompositeBuilder, IDisposable
+sealed class CompositeBuilder(CompositeInfo compositeInfo) : CompositeBuilder(compositeInfo.CreateTempBoxes(), compositeInfo.Fields), IDisposable
{
- readonly CompositeInfo _compositeInfo;
T _instance = default!;
- object? _boxedInstance;
-
- public CompositeBuilder(CompositeInfo compositeInfo)
- : base(compositeInfo.CreateTempBoxes())
- => _compositeInfo = compositeInfo;
public T Complete()
{
- if (_currentField < _compositeInfo.Fields.Count)
- throw new InvalidOperationException($"Missing values, expected: {_compositeInfo.Fields.Count} got: {_currentField}");
+ if (_currentField < compositeInfo.Fields.Count)
+ throw new InvalidOperationException($"Missing values, expected: {compositeInfo.Fields.Count} got: {_currentField}");
return (T)(_boxedInstance ?? _instance!);
}
- public void Reset()
- {
- _instance = default!;
- _boxedInstance = null;
- _currentField = 0;
- foreach (var box in _tempBoxes)
- box.Clear();
- }
-
- public void Dispose() => Reset();
-
protected override void Construct()
{
var tempBoxes = _tempBoxes;
if (_currentField < tempBoxes.Length - 1)
throw new InvalidOperationException($"Missing values, expected: {tempBoxes.Length} got: {_currentField + 1}");
- var fields = _compositeInfo.Fields;
- var args = ArrayPool.Shared.Rent(_compositeInfo.ConstructorParameters);
+ var fields = compositeInfo.Fields;
+ var args = ArrayPool.Shared.Rent(compositeInfo.ConstructorParameters);
for (var i = 0; i < tempBoxes.Length; i++)
{
var field = fields[i];
if (field.ConstructorParameterIndex is { } argIndex)
args[argIndex] = tempBoxes[i];
}
- _instance = _compositeInfo.Constructor(args)!;
- ArrayPool.Shared.Return(args);
+ _instance = compositeInfo.Constructor(args)!;
+ ArrayPool.Shared.Return(args, clearArray: true);
- if (tempBoxes.Length == _compositeInfo.Fields.Count)
+ if (tempBoxes.Length == compositeInfo.Fields.Count)
return;
// We're expecting or already have stored more fields, so box the instance once here.
_boxedInstance = _instance;
for (var i = 0; i < tempBoxes.Length; i++)
{
- var field = _compositeInfo.Fields[i];
+ var field = compositeInfo.Fields[i];
if (field.ConstructorParameterIndex is null)
field.Set(_boxedInstance, tempBoxes[i]);
}
}
- protected override void SetField(TValue value)
+ public void Reset()
{
- if (_boxedInstance is null)
- ThrowHelper.ThrowInvalidOperationException("Not constructed yet, or no more fields were expected.");
-
- var currentField = _currentField;
- var fields = _compositeInfo.Fields;
- if (currentField > fields.Count - 1)
- ThrowHelper.ThrowIndexOutOfRangeException($"Cannot set field {value} at position {currentField} - all fields have already been set");
-
- ((CompositeFieldInfo)fields[currentField]).Set(_boxedInstance, value);
+ _instance = default!;
+ _boxedInstance = null;
+ _currentField = 0;
+ foreach (var box in _tempBoxes)
+ box.Clear();
}
+
+ public void Dispose() { }
}
diff --git a/src/Npgsql/Internal/Composites/Metadata/CompositeFieldInfo.cs b/src/Npgsql/Internal/Composites/Metadata/CompositeFieldInfo.cs
index a6cc79e4e9..080d31ea68 100644
--- a/src/Npgsql/Internal/Composites/Metadata/CompositeFieldInfo.cs
+++ b/src/Npgsql/Internal/Composites/Metadata/CompositeFieldInfo.cs
@@ -143,10 +143,12 @@ sealed class CompositeFieldInfo : CompositeFieldInfo
_getter = getter;
}
+ // Accessed through reflection (ReflectionCompositeInfoFactory)
public CompositeFieldInfo(string name, PgTypeInfo typeInfo, PgTypeId nominalPgTypeId, Func