├── .editorconfig ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CHANGELOG.md ├── LICENSE.txt ├── NHibernate.Extensions.slnx ├── README.md ├── database ├── postgres │ └── 00_aspnet_core_identity.sql └── up.md ├── push-package.sh ├── src ├── NHibernate.Extensions.Npgsql │ ├── ArrayExtensions.cs │ ├── ArrayTypeUtil.cs │ ├── Generators │ │ └── ArrayHqlGenerator.cs │ ├── LinqToHqlGeneratorsRegistry.cs │ ├── NHibernate.Extensions.Npgsql.csproj │ ├── NpgsqlDialect.cs │ ├── NpgsqlDriver.cs │ ├── NpgsqlType.cs │ └── UserTypes │ │ ├── ArrayType.cs │ │ ├── JsonType.cs │ │ ├── TimeStampType.cs │ │ └── TimeStampTzType.cs ├── NHibernate.Extensions.Sqlite │ ├── NHibernate.Extensions.Sqlite.csproj │ ├── SqliteDataBaseMetaData.cs │ ├── SqliteDialect.cs │ └── SqliteDriver.cs ├── NHibernate.NetCore │ ├── LoggerFactoryExtensions.cs │ ├── NHibernate.NetCore.csproj │ ├── NetCoreLogger.cs │ ├── NetCoreLoggerFactory.cs │ ├── ServiceCollectionExtensions.cs │ └── ServiceProviderExtensions.cs └── package-version.props └── test └── NHibernate.Extensions.UnitTest ├── ArrTest.cs ├── ArrayTypeTest.cs ├── AttrMapTest.cs ├── BaseTest.cs ├── ConfigTest.cs ├── DapperTest.cs ├── DvdRentalTest.cs ├── InheritanceMappingTest.cs ├── NHibernate.Extensions.UnitTest.csproj ├── NameQueryTest.cs ├── NpgTimeTests.cs ├── NpgsqlTypeTest.cs ├── QueryTest.cs ├── SnowFlakeTest.cs ├── Sqlite ├── Author.cs └── Book.cs ├── SqliteDriverTest.cs ├── SqliteTest.cs ├── TestDb ├── Actor.cs ├── ArrTestEntity.cs ├── Author.cs ├── BaseResource.cs ├── Book.cs ├── JsonValue.cs ├── MoniData.cs ├── NpgTime.cs ├── SnowFlakeTestEntity.cs ├── TestEntity.cs ├── VersionTable.cs └── XmlTestEntity.cs ├── VersionTest.cs ├── XmlTests.cs ├── appsettings.json ├── hibernate.config ├── hibernate.sqlite.config ├── sql ├── 01_test_table.sql ├── 02_snow_flake_id.sql └── 03_snow_flake_test.sql └── test_db.db3 /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | # All files 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 4 10 | trim_trailing_whitespace = true 11 | # html/scss/css files 12 | [*.{html,scss,css,json,xml,csproj,config}] 13 | indent_size = 2 14 | # markdown files 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vs 3 | .vscode 4 | obj 5 | bin 6 | *.user 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "WebTest", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/NHibernate.Extensions.WebTest/bin/Debug/netcoreapp2.1/NHibernate.Extensions.WebTest.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test/NHibernate.Extensions.WebTest", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "internalConsole", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | } 21 | ,] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/test/NHibernate.Extensions.WebTest/NHibernate.Extensions.WebTest.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelogs for NHibernate.Extensions 2 | 3 | ## 9.0.3 4 | 5 | - Update to .NET 9.0.5; 6 | - Update packages; 7 | 8 | ## 9.0.2 9 | 10 | - Update to .NET 9.0.3; 11 | - Update Npgsql to 9.0.3; 12 | - Update other packages; 13 | - New solution format (slnx); 14 | 15 | ## 9.0.1 16 | 17 | - Update to .NET 9.0.2; 18 | - Update packages; 19 | 20 | ## 9.0.0 21 | 22 | - Update to .NET 9.0.0; 23 | - Update packages; 24 | - Remove obsoleted types; 25 | - Move custom types to namespace `NHibernate.Extensions.Npgsql.UserTypes`; 26 | 27 | ## 8.0.14 28 | 29 | - Update to .NET 8.0.10; 30 | 31 | ## 8.0.13 32 | 33 | - Rename `NpgSql` to `Npgsql`; 34 | - Mark array types inherits from `ArrayType` as obsolete, please use `ArrayType`; 35 | - Mark `SetCustomParameter` `AddCustomScalar` as obsolete, no need to call these methods; 36 | - Obsoleted types and methods will be remove from `9.0.x` . 37 | 38 | ## 8.0.12 39 | 40 | - Optimize array extension methods; 41 | - Add user type aliases; 42 | 43 | ## 8.0.11 44 | 45 | - Add `array_contains` and `array_intersects` extension methods to hql query; 46 | - Add `ArrayContains` and `ArrayIntersects` extension method to linq query; 47 | 48 | Please refer [ArrTest](https://github.com/beginor/nhibernate-extensions/blob/master/test/NHibernate.Extensions.UnitTest/ArrTest.cs) for more details. 49 | 50 | ## 8.0.10 51 | 52 | - Update Npgsql to 8.0.4; 53 | 54 | ## 8.0.9 55 | 56 | - Map `StringArrayType` to `NpgsqlDbType.Array | NpgsqlDbType.Varchar`; 57 | 58 | ## 8.0.8 59 | 60 | - Update NHibernate to 5.5.2; 61 | 62 | ## 8.0.7 63 | 64 | - Update Npgsql to 8.0.3; 65 | 66 | ## 8.0.6 67 | 68 | - Update NHibernate to 5.5.1; 69 | - Refactor to use keyed service; 70 | 71 | ## 8.0.5 72 | 73 | - Fix json types with Npgsql 8.x release. 74 | 75 | ## 8.0.4 76 | 77 | - Update Npgsql to 8.0.2; 78 | 79 | ## 8.0.3 80 | 81 | - add `AddCustomScalar` and `SetCustomParameter` extension method for `ISQLQuery`; 82 | 83 | ## 8.0.2 84 | 85 | - Add NumericArray, thanks to [Stephan](https://github.com/stephanstapel) ! 86 | 87 | ## 8.0.1 88 | 89 | - Update NHibernate to 5.5.0; 90 | - Update Npgsql to 8.0.1; 91 | - Update NUnit to 4.0.0; 92 | - Other nuget package updates. 93 | 94 | ## 8.0.0 95 | 96 | - Update to .NET 8.0.0; 97 | - Update Npgsql to 8.0.0-rc.2; 98 | 99 | ## 7.0.9 100 | 101 | - Update Npgsql to 7.0.6; 102 | - Update Microsoft.Data.Sqlite.Core to 7.0.11; 103 | 104 | ## 7.0.8 105 | 106 | - Update NHibernate to 5.4.6; 107 | - Update Microsoft.NET.Test.Sdk to 17.7.2; 108 | - Update SQLitePCLRaw.bundle_e_sqlite3 to 2.1.6; 109 | 110 | ## 7.0.7 111 | 112 | - Update to .NET 7.0.10; 113 | - Update NHibernate to 5.4.5; 114 | - Update Dapper to 2.0.151; 115 | - Update Microsoft.Data.Sqlite.Core to 7.0.10; 116 | - Update Microsoft.NET.Test.Sdk to 17.6.3; 117 | 118 | ## 7.0.6 119 | 120 | - Update NHibernate to 5.4.4; 121 | - Update to .NET 7.0.9; 122 | 123 | ## 7.0.5 124 | 125 | - Update to .NET 7.0.8; 126 | - Update NHibernate to 5.4.3; 127 | - Update Dapper to 2.0.143; 128 | - Update Microsoft.NET.Test.Sdk to 17.6.3; 129 | 130 | ## 7.0.4 131 | 132 | - Update to .NET 7.0.7; 133 | - Update Npgsql to 7.0.4; 134 | 135 | ## 7.0.3 136 | 137 | - Update to .NET 7.0.4; 138 | - Update Hibernate to 5.4.2; 139 | 140 | ## 7.0.2 141 | 142 | - Update to .NET 7.0.2; 143 | - Update NHibernate to 5.4.1; 144 | 145 | ## 7.0.1 146 | 147 | - Update to .NET 7.0.1; 148 | - Update Npgsql to 7.0.1; 149 | 150 | ## 7.0.0 151 | 152 | - Update to .NET 7.0.0; 153 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /NHibernate.Extensions.slnx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Some useful extensions for NHibernate 2 | 3 | Some useful extensions for NHibernate used in my projects. 4 | 5 | ## NHibernate.Extensions.Npgsql 6 | 7 | - Extended PostgreSQL driver [NpgsqlDriver](https://github.com/beginor/nhibernate-extensions/blob/master/src/NHibernate.Extensions.Npgsql/NpgsqlDriver.cs) for NHibernate, with support of: 8 | 9 | - Type of `json` and `jsonb` 10 | - Array type of `boolean`, `double`, `float (real)`, `int`, `short (small int)`, `long (big int)` and `string (character)`; 11 | - Add `array_contains` and `array_intersects` function to hql query; 12 | - Add `ArrayContains` and `ArrayIntersects` extension to linq query; 13 | 14 | - Extended PostgreSQL dialect [NpgsqlDialect](https://github.com/beginor/nhibernate-extensions/blob/master/src/NHibernate.Extensions.Npgsql/NpgsqlDialect.cs) for schema export of supported types. 15 | 16 | Refer [ArrTestEntity](./test/NHibernate.Extensions.UnitTest/TestDb/ArrTestEntity.cs) for mapping, and [ArrTest](./test/NHibernate.Extensions.UnitTest/ArrTest.cs) for sample query usage; 17 | 18 | ## NHibernate.Extensions.Sqlite 19 | 20 | NHibernate driver for the Microsoft.Data.Sqlite.Core data provider for .NET. 21 | 22 | In order to use this driver, you must have the nuget package [Microsoft.Data.Sqlite.Core](https://www.nuget.org/packages/Microsoft.Data.Sqlite.Core/) and [SQLitePCLRaw.bundle_e_sqlite3](https://www.nuget.org/packages/SQLitePCLRaw.bundle_e_sqlite3/) installed for NHibernate to load. 23 | 24 | It's a .NET Standard 2.0 Driver, Can run on any platform that dotnet core support. 25 | 26 | ## NHibernate.NetCore 27 | 28 | Extensions for NHibernate when used in dotnet core: 29 | 30 | - Use `Microsoft.Extensions.Logging.ILoggerFactory` as NHibernate's logger factory; 31 | - Service Collection Extension of `AddHibernate` method for integration with `IServiceProvider`; 32 | - `OrderBy(string propertyName)`, `OrderByDescending(string propertyName)` and 33 | `AddOrderBy(string propertyName, bool isAsc)` extension methods to `IQueryable`, 34 | make fun with dynamic order by. 35 | 36 | Example usage: 37 | 38 | ```cs 39 | public void ConfigureServices(IServiceCollection services) { 40 | // where is your hibernate.config path 41 | var path = System.IO.Path.Combine( 42 | AppDomain.CurrentDomain.BaseDirectory, 43 | "hibernate.config" 44 | ); 45 | // add NHibernate services; 46 | services.AddHibernate(path); 47 | } 48 | 49 | public void Configure( 50 | IApplicationBuilder app, 51 | IHostingEnvironment env, 52 | Microsoft.Extensions.Logging.ILoggerFactory loggerFactory 53 | ) { 54 | // Use loggerFactory as NHibernate logger factory. 55 | loggerFactory.UseAsHibernateLoggerFactory(); 56 | /* other code goes here */ 57 | } 58 | ``` 59 | 60 | And then use `ISessionFactory` in your controller: 61 | 62 | ```cs 63 | [Route("api/[controller]")] 64 | public class SamplesController : Controller { 65 | 66 | private ISessionFactory sessionFactory; 67 | 68 | public SamplesController(ISessionFactory sessionFactory) { 69 | this.sessionFactory = sessionFactory; 70 | } 71 | 72 | protected override void Dispose(bool disposing) { 73 | if (disposing) { 74 | sessionFactory = null; 75 | } 76 | } 77 | 78 | [HttpGet("")] 79 | public async Task> GetAll() { 80 | try { 81 | using (var session = sessionFactory.OpenSession()) { 82 | var result = await session.Query().ToListAsync(); 83 | return result; 84 | } 85 | } 86 | catch (Exception ex) { 87 | return StatusCode(500, ex.Message); 88 | } 89 | } 90 | } 91 | ``` 92 | 93 | Or just use `ISession` in your controller: 94 | 95 | ```cs 96 | [Route("api/[controller]")] 97 | public class SamplesController : Controller { 98 | 99 | private ISession session; 100 | 101 | public SamplesController(ISession session) { 102 | this.session = session; 103 | } 104 | 105 | protected override void Dispose(bool disposing) { 106 | if (disposing) { 107 | session = null; 108 | } 109 | } 110 | 111 | [HttpGet("")] 112 | public async Task> GetAll() { 113 | try { 114 | var result = await session.Query().ToListAsync(); 115 | return result; 116 | } 117 | catch (Exception ex) { 118 | return StatusCode(500, ex.Message); 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | - `ISessionFactory` is registerd as a singleton service, you should not dispose it; 125 | - `ISession` is registerd as a scoped service, so you do not need to dispose it by hand; 126 | -------------------------------------------------------------------------------- /database/postgres/00_aspnet_core_identity.sql: -------------------------------------------------------------------------------- 1 | -- Table: public.aspnet_roles 2 | 3 | -- DROP TABLE public.aspnet_roles; 4 | 5 | CREATE TABLE public.aspnet_roles 6 | ( 7 | id character(32) COLLATE pg_catalog."default" NOT NULL, 8 | name character varying(64) COLLATE pg_catalog."default" NOT NULL, 9 | normalized_name character varying(64) COLLATE pg_catalog."default" NOT NULL, 10 | concurrency_stamp character(36) COLLATE pg_catalog."default", 11 | CONSTRAINT pk_aspnet_roles PRIMARY KEY (id), 12 | CONSTRAINT u_aspnet_roles_name UNIQUE (name), 13 | CONSTRAINT u_aspnet_roles_normalized_name UNIQUE (normalized_name) 14 | ) 15 | WITH ( 16 | OIDS = FALSE 17 | ) 18 | TABLESPACE pg_default; 19 | 20 | ALTER TABLE public.aspnet_roles 21 | OWNER to postgres; 22 | 23 | -- Index: ix_aspnet_roles_name 24 | 25 | -- DROP INDEX public.ix_aspnet_roles_name; 26 | 27 | CREATE INDEX ix_aspnet_roles_name 28 | ON public.aspnet_roles USING btree 29 | (normalized_name COLLATE pg_catalog."default") 30 | TABLESPACE pg_default; 31 | 32 | -- Table: public.aspnet_role_claims 33 | 34 | -- DROP TABLE public.aspnet_role_claims; 35 | 36 | CREATE TABLE public.aspnet_role_claims 37 | ( 38 | id serial NOT NULL, 39 | role_id character(32) COLLATE pg_catalog."default" NOT NULL, 40 | claim_type character varying(1024) COLLATE pg_catalog."default" NOT NULL, 41 | claim_value character varying(1024) COLLATE pg_catalog."default" NOT NULL, 42 | CONSTRAINT pk_aspnet_role_claims PRIMARY KEY (id), 43 | CONSTRAINT fk_aspnet_role_claims_role_id FOREIGN KEY (role_id) 44 | REFERENCES public.aspnet_roles (id) MATCH SIMPLE 45 | ON UPDATE CASCADE 46 | ON DELETE CASCADE 47 | ) 48 | WITH ( 49 | OIDS = FALSE 50 | ) 51 | TABLESPACE pg_default; 52 | 53 | ALTER TABLE public.aspnet_role_claims 54 | OWNER to postgres; 55 | 56 | -- Index: ix_aspnet_role_claims_role_id 57 | 58 | -- DROP INDEX public.ix_aspnet_role_claims_role_id; 59 | 60 | CREATE INDEX ix_aspnet_role_claims_role_id 61 | ON public.aspnet_role_claims USING btree 62 | (role_id COLLATE pg_catalog."default") 63 | TABLESPACE pg_default; 64 | 65 | -- Table: public.aspnet_users 66 | 67 | -- DROP TABLE public.aspnet_users; 68 | 69 | CREATE TABLE public.aspnet_users 70 | ( 71 | id character(32) COLLATE pg_catalog."default" NOT NULL, 72 | user_name character varying(64) COLLATE pg_catalog."default" NOT NULL, 73 | normalized_user_name character varying(64) COLLATE pg_catalog."default" NOT NULL, 74 | email character varying(256) COLLATE pg_catalog."default" NOT NULL, 75 | normalized_email character varying(256) COLLATE pg_catalog."default" NOT NULL, 76 | email_confirmed boolean NOT NULL, 77 | phone_number character varying(32) COLLATE pg_catalog."default", 78 | phone_number_confirmed boolean NOT NULL, 79 | lockout_enabled boolean NOT NULL, 80 | lockout_end_unix_time_seconds bigint, 81 | password_hash character varying(256) COLLATE pg_catalog."default", 82 | access_failed_count integer NOT NULL, 83 | security_stamp character varying(256) COLLATE pg_catalog."default", 84 | two_factor_enabled boolean NOT NULL, 85 | concurrency_stamp character(36) COLLATE pg_catalog."default", 86 | CONSTRAINT pk_aspnet_users PRIMARY KEY (id), 87 | CONSTRAINT u_aspnet_users_normalized_user_name UNIQUE (normalized_user_name), 88 | CONSTRAINT u_aspnet_users_username UNIQUE (user_name) 89 | ) 90 | WITH ( 91 | OIDS = FALSE 92 | ) 93 | TABLESPACE pg_default; 94 | 95 | ALTER TABLE public.aspnet_users 96 | OWNER to postgres; 97 | 98 | -- Index: ix_aspnet_users_email 99 | 100 | -- DROP INDEX public.ix_aspnet_users_email; 101 | 102 | CREATE INDEX ix_aspnet_users_email 103 | ON public.aspnet_users USING btree 104 | (normalized_email COLLATE pg_catalog."default") 105 | TABLESPACE pg_default; 106 | 107 | -- Index: ix_aspnet_users_user_name 108 | 109 | -- DROP INDEX public.ix_aspnet_users_user_name; 110 | 111 | CREATE INDEX ix_aspnet_users_user_name 112 | ON public.aspnet_users USING btree 113 | (normalized_user_name COLLATE pg_catalog."default") 114 | TABLESPACE pg_default; 115 | 116 | -- Table: public.aspnet_user_claims 117 | 118 | -- DROP TABLE public.aspnet_user_claims; 119 | 120 | CREATE TABLE public.aspnet_user_claims 121 | ( 122 | id serial NOT NULL, 123 | user_id character(32) COLLATE pg_catalog."default" NOT NULL, 124 | claim_type character varying(1024) COLLATE pg_catalog."default" NOT NULL, 125 | claim_value character varying(1024) COLLATE pg_catalog."default" NOT NULL, 126 | CONSTRAINT pk_aspnet_user_claims PRIMARY KEY (id), 127 | CONSTRAINT fk_aspnet_user_claims_user_id FOREIGN KEY (user_id) 128 | REFERENCES public.aspnet_users (id) MATCH SIMPLE 129 | ON UPDATE CASCADE 130 | ON DELETE CASCADE 131 | ) 132 | WITH ( 133 | OIDS = FALSE 134 | ) 135 | TABLESPACE pg_default; 136 | 137 | ALTER TABLE public.aspnet_user_claims 138 | OWNER to postgres; 139 | 140 | -- Index: ix_aspnet_user_claims_user_id 141 | 142 | -- DROP INDEX public.ix_aspnet_user_claims_user_id; 143 | 144 | CREATE INDEX ix_aspnet_user_claims_user_id 145 | ON public.aspnet_user_claims USING btree 146 | (user_id COLLATE pg_catalog."default") 147 | TABLESPACE pg_default; 148 | 149 | -- Table: public.aspnet_user_logins 150 | 151 | -- DROP TABLE public.aspnet_user_logins; 152 | 153 | CREATE TABLE public.aspnet_user_logins 154 | ( 155 | login_provider character varying(32) COLLATE pg_catalog."default" NOT NULL, 156 | provider_key character varying(1024) COLLATE pg_catalog."default" NOT NULL, 157 | provider_display_name character varying(32) COLLATE pg_catalog."default" NOT NULL, 158 | user_id character(32) COLLATE pg_catalog."default" NOT NULL, 159 | CONSTRAINT pk_aspnet_user_logins PRIMARY KEY (login_provider, provider_key), 160 | CONSTRAINT fk_aspnet_user_logins_user_id FOREIGN KEY (user_id) 161 | REFERENCES public.aspnet_users (id) MATCH SIMPLE 162 | ON UPDATE CASCADE 163 | ON DELETE CASCADE 164 | ) 165 | WITH ( 166 | OIDS = FALSE 167 | ) 168 | TABLESPACE pg_default; 169 | 170 | ALTER TABLE public.aspnet_user_logins 171 | OWNER to postgres; 172 | 173 | -- Index: ix_aspnet_user_logins_user_id 174 | 175 | -- DROP INDEX public.ix_aspnet_user_logins_user_id; 176 | 177 | CREATE INDEX ix_aspnet_user_logins_user_id 178 | ON public.aspnet_user_logins USING btree 179 | (user_id COLLATE pg_catalog."default") 180 | TABLESPACE pg_default; 181 | 182 | -- Table: public.aspnet_user_roles 183 | 184 | -- DROP TABLE public.aspnet_user_roles; 185 | 186 | CREATE TABLE public.aspnet_user_roles 187 | ( 188 | user_id character(32) COLLATE pg_catalog."default" NOT NULL, 189 | role_id character(32) COLLATE pg_catalog."default" NOT NULL, 190 | CONSTRAINT pk_aspnet_user_roles PRIMARY KEY (user_id, role_id), 191 | CONSTRAINT fk_aspnet_user_roles_role_id FOREIGN KEY (role_id) 192 | REFERENCES public.aspnet_roles (id) MATCH SIMPLE 193 | ON UPDATE NO ACTION 194 | ON DELETE CASCADE, 195 | CONSTRAINT fk_aspnet_user_roles_user_id FOREIGN KEY (user_id) 196 | REFERENCES public.aspnet_users (id) MATCH SIMPLE 197 | ON UPDATE NO ACTION 198 | ON DELETE CASCADE 199 | ) 200 | WITH ( 201 | OIDS = FALSE 202 | ) 203 | TABLESPACE pg_default; 204 | 205 | ALTER TABLE public.aspnet_user_roles 206 | OWNER to postgres; 207 | 208 | -- Index: ix_aspnet_user_roles_role_id 209 | 210 | -- DROP INDEX public.ix_aspnet_user_roles_role_id; 211 | 212 | CREATE INDEX ix_aspnet_user_roles_role_id 213 | ON public.aspnet_user_roles USING btree 214 | (role_id COLLATE pg_catalog."default") 215 | TABLESPACE pg_default; 216 | 217 | -- Index: ix_aspnet_user_roles_user_id 218 | 219 | -- DROP INDEX public.ix_aspnet_user_roles_user_id; 220 | 221 | CREATE INDEX ix_aspnet_user_roles_user_id 222 | ON public.aspnet_user_roles USING btree 223 | (user_id COLLATE pg_catalog."default") 224 | TABLESPACE pg_default; 225 | 226 | -- Table: public.aspnet_user_tokens 227 | 228 | -- DROP TABLE public.aspnet_user_tokens; 229 | 230 | CREATE TABLE public.aspnet_user_tokens 231 | ( 232 | user_id character(32) COLLATE pg_catalog."default" NOT NULL, 233 | login_provider character varying(32) COLLATE pg_catalog."default" NOT NULL, 234 | name character varying(32) COLLATE pg_catalog."default" NOT NULL, 235 | value character varying(256) COLLATE pg_catalog."default", 236 | CONSTRAINT pk_aspnet_user_tokens PRIMARY KEY (user_id, login_provider, name), 237 | CONSTRAINT fk_aspnet_user_tokens_user_id FOREIGN KEY (user_id) 238 | REFERENCES public.aspnet_users (id) MATCH SIMPLE 239 | ON UPDATE CASCADE 240 | ON DELETE CASCADE 241 | ) 242 | WITH ( 243 | OIDS = FALSE 244 | ) 245 | TABLESPACE pg_default; 246 | 247 | ALTER TABLE public.aspnet_user_tokens 248 | OWNER to postgres; 249 | 250 | -- Index: ix_aspnet_user_tokens_user_id 251 | 252 | -- DROP INDEX public.ix_aspnet_user_tokens_user_id; 253 | 254 | CREATE INDEX ix_aspnet_user_tokens_user_id 255 | ON public.aspnet_user_tokens USING btree 256 | (user_id COLLATE pg_catalog."default") 257 | TABLESPACE pg_default; 258 | -------------------------------------------------------------------------------- /database/up.md: -------------------------------------------------------------------------------- 1 | ```c# 2 | protected override void Up(MigrationBuilder migrationBuilder) 3 | { 4 | migrationBuilder.CreateTable( 5 | name: "AspNetRoles", 6 | columns: table => new 7 | { 8 | Id = table.Column(nullable: false), 9 | ConcurrencyStamp = table.Column(nullable: true), 10 | Name = table.Column(maxLength: 256, nullable: true), 11 | NormalizedName = table.Column(maxLength: 256, nullable: true) 12 | }, 13 | constraints: table => 14 | { 15 | table.PrimaryKey("PK_AspNetRoles", x => x.Id); 16 | }); 17 | 18 | migrationBuilder.CreateTable( 19 | name: "AspNetUserTokens", 20 | columns: table => new 21 | { 22 | UserId = table.Column(nullable: false), 23 | LoginProvider = table.Column(nullable: false), 24 | Name = table.Column(nullable: false), 25 | Value = table.Column(nullable: true) 26 | }, 27 | constraints: table => 28 | { 29 | table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); 30 | }); 31 | 32 | migrationBuilder.CreateTable( 33 | name: "AspNetUsers", 34 | columns: table => new 35 | { 36 | Id = table.Column(nullable: false), 37 | AccessFailedCount = table.Column(nullable: false), 38 | ConcurrencyStamp = table.Column(nullable: true), 39 | Email = table.Column(maxLength: 256, nullable: true), 40 | EmailConfirmed = table.Column(nullable: false), 41 | LockoutEnabled = table.Column(nullable: false), 42 | LockoutEnd = table.Column(nullable: true), 43 | NormalizedEmail = table.Column(maxLength: 256, nullable: true), 44 | NormalizedUserName = table.Column(maxLength: 256, nullable: true), 45 | PasswordHash = table.Column(nullable: true), 46 | PhoneNumber = table.Column(nullable: true), 47 | PhoneNumberConfirmed = table.Column(nullable: false), 48 | SecurityStamp = table.Column(nullable: true), 49 | TwoFactorEnabled = table.Column(nullable: false), 50 | UserName = table.Column(maxLength: 256, nullable: true) 51 | }, 52 | constraints: table => 53 | { 54 | table.PrimaryKey("PK_AspNetUsers", x => x.Id); 55 | }); 56 | 57 | migrationBuilder.CreateTable( 58 | name: "AspNetRoleClaims", 59 | columns: table => new 60 | { 61 | Id = table.Column(nullable: false) 62 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 63 | ClaimType = table.Column(nullable: true), 64 | ClaimValue = table.Column(nullable: true), 65 | RoleId = table.Column(nullable: false) 66 | }, 67 | constraints: table => 68 | { 69 | table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); 70 | table.ForeignKey( 71 | name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", 72 | column: x => x.RoleId, 73 | principalTable: "AspNetRoles", 74 | principalColumn: "Id", 75 | onDelete: ReferentialAction.Cascade); 76 | }); 77 | 78 | migrationBuilder.CreateTable( 79 | name: "AspNetUserClaims", 80 | columns: table => new 81 | { 82 | Id = table.Column(nullable: false) 83 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 84 | ClaimType = table.Column(nullable: true), 85 | ClaimValue = table.Column(nullable: true), 86 | UserId = table.Column(nullable: false) 87 | }, 88 | constraints: table => 89 | { 90 | table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); 91 | table.ForeignKey( 92 | name: "FK_AspNetUserClaims_AspNetUsers_UserId", 93 | column: x => x.UserId, 94 | principalTable: "AspNetUsers", 95 | principalColumn: "Id", 96 | onDelete: ReferentialAction.Cascade); 97 | }); 98 | 99 | migrationBuilder.CreateTable( 100 | name: "AspNetUserLogins", 101 | columns: table => new 102 | { 103 | LoginProvider = table.Column(nullable: false), 104 | ProviderKey = table.Column(nullable: false), 105 | ProviderDisplayName = table.Column(nullable: true), 106 | UserId = table.Column(nullable: false) 107 | }, 108 | constraints: table => 109 | { 110 | table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); 111 | table.ForeignKey( 112 | name: "FK_AspNetUserLogins_AspNetUsers_UserId", 113 | column: x => x.UserId, 114 | principalTable: "AspNetUsers", 115 | principalColumn: "Id", 116 | onDelete: ReferentialAction.Cascade); 117 | }); 118 | 119 | migrationBuilder.CreateTable( 120 | name: "AspNetUserRoles", 121 | columns: table => new 122 | { 123 | UserId = table.Column(nullable: false), 124 | RoleId = table.Column(nullable: false) 125 | }, 126 | constraints: table => 127 | { 128 | table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); 129 | table.ForeignKey( 130 | name: "FK_AspNetUserRoles_AspNetRoles_RoleId", 131 | column: x => x.RoleId, 132 | principalTable: "AspNetRoles", 133 | principalColumn: "Id", 134 | onDelete: ReferentialAction.Cascade); 135 | table.ForeignKey( 136 | name: "FK_AspNetUserRoles_AspNetUsers_UserId", 137 | column: x => x.UserId, 138 | principalTable: "AspNetUsers", 139 | principalColumn: "Id", 140 | onDelete: ReferentialAction.Cascade); 141 | }); 142 | 143 | migrationBuilder.CreateIndex( 144 | name: "RoleNameIndex", 145 | table: "AspNetRoles", 146 | column: "NormalizedName"); 147 | 148 | migrationBuilder.CreateIndex( 149 | name: "IX_AspNetRoleClaims_RoleId", 150 | table: "AspNetRoleClaims", 151 | column: "RoleId"); 152 | 153 | migrationBuilder.CreateIndex( 154 | name: "IX_AspNetUserClaims_UserId", 155 | table: "AspNetUserClaims", 156 | column: "UserId"); 157 | 158 | migrationBuilder.CreateIndex( 159 | name: "IX_AspNetUserLogins_UserId", 160 | table: "AspNetUserLogins", 161 | column: "UserId"); 162 | 163 | migrationBuilder.CreateIndex( 164 | name: "IX_AspNetUserRoles_RoleId", 165 | table: "AspNetUserRoles", 166 | column: "RoleId"); 167 | 168 | migrationBuilder.CreateIndex( 169 | name: "IX_AspNetUserRoles_UserId", 170 | table: "AspNetUserRoles", 171 | column: "UserId"); 172 | 173 | migrationBuilder.CreateIndex( 174 | name: "EmailIndex", 175 | table: "AspNetUsers", 176 | column: "NormalizedEmail"); 177 | 178 | migrationBuilder.CreateIndex( 179 | name: "UserNameIndex", 180 | table: "AspNetUsers", 181 | column: "NormalizedUserName", 182 | unique: true); 183 | } 184 | ``` 185 | -------------------------------------------------------------------------------- /push-package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | export PACKAGE_VERSION="9.0.3" 3 | dotnet pack src/NHibernate.NetCore/NHibernate.NetCore.csproj -c Release 4 | dotnet nuget push src/NHibernate.NetCore/bin/Release/NHibernate.NetCore.$PACKAGE_VERSION.nupkg -s nuget.org -k $(cat ~/.nuget/key.txt) 5 | rm -rf src/NHibernate.NetCore/bin/Release 6 | 7 | dotnet pack src/NHibernate.Extensions.Npgsql/NHibernate.Extensions.Npgsql.csproj -c Release 8 | dotnet nuget push src/NHibernate.Extensions.Npgsql/bin/Release/NHibernate.Extensions.Npgsql.$PACKAGE_VERSION.nupkg -s nuget.org -k $(cat ~/.nuget/key.txt) 9 | rm -rf src/NHibernate.Extensions.Npgsql/bin/Release 10 | 11 | dotnet pack src/NHibernate.Extensions.Sqlite/NHibernate.Extensions.Sqlite.csproj -c Release 12 | dotnet nuget push src/NHibernate.Extensions.Sqlite/bin/Release/NHibernate.Extensions.Sqlite.$PACKAGE_VERSION.nupkg -s nuget.org -k $(cat ~/.nuget/key.txt) 13 | rm -rf src/NHibernate.Extensions.Sqlite/bin/Release 14 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace NHibernate.Extensions.Npgsql; 4 | 5 | public static class ArrayExtensions { 6 | 7 | public static bool ArrayContains(this T[] array, T element) { 8 | return array.Contains(element); 9 | } 10 | 11 | public static bool ArrayIntersects(this T[] array, T[] other) { 12 | return array.Intersect(other).Any(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/ArrayTypeUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using NpgsqlTypes; 5 | 6 | namespace NHibernate.Extensions.Npgsql; 7 | 8 | public static class ArrayTypeUtil { 9 | 10 | public static readonly IDictionary KnownTypes 11 | = new ConcurrentDictionary { 12 | [typeof(bool)] = NpgsqlDbType.Boolean, 13 | [typeof(short)] = NpgsqlDbType.Smallint, 14 | [typeof(int)] = NpgsqlDbType.Integer, 15 | [typeof(long)] = NpgsqlDbType.Bigint, 16 | [typeof(float)] = NpgsqlDbType.Real, 17 | [typeof(double)] = NpgsqlDbType.Double, 18 | [typeof(decimal)] = NpgsqlDbType.Numeric, 19 | [typeof(string)] = NpgsqlDbType.Varchar, 20 | [typeof(DateTime)] = NpgsqlDbType.Timestamp, 21 | [typeof(DateTimeOffset)] = NpgsqlDbType.TimestampTz, 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/Generators/ArrayHqlGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using NHibernate.Hql.Ast; 7 | using NHibernate.Linq.Functions; 8 | using NHibernate.Linq.Visitors; 9 | using NHibernate.Util; 10 | 11 | namespace NHibernate.Extensions.Npgsql.Generators; 12 | 13 | public class ArrayHqlGenerator : BaseHqlGeneratorForMethod { 14 | 15 | public ArrayHqlGenerator() { 16 | SupportedMethods = [ 17 | ReflectHelper.GetMethodDefinition(x => x.ArrayContains(0)), 18 | ReflectHelper.GetMethodDefinition(x => x.ArrayIntersects(Array.Empty())), 19 | ]; 20 | } 21 | 22 | public override HqlTreeNode BuildHql( 23 | MethodInfo method, 24 | Expression targetObject, 25 | ReadOnlyCollection arguments, 26 | HqlTreeBuilder treeBuilder, 27 | IHqlExpressionVisitor visitor 28 | ) { 29 | var hqlMethod = ""; 30 | var linqMethod = method.Name; 31 | hqlMethod = linqMethod switch { 32 | "ArrayContains" => "array_contains", 33 | "ArrayIntersects" => "array_intersects", 34 | _ => hqlMethod 35 | }; 36 | if (string.IsNullOrEmpty(hqlMethod)) { 37 | throw new HibernateException($"Method {method.Name} not found"); 38 | } 39 | return treeBuilder.BooleanMethodCall( 40 | hqlMethod, 41 | arguments.Select(visitor.Visit).Cast() 42 | ); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/LinqToHqlGeneratorsRegistry.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Linq.Functions; 2 | 3 | using NHibernate.Extensions.Npgsql.Generators; 4 | 5 | namespace NHibernate.Extensions.Npgsql; 6 | 7 | public class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry { 8 | 9 | public LinqToHqlGeneratorsRegistry() { 10 | this.Merge(new ArrayHqlGenerator()); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/NHibernate.Extensions.Npgsql.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | NHibernate.Extensions.Npgsql 7 | beginor 8 | Extended PostgreSQL driver for NHibernate, with support of json and array types. 9 | NHibernate.Extensions.Npgsql 10 | $(PackageVersion) 11 | $(PackageReleaseNotes) 12 | $(PackageTags) 13 | $(PackageProjectUrl) 14 | NHibernate.Extensions.Npgsql 15 | NHibernate.Extensions.Npgsql 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/NpgsqlDialect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using NHibernate.Dialect.Function; 4 | using NHibernate.SqlTypes; 5 | using NHibernate.Type; 6 | using NpgsqlTypes; 7 | using NHibernate.Extensions.Npgsql.UserTypes; 8 | 9 | namespace NHibernate.Extensions.Npgsql; 10 | 11 | public class NpgsqlDialect : NHibernate.Dialect.PostgreSQL83Dialect { 12 | 13 | public NpgsqlDialect() { 14 | RegisterFunctions(); 15 | RegisterUserTypes(); 16 | } 17 | 18 | public override string GetTypeName( 19 | SqlType sqlType 20 | ) { 21 | if (sqlType is NpgsqlType npgSqlType) { 22 | var npgDbType = npgSqlType.NpgDbType; 23 | if (npgDbType == NpgsqlDbType.Numeric) { 24 | return "numeric"; 25 | } 26 | if (npgDbType == NpgsqlDbType.Json) { 27 | return "json"; 28 | } 29 | if (npgDbType == NpgsqlDbType.Jsonb) { 30 | return "jsonb"; 31 | } 32 | // array type 33 | if (npgDbType < 0) { 34 | var arrayDbType = (NpgsqlDbType) (npgDbType - NpgsqlDbType.Array); 35 | string type = arrayDbType switch { 36 | NpgsqlDbType.Boolean => "boolean[]", 37 | NpgsqlDbType.Smallint => "smallint[]", 38 | NpgsqlDbType.Integer => "integer[]", 39 | NpgsqlDbType.Bigint => "bigint[]", 40 | NpgsqlDbType.Real => "real[]", 41 | NpgsqlDbType.Double => "double precision[]", 42 | NpgsqlDbType.Numeric => "numeric[]", 43 | NpgsqlDbType.Varchar => "character varying[]", 44 | NpgsqlDbType.Timestamp => "timestamp[]", 45 | NpgsqlDbType.TimestampTz => "timestamptz[]", 46 | _ => "" 47 | }; 48 | return type; 49 | } 50 | } 51 | return base.GetTypeName(sqlType); 52 | } 53 | 54 | private void RegisterFunctions() { 55 | // array_contains(arr, 3) => :num = any(arr) 56 | RegisterFunction("array_contains", new SQLFunctionTemplate(NHibernateUtil.Boolean, "?2 = any(?1)")); 57 | // array_intersects => ?1 && ?2 58 | RegisterFunction("array_intersects", new SQLFunctionTemplate(NHibernateUtil.Boolean, "?1 && ?2")); 59 | } 60 | 61 | private void RegisterUserTypes() { 62 | TypeFactory.RegisterType(typeof(bool[]), NHibernateUtil.Custom(typeof(ArrayType)), ["bool[]"]); 63 | TypeFactory.RegisterType(typeof(short[]), NHibernateUtil.Custom(typeof(ArrayType)), ["short[]"]); 64 | TypeFactory.RegisterType(typeof(int[]), NHibernateUtil.Custom(typeof(ArrayType)), ["int[]"]); 65 | TypeFactory.RegisterType(typeof(long[]), NHibernateUtil.Custom(typeof(ArrayType)), ["long[]"]); 66 | TypeFactory.RegisterType(typeof(float[]), NHibernateUtil.Custom(typeof(ArrayType)), ["float[]"]); 67 | TypeFactory.RegisterType(typeof(double[]), NHibernateUtil.Custom(typeof(ArrayType)), ["double[]"]); 68 | TypeFactory.RegisterType(typeof(decimal[]), NHibernateUtil.Custom(typeof(ArrayType)), ["decimal[]"]); 69 | TypeFactory.RegisterType(typeof(string[]), NHibernateUtil.Custom(typeof(ArrayType)), ["string[]"]); 70 | TypeFactory.RegisterType(typeof(DateTime[]), NHibernateUtil.Custom(typeof(ArrayType)), ["datetime[]", "timestamp[]"]); 71 | TypeFactory.RegisterType(typeof(DateTimeOffset[]), NHibernateUtil.Custom(typeof(ArrayType)), ["datetimeoffset[]", "timestamptz[]"]); 72 | 73 | TypeFactory.RegisterType(typeof(JsonElement), NHibernateUtil.Custom(typeof(JsonbType)), ["jsonb"]); 74 | TypeFactory.RegisterType(typeof(JsonElement), NHibernateUtil.Custom(typeof(JsonType)), ["json"]); 75 | TypeFactory.RegisterType(typeof(DateTime), NHibernateUtil.Custom(typeof(TimeStampType)), ["timestamp"]); 76 | TypeFactory.RegisterType(typeof(DateTimeOffset), NHibernateUtil.Custom(typeof(TimeStampTzType)), ["timestamptz"]); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/NpgsqlDriver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data.Common; 3 | using NHibernate.Cfg; 4 | using NHibernate.SqlTypes; 5 | using Npgsql; 6 | 7 | namespace NHibernate.Extensions.Npgsql; 8 | 9 | public class NpgsqlDriver : NHibernate.Driver.NpgsqlDriver { 10 | 11 | private NpgsqlDataSource? dataSource; 12 | 13 | public override void Configure( 14 | IDictionary settings 15 | ) { 16 | base.Configure(settings); 17 | var connectionString = settings["connection.connection_string"]; 18 | if (string.IsNullOrEmpty(connectionString)) { 19 | throw new HibernateConfigException("connection.connection_string is not set!"); 20 | } 21 | var builder = new NpgsqlDataSourceBuilder(connectionString); 22 | builder.EnableDynamicJson(); 23 | dataSource = NpgsqlDataSource.Create(connectionString); 24 | } 25 | 26 | protected override void InitializeParameter(DbParameter dbParam, string name, SqlType sqlType) { 27 | if (sqlType is NpgsqlType type && dbParam is NpgsqlParameter parameter) { 28 | InitializeParameter(parameter, name, type); 29 | } 30 | else { 31 | base.InitializeParameter(dbParam, name, sqlType); 32 | } 33 | } 34 | 35 | protected virtual void InitializeParameter(NpgsqlParameter dbParam, string name, NpgsqlType sqlType) { 36 | if (sqlType == null) { 37 | throw new QueryException($"No type assigned to parameter '{name}'"); 38 | } 39 | dbParam.ParameterName = FormatNameForParameter(name); 40 | dbParam.DbType = sqlType.DbType; 41 | dbParam.NpgsqlDbType = sqlType.NpgDbType; 42 | } 43 | 44 | public override DbConnection CreateConnection() { 45 | if (dataSource == null) { 46 | throw new HibernateException("dataSource is not created!"); 47 | } 48 | return dataSource.CreateConnection(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/NpgsqlType.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using NHibernate.SqlTypes; 3 | using NpgsqlTypes; 4 | 5 | namespace NHibernate.Extensions.Npgsql; 6 | 7 | public class NpgsqlType : SqlType { 8 | 9 | public NpgsqlDbType NpgDbType { get; } 10 | 11 | public NpgsqlType(DbType dbType, NpgsqlDbType npgDbType) 12 | : base(dbType) { 13 | NpgDbType = npgDbType; 14 | } 15 | 16 | public NpgsqlType(DbType dbType, NpgsqlDbType npgDbType, int length) 17 | : base(dbType, length) { 18 | NpgDbType = npgDbType; 19 | } 20 | 21 | public NpgsqlType(DbType dbType, NpgsqlDbType npgDbType, byte precision, byte scale) 22 | : base(dbType, precision, scale) { 23 | NpgDbType = npgDbType; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/UserTypes/ArrayType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using System.Linq; 5 | using NHibernate.Engine; 6 | using NHibernate.SqlTypes; 7 | using NHibernate.UserTypes; 8 | using Npgsql; 9 | using NpgsqlTypes; 10 | 11 | namespace NHibernate.Extensions.Npgsql.UserTypes; 12 | 13 | public class ArrayType : IUserType { 14 | 15 | public SqlType[] SqlTypes => [GetNpgsqlType()]; 16 | 17 | public System.Type ReturnedType => typeof(T[]); 18 | 19 | public bool IsMutable => false; 20 | 21 | public object Assemble(object cached, object owner) { 22 | return cached; 23 | } 24 | 25 | public object? DeepCopy(object value) { 26 | if (!(value is T[] arr)) { 27 | return null; 28 | } 29 | var result = new T[arr.Length]; 30 | Array.Copy(arr, result, arr.Length); 31 | return result; 32 | } 33 | 34 | public object Disassemble(object value) { 35 | return value; 36 | } 37 | 38 | public new bool Equals(object? x, object? y) { 39 | if (x == null && y == null) { 40 | return true; 41 | } 42 | if (x == null || y == null) { 43 | return false; 44 | } 45 | return ((T[])x).Equals((T[])y); 46 | } 47 | 48 | public int GetHashCode(object? x) { 49 | return x == null ? 0 : x.GetHashCode(); 50 | } 51 | 52 | public object? NullSafeGet( 53 | DbDataReader rs, 54 | string[] names, 55 | ISessionImplementor session, 56 | object owner 57 | ) { 58 | if (names.Length != 1) { 59 | throw new InvalidOperationException("Only expecting one column..."); 60 | } 61 | return rs[names[0]] as T[]; 62 | } 63 | 64 | public void NullSafeSet( 65 | DbCommand cmd, 66 | object? value, 67 | int index, 68 | ISessionImplementor session 69 | ) { 70 | var parameter = (NpgsqlParameter)cmd.Parameters[index]; 71 | if (value == null) { 72 | parameter.Value = DBNull.Value; 73 | } 74 | else { 75 | parameter.NpgsqlDbType = GetNpgsqlType().NpgDbType; 76 | if (!(value is T[] arr)) { 77 | throw new InvalidOperationException( 78 | $"\"{parameter.ParameterName}\" is not {typeof(T)}[]" 79 | ); 80 | } 81 | parameter.Value = arr; 82 | } 83 | } 84 | 85 | public object Replace(object original, object target, object owner) { 86 | return original; 87 | } 88 | 89 | protected virtual NpgsqlType GetNpgsqlType() { 90 | var type = typeof(T); 91 | if (!ArrayTypeUtil.KnownTypes.TryGetValue(type, out var dbType)) { 92 | throw new NotSupportedException($"Unknown type {typeof(T)}"); 93 | } 94 | return new NpgsqlType( 95 | DbType.Object, 96 | // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags 97 | NpgsqlDbType.Array | dbType 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/UserTypes/JsonType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using System.Text.Json; 5 | using NHibernate.Engine; 6 | using NHibernate.SqlTypes; 7 | using NHibernate.UserTypes; 8 | using Npgsql; 9 | using NpgsqlTypes; 10 | 11 | namespace NHibernate.Extensions.Npgsql.UserTypes; 12 | 13 | public class JsonType : IUserType { 14 | 15 | public virtual SqlType[] SqlTypes => [ 16 | new NpgsqlType(DbType.Object, NpgsqlDbType.Json) 17 | ]; 18 | 19 | public System.Type ReturnedType => typeof(T); 20 | 21 | public bool IsMutable => true; 22 | 23 | public object Assemble(object cached, object owner) { 24 | return cached; 25 | } 26 | 27 | public object? DeepCopy(object? value) { 28 | if (value == null) { 29 | return null; 30 | } 31 | var json = JsonSerializer.Serialize(value); 32 | var obj = JsonSerializer.Deserialize(json, value.GetType()); 33 | return obj; 34 | } 35 | 36 | public object Disassemble(object value) { 37 | return value; 38 | } 39 | 40 | public new bool Equals(object? x, object? y) { 41 | if (x == null && y == null) { 42 | return true; 43 | } 44 | if (x == null || y == null) { 45 | return false; 46 | } 47 | return ((T)x).Equals((T)y); 48 | } 49 | 50 | public int GetHashCode(object? x) { 51 | return x == null ? 0 : x.GetHashCode(); 52 | } 53 | 54 | public object? NullSafeGet( 55 | DbDataReader rs, 56 | string[] names, 57 | ISessionImplementor session, 58 | object owner 59 | ) { 60 | if (names.Length != 1) { 61 | throw new InvalidOperationException("Only expecting one column..."); 62 | } 63 | var type = typeof(T); 64 | var name = names[0]; 65 | if (rs.IsDBNull(name)) { 66 | return default(T); 67 | } 68 | var jsonStr = rs.GetString(name); 69 | var jsonDoc = JsonDocument.Parse(jsonStr); 70 | if (type == typeof(JsonDocument)) { 71 | return jsonDoc; 72 | } 73 | if (type == typeof(JsonElement)) { 74 | return jsonDoc.RootElement; 75 | } 76 | return JsonSerializer.Deserialize(jsonStr); 77 | } 78 | 79 | public void NullSafeSet( 80 | DbCommand cmd, 81 | object? value, 82 | int index, 83 | ISessionImplementor session 84 | ) { 85 | var parameter = (NpgsqlParameter)cmd.Parameters[index]; 86 | if (value == null) { 87 | parameter.Value = DBNull.Value; 88 | return; 89 | } 90 | if (!value.GetType().IsAssignableTo(typeof(T))) { 91 | throw new InvalidOperationException($"{value.GetType()} is not assignable to {typeof(T)}"); 92 | } 93 | var type = typeof(T); 94 | if (type == typeof(JsonElement) || type == typeof(JsonDocument)) { 95 | parameter.Value = value; 96 | return; 97 | } 98 | var valueType = value.GetType(); 99 | var json = JsonSerializer.Serialize(value, valueType); 100 | parameter.Value = json; 101 | } 102 | 103 | public object Replace(object original, object target, object owner) { 104 | return original; 105 | } 106 | 107 | } 108 | 109 | public class JsonbType : JsonType { 110 | 111 | public override SqlType[] SqlTypes => new SqlType[] { 112 | new NpgsqlType(DbType.Binary, NpgsqlDbType.Jsonb) 113 | }; 114 | 115 | } 116 | 117 | public class JsonType : JsonType { } 118 | 119 | public class JsonbType : JsonbType { } 120 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/UserTypes/TimeStampType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using NHibernate.Engine; 5 | using NHibernate.SqlTypes; 6 | using NHibernate.UserTypes; 7 | using Npgsql; 8 | using NpgsqlTypes; 9 | 10 | namespace NHibernate.Extensions.Npgsql.UserTypes; 11 | 12 | public class TimeStampType : IUserType { 13 | 14 | public virtual SqlType[] SqlTypes => [ 15 | new NpgsqlType(DbType.DateTime, NpgsqlDbType.Timestamp) 16 | ]; 17 | 18 | public System.Type ReturnedType => typeof(DateTime); 19 | 20 | public bool IsMutable => true; 21 | 22 | public object Assemble(object cached, object owner) { 23 | return cached; 24 | } 25 | 26 | public object? DeepCopy(object? value) { 27 | if (value == null) { 28 | return null; 29 | } 30 | var val = (DateTime)value; 31 | var result = DateTime.FromBinary(val.ToBinary()); 32 | return result; 33 | } 34 | 35 | public object Disassemble(object value) { 36 | return value; 37 | } 38 | 39 | public new bool Equals(object? x, object? y) { 40 | if (x == null && y == null) { 41 | return true; 42 | } 43 | if (x == null || y == null) { 44 | return false; 45 | } 46 | return ((DateTime)x).Equals((DateTime)y); 47 | } 48 | 49 | public int GetHashCode(object? x) { 50 | return x == null ? 0 : x.GetHashCode(); 51 | } 52 | 53 | public object? NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner) { 54 | if (names.Length != 1) { 55 | throw new InvalidOperationException("Only expecting one column..."); 56 | } 57 | var name = names[0]; 58 | if (rs.IsDBNull(name)) { 59 | return null; 60 | } 61 | return rs.GetDateTime(name); 62 | } 63 | 64 | public void NullSafeSet(DbCommand cmd, object? value, int index, ISessionImplementor session) { 65 | var parameter = (NpgsqlParameter)cmd.Parameters[index]; 66 | parameter.Value = value ?? DBNull.Value; 67 | parameter.NpgsqlDbType = NpgsqlDbType.Timestamp; 68 | } 69 | 70 | public object Replace(object original, object target, object owner) { 71 | return original; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Npgsql/UserTypes/TimeStampTzType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using System.Data.Common; 4 | using NHibernate.Engine; 5 | using NHibernate.SqlTypes; 6 | using NHibernate.UserTypes; 7 | using Npgsql; 8 | using NpgsqlTypes; 9 | 10 | namespace NHibernate.Extensions.Npgsql.UserTypes; 11 | 12 | public class TimeStampTzType : IUserType { 13 | 14 | public virtual SqlType[] SqlTypes => [ 15 | new NpgsqlType(DbType.DateTime, NpgsqlDbType.TimestampTz) 16 | ]; 17 | 18 | public System.Type ReturnedType => typeof(DateTime); 19 | 20 | public bool IsMutable => true; 21 | 22 | public object Assemble(object cached, object owner) { 23 | return cached; 24 | } 25 | 26 | public object? DeepCopy(object? value) { 27 | if (value == null) { 28 | return null; 29 | } 30 | var val = (DateTime)value; 31 | var result = DateTime.FromBinary(val.ToBinary()); 32 | return result; 33 | } 34 | 35 | public object Disassemble(object value) { 36 | return value; 37 | } 38 | 39 | public new bool Equals(object? x, object? y) { 40 | if (x == null && y == null) { 41 | return true; 42 | } 43 | if (x == null || y == null) { 44 | return false; 45 | } 46 | return ((DateTime)x).Equals((DateTime)y); 47 | } 48 | 49 | public int GetHashCode(object? x) { 50 | return x == null ? 0 : x.GetHashCode(); 51 | } 52 | 53 | public object? NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner) { 54 | if (names.Length != 1) { 55 | throw new InvalidOperationException("Only expecting one column..."); 56 | } 57 | var name = names[0]; 58 | if (rs.IsDBNull(name)) { 59 | return null; 60 | } 61 | return rs.GetDateTime(name); 62 | } 63 | 64 | public void NullSafeSet(DbCommand cmd, object? value, int index, ISessionImplementor session) { 65 | var parameter = (NpgsqlParameter)cmd.Parameters[index]; 66 | parameter.Value = value ?? DBNull.Value; 67 | parameter.NpgsqlDbType = NpgsqlDbType.TimestampTz; 68 | } 69 | 70 | public object Replace(object original, object target, object owner) { 71 | return original; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Sqlite/NHibernate.Extensions.Sqlite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | NHibernate.Extensions.Sqlite 7 | beginor 8 | NHibernate Sqlite Driver with Microsoft.Data.Sqlite.Core. In order to use this driver, you must have the nuget package [Microsoft.Data.Sqlite.Core](https://www.nuget.org/packages/Microsoft.Data.Sqlite.Core/) and [SQLitePCLRaw.bundle_e_sqlite3](https://www.nuget.org/packages/SQLitePCLRaw.bundle_e_sqlite3/) installed for NHibernate to load. It's a .NET Standard 2.0 Driver, Can run on any platform that dotnet core support. 9 | NHibernate.Extensions.Sqlite 10 | $(PackageVersion) 11 | $(PackageReleaseNotes) 12 | $(PackageTags) 13 | $(PackageProjectUrl) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Sqlite/SqliteDataBaseMetaData.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using NHibernate.Dialect.Schema; 3 | 4 | namespace NHibernate.Extensions.Sqlite; 5 | 6 | public class SqliteDataBaseMetaData( 7 | DbConnection connection, 8 | NHibernate.Dialect.Dialect dialect 9 | ) : SQLiteDataBaseMetaData(connection, dialect) { 10 | 11 | public SqliteDataBaseMetaData( 12 | DbConnection connection 13 | ) : this(connection, new SqliteDialect()) { } 14 | 15 | public override bool IncludeDataTypesInReservedWords => false; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Sqlite/SqliteDialect.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using NHibernate.Dialect.Schema; 3 | 4 | namespace NHibernate.Extensions.Sqlite; 5 | 6 | public class SqliteDialect : Dialect.SQLiteDialect { 7 | 8 | public override IDataBaseSchema GetDataBaseSchema(DbConnection connection) { 9 | var schema = new SqliteDataBaseMetaData(connection, this); 10 | return schema; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/NHibernate.Extensions.Sqlite/SqliteDriver.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Driver; 2 | using NHibernate.Engine; 3 | 4 | namespace NHibernate.Extensions.Sqlite; 5 | 6 | /// 7 | /// NHibernate driver for the Microsoft.Data.Sqlite.Core data provider for .NET. 8 | /// 9 | /// 10 | /// In order to use this driver, you must have the nuget package 11 | /// 12 | /// Microsoft.Data.Sqlite.Core and 13 | /// 14 | /// SQLitePCLRaw.bundle_e_sqlite3 installed for NHibernate to load. 15 | /// 16 | public class SqliteDriver : ReflectionBasedDriver { 17 | 18 | public SqliteDriver() : base( 19 | "Microsoft.Data.Sqlite.Core", 20 | "Microsoft.Data.Sqlite", 21 | "Microsoft.Data.Sqlite.SqliteConnection", 22 | "Microsoft.Data.Sqlite.SqliteCommand" 23 | ) { } 24 | 25 | public override IResultSetsCommand GetResultSetsCommand(ISessionImplementor session) { 26 | return new BasicResultSetsCommand(session); 27 | } 28 | 29 | public override bool UseNamedPrefixInSql => true; 30 | 31 | public override bool UseNamedPrefixInParameter => true; 32 | 33 | public override string NamedPrefix => "@"; 34 | 35 | public override bool SupportsMultipleOpenReaders => false; 36 | 37 | public override bool SupportsMultipleQueries => true; 38 | 39 | public override bool SupportsNullEnlistment => false; 40 | 41 | public override bool HasDelayedDistributedTransactionCompletion => true; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/LoggerFactoryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace NHibernate.NetCore; 4 | 5 | public static class LoggerFactoryExtensions { 6 | 7 | public static void UseAsHibernateLoggerFactory( 8 | this Microsoft.Extensions.Logging.ILoggerFactory loggerFactory 9 | ) { 10 | NHibernateLogger.SetLoggersFactory( 11 | new NetCoreLoggerFactory(loggerFactory) 12 | ); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/NHibernate.NetCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | enable 6 | NHibernate.NetCore 7 | beginor 8 | Extensions for NHibernate when used in dotnet core. For examples, visit https://github.com/beginor/nhibernate-extensions/blob/master/test/NHibernate.Extensions.UnitTest/BaseTest.cs 9 | NHibernate.NetCore 10 | $(PackageVersion) 11 | $(PackageReleaseNotes) 12 | $(PackageTags) 13 | $(PackageProjectUrl) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/NetCoreLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace NHibernate.NetCore; 5 | 6 | public class NetCoreLogger(ILogger logger) : IDisposable, INHibernateLogger { 7 | 8 | ~NetCoreLogger() { 9 | Dispose(false); 10 | } 11 | 12 | public void Dispose() { 13 | Dispose(true); 14 | } 15 | 16 | protected virtual void Dispose(bool disposing) { 17 | // if (disposing) { 18 | // logger = null; 19 | // } 20 | } 21 | 22 | public bool IsEnabled(NHibernateLogLevel logLevel) { 23 | var level = (int)logLevel; 24 | var msLogLevel = (LogLevel)level; 25 | return logger.IsEnabled(msLogLevel); 26 | } 27 | 28 | public void Log( 29 | NHibernateLogLevel logLevel, 30 | NHibernateLogValues state, 31 | Exception exception 32 | ) { 33 | var level = (int)logLevel; 34 | var msLogLevel = (LogLevel)level; 35 | logger.Log( 36 | msLogLevel, 37 | default(EventId), 38 | state, 39 | exception, 40 | (s, ex) => { 41 | var message = s.ToString(); 42 | if (ex != null) { 43 | message += ex.ToString(); 44 | } 45 | return message; 46 | } 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/NetCoreLoggerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Logging; 3 | using MsILoggerFactory = Microsoft.Extensions.Logging.ILoggerFactory; 4 | 5 | namespace NHibernate.NetCore; 6 | 7 | public class NetCoreLoggerFactory( 8 | MsILoggerFactory loggerFactory 9 | ) : IDisposable, INHibernateLoggerFactory { 10 | 11 | private readonly MsILoggerFactory loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); 12 | 13 | ~NetCoreLoggerFactory() { 14 | Dispose(false); 15 | } 16 | 17 | public void Dispose() { 18 | Dispose(true); 19 | } 20 | 21 | protected virtual void Dispose(bool disposing) { 22 | if (disposing) { 23 | loggerFactory.Dispose(); 24 | } 25 | } 26 | 27 | public INHibernateLogger LoggerFor(string keyName) { 28 | var logger = loggerFactory.CreateLogger(keyName); 29 | return new NetCoreLogger(logger); 30 | } 31 | 32 | public INHibernateLogger LoggerFor(System.Type type) { 33 | var logger = loggerFactory.CreateLogger(type); 34 | return new NetCoreLogger(logger); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Xml; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using NHibernate.Cfg; 6 | 7 | namespace NHibernate.NetCore; 8 | 9 | public static class ServiceCollectionExtensions { 10 | 11 | public static void AddHibernate( 12 | this IServiceCollection services 13 | ) { 14 | if (services == null) { 15 | throw new ArgumentNullException(nameof(services)); 16 | } 17 | var cfg = new Configuration(); 18 | cfg.Configure(); 19 | AddHibernate(services, cfg); 20 | } 21 | 22 | public static void AddHibernate( 23 | this IServiceCollection services, 24 | string path 25 | ) { 26 | if (services == null) { 27 | throw new ArgumentNullException(nameof(services)); 28 | } 29 | if (string.IsNullOrEmpty(path)) { 30 | throw new ArgumentNullException(nameof(path)); 31 | } 32 | var cfg = new Configuration(); 33 | cfg.Configure(path); 34 | AddHibernate(services, cfg); 35 | } 36 | 37 | public static void AddHibernate( 38 | this IServiceCollection services, 39 | XmlReader xmlReader 40 | ) { 41 | if (services == null) { 42 | throw new ArgumentNullException(nameof(services)); 43 | } 44 | if (xmlReader == null) { 45 | throw new ArgumentNullException(nameof(xmlReader)); 46 | } 47 | var cfg = new Configuration(); 48 | cfg.Configure(xmlReader); 49 | AddHibernate(services, cfg); 50 | } 51 | 52 | public static void AddHibernate( 53 | this IServiceCollection services, 54 | Assembly assembly, 55 | string resourceName 56 | ) { 57 | if (services == null) { 58 | throw new ArgumentNullException(nameof(services)); 59 | } 60 | if (assembly == null) { 61 | throw new ArgumentNullException(nameof(assembly)); 62 | } 63 | if (resourceName == null) { 64 | throw new ArgumentNullException(nameof(resourceName)); 65 | } 66 | var cfg = new Configuration(); 67 | cfg.Configure(assembly, resourceName); 68 | AddHibernate(services, cfg); 69 | } 70 | 71 | public static void AddHibernate( 72 | this IServiceCollection services, 73 | Configuration cfg 74 | ) { 75 | if (services == null) { 76 | throw new ArgumentNullException(nameof(services)); 77 | } 78 | if (cfg == null) { 79 | throw new ArgumentNullException(nameof(cfg)); 80 | } 81 | // Add Configuration as singleton 82 | services.AddSingleton(cfg); 83 | // Add ISessionFactory as singleton 84 | services.AddSingleton(provider => { 85 | var config = provider.GetService(); 86 | if (config == null) { 87 | throw new InvalidOperationException($"Can not get service {typeof(Configuration)}"); 88 | } 89 | return config.BuildSessionFactory(); 90 | }); 91 | // Add ISession as scoped 92 | services.AddScoped(provider => { 93 | var factory = provider.GetService(); 94 | if (factory == null) { 95 | throw new InvalidOperationException($"Can not get service {typeof(ISessionFactory)}"); 96 | } 97 | return factory.OpenSession(); 98 | }); 99 | } 100 | 101 | public static void AddHibernate( 102 | this IServiceCollection services, 103 | string key, 104 | Configuration cfg 105 | ) { 106 | if (services == null) { 107 | throw new ArgumentNullException(nameof(services)); 108 | } 109 | if (key == null) { 110 | throw new ArgumentNullException(nameof(key)); 111 | } 112 | if (cfg == null) { 113 | throw new ArgumentNullException(nameof(cfg)); 114 | } 115 | services.AddKeyedSingleton(key, cfg); 116 | // Add ISessionFactory as singleton 117 | services.AddKeyedSingleton( 118 | key, 119 | (provider, o) => { 120 | var config = provider.GetRequiredKeyedService(o); 121 | return config.BuildSessionFactory(); 122 | } 123 | ); 124 | // Add ISession as scoped 125 | services.AddKeyedScoped( 126 | key, 127 | (provider, o) => { 128 | var sessionFactory = provider.GetRequiredKeyedService(o); 129 | return sessionFactory.OpenSession(); 130 | } 131 | ); 132 | } 133 | 134 | public static void AddHibernate( 135 | this IServiceCollection services, 136 | string key, 137 | string path 138 | ) { 139 | if (services == null) { 140 | throw new ArgumentNullException(nameof(services)); 141 | } 142 | if (key == null) { 143 | throw new ArgumentNullException(nameof(key)); 144 | } 145 | if (string.IsNullOrEmpty(path)) { 146 | throw new ArgumentNullException(nameof(path)); 147 | } 148 | var cfg = new Configuration(); 149 | cfg.Configure(path); 150 | AddHibernate(services, key, cfg); 151 | } 152 | 153 | public static void AddHibernate( 154 | this IServiceCollection services, 155 | string key, 156 | XmlReader xmlReader 157 | ) { 158 | if (services == null) { 159 | throw new ArgumentNullException(nameof(services)); 160 | } 161 | if (key == null) { 162 | throw new ArgumentNullException(nameof(key)); 163 | } 164 | if (xmlReader == null) { 165 | throw new ArgumentNullException(nameof(xmlReader)); 166 | } 167 | var cfg = new Configuration(); 168 | cfg.Configure(xmlReader); 169 | AddHibernate(services, key, cfg); 170 | } 171 | 172 | public static void AddHibernate( 173 | this IServiceCollection services, 174 | string key, 175 | Assembly assembly, 176 | string resourceName 177 | ) { 178 | if (services == null) { 179 | throw new ArgumentNullException(nameof(services)); 180 | } 181 | if (key == null) { 182 | throw new ArgumentNullException(nameof(key)); 183 | } 184 | if (assembly == null) { 185 | throw new ArgumentNullException(nameof(assembly)); 186 | } 187 | if (resourceName == null) { 188 | throw new ArgumentNullException(nameof(resourceName)); 189 | } 190 | var cfg = new Configuration(); 191 | cfg.Configure(assembly, resourceName); 192 | AddHibernate(services, key, cfg); 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/NHibernate.NetCore/ServiceProviderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace NHibernate.NetCore; 5 | 6 | public static class ServiceProviderExtensions { 7 | 8 | public static ISessionFactory GetSessionFactory( 9 | this IServiceProvider serviceProvider 10 | ) { 11 | return serviceProvider.GetRequiredService(); 12 | } 13 | 14 | public static ISessionFactory GetSessionFactory( 15 | this IServiceProvider serviceProvider, 16 | string key 17 | ) { 18 | return serviceProvider.GetRequiredKeyedService(key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/package-version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(PACKAGE_VERSION) 4 | Update to .NET 9.0.3; Update Npgsql to 9.0.3; Update other packages; New solution format (slnx); 5 | nhibernate, npgsql, dotnet, dotnet core, array type, json type 6 | https://github.com/beginor/nhibernate-extensions 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/ArrTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Linq; 2 | 3 | using NHibernate.Extensions.Npgsql; 4 | using NHibernate.Extensions.Npgsql.UserTypes; 5 | using NHibernate.Extensions.UnitTest.TestDb; 6 | using NHibernate.Type; 7 | 8 | namespace NHibernate.Extensions.UnitTest; 9 | 10 | [TestFixture] 11 | public class ArrTest : BaseTest { 12 | 13 | private readonly IList entityIdList = new List(); 14 | 15 | public ArrTest() { 16 | Console.WriteLine("ArrTest::Constructor"); 17 | } 18 | 19 | [OneTimeSetUp] 20 | public override void OneTimeSetUp() { 21 | base.OneTimeSetUp(); 22 | using var session = OpenTestDbSession(); 23 | var entity = new ArrTestEntity { 24 | IntArr = [1, 2, 3], 25 | StrArr = ["a", "b", "c"] 26 | }; 27 | session.Save(entity); 28 | Assert.That(entity.Id, Is.GreaterThan(0)); 29 | entityIdList.Add(entity.Id); 30 | entity = new ArrTestEntity { 31 | IntArr = [4, 5, 6], 32 | StrArr = ["d", "e", "f"] 33 | }; 34 | session.Save(entity); 35 | Assert.That(entity.Id, Is.GreaterThan(0)); 36 | entityIdList.Add(entity.Id); 37 | session.Flush(); 38 | session.Clear(); 39 | Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(entityIdList)); 40 | } 41 | 42 | [OneTimeTearDown] 43 | public void OneTimeTearDown() { 44 | using var session = OpenTestDbSession(); 45 | var query = session.CreateSQLQuery("truncate table public.arr_test"); 46 | query.ExecuteUpdate(); 47 | entityIdList.Clear(); 48 | } 49 | 50 | [Test] 51 | public async Task _01_Can_QueryArrayEntity() { 52 | Assert.That(TestDbSessionFactory, Is.Not.Null); 53 | 54 | using var session = TestDbSessionFactory.OpenSession(); 55 | var entities = await session.Query().ToListAsync(); 56 | Assert.That(entities, Is.Not.Null); 57 | Assert.That(entities.Count, Is.GreaterThan(0)); 58 | } 59 | 60 | [Test] 61 | public void _01_CanQueryArayContainsWithHql() { 62 | using var session = TestDbSessionFactory.OpenSession(); 63 | var num = 1; 64 | var query1 = session.CreateQuery( 65 | $"from ArrTestEntity e where array_contains(e.IntArr, :{nameof(num)})" 66 | ); 67 | query1.SetParameter(nameof(num), num, NHibernateUtil.Int32); 68 | var data1 = query1.List(); 69 | Assert.That(data1, Is.Not.Empty); 70 | 71 | var str = "a"; 72 | var query2 = session.CreateQuery( 73 | $"from ArrTestEntity e where array_contains(e.StrArr, :{nameof(str)})" 74 | ); 75 | query2.SetParameter(nameof(str), str, NHibernateUtil.String); 76 | var data2 = query2.List(); 77 | Assert.That(data2, Is.Not.Empty); 78 | 79 | var query3 = session.CreateQuery( 80 | $"from ArrTestEntity e where array_contains(e.StrArr, :{nameof(str)})" 81 | + $" and array_contains(e.IntArr, :{nameof(num)})" 82 | ); 83 | query3.SetParameter(nameof(str), str, NHibernateUtil.String); 84 | query3.SetParameter(nameof(num), num, NHibernateUtil.Int32); 85 | var data3 = query3.List(); 86 | Assert.That(data3, Is.Not.Empty); 87 | } 88 | 89 | [Test] 90 | public void _03_CanQueryArrayContainsWithLinq() { 91 | using var session = TestDbSessionFactory.OpenSession(); 92 | var num = 1; 93 | var query1 = session.Query().Where( 94 | x => x.IntArr.ArrayContains(num) 95 | ); 96 | var data1 = query1.ToList(); 97 | Assert.That(data1, Is.Not.Empty); 98 | 99 | var str = "a"; 100 | var query2 = session.Query().Where( 101 | x => x.StrArr.ArrayContains(str) 102 | ); 103 | var data2 = query2.ToList(); 104 | Assert.That(data2, Is.Not.Empty); 105 | 106 | var query3 = session.Query().Where( 107 | x => x.IntArr.ArrayContains(num) && x.StrArr.ArrayContains(str) 108 | ); 109 | var data3 = query3.ToList(); 110 | Assert.That(data3, Is.Not.Empty); 111 | } 112 | 113 | [Test] 114 | public void _04_CanQueryArrayIntersectsWithHql() { 115 | using var session = TestDbSessionFactory.OpenSession(); 116 | 117 | string[] strArr = ["a", "c"]; 118 | var query1 = session.CreateQuery( 119 | $"from ArrTestEntity e where array_intersects(e.StrArr, :{nameof(strArr)})" 120 | ); 121 | query1.SetParameter(nameof(strArr), strArr, NHibernateUtil.Custom(typeof(ArrayType))); 122 | var data1 = query1.List(); 123 | Assert.That(data1, Is.Not.Empty); 124 | 125 | int[] intArr = [1, 3]; 126 | var query2 = session.CreateQuery( 127 | $"from ArrTestEntity e where array_intersects(e.IntArr, :{nameof(intArr)})" 128 | ); 129 | query2.SetParameter(nameof(intArr), intArr, NHibernateUtil.Custom(typeof(ArrayType))); 130 | var data2 = query1.List(); 131 | Assert.That(data2, Is.Not.Empty); 132 | 133 | var query3 = session.CreateQuery( 134 | $"from ArrTestEntity e where array_intersects(e.StrArr, :{nameof(strArr)})" 135 | + $" and array_intersects(e.IntArr, :{nameof(intArr)})" 136 | ); 137 | query3.SetParameter(nameof(strArr), strArr, NHibernateUtil.Custom(typeof(ArrayType))); 138 | query3.SetParameter(nameof(intArr), intArr, NHibernateUtil.Custom(typeof(ArrayType))); 139 | var data3 = query3.List(); 140 | Assert.That(data3, Is.Not.Empty); 141 | } 142 | 143 | [Test] 144 | public async Task _05_CanQueryArrayIntersectsWithLinq() { 145 | using var session = TestDbSessionFactory.OpenSession(); 146 | 147 | string[] strArr = ["a", "c"]; 148 | var query1 = session.Query().Where( 149 | x => x.StrArr.ArrayIntersects(strArr) 150 | ); 151 | var data1 = await query1.ToListAsync(); 152 | Assert.That(data1, Is.Not.Empty); 153 | 154 | int[] intArr = [1, 3]; 155 | var query2 = session.Query().Where( 156 | x => x.IntArr.ArrayIntersects(intArr) 157 | ); 158 | var data2 = await query2.ToListAsync(); 159 | Assert.That(data2, Is.Not.Empty); 160 | 161 | var query3 = session.Query().Where( 162 | x => x.StrArr.ArrayIntersects(strArr) && x.IntArr.ArrayIntersects(intArr) 163 | ); 164 | var data3 = await query3.ToListAsync(); 165 | Assert.That(data3, Is.Not.Empty); 166 | } 167 | 168 | [Test] 169 | public void _06_CanQueryContains() { 170 | using var session = TestDbSessionFactory.OpenSession(); 171 | var idList = entityIdList.ToArray(); 172 | var query = session.Query().Where( 173 | x => idList.Contains(x.Id) 174 | ); 175 | var data = query.ToList(); 176 | Assert.That(data, Is.Not.Empty); 177 | 178 | var idArr = idList.ToArray(); 179 | var query2 = session.Query().Where( 180 | x => idArr.ArrayContains(x.Id) 181 | ); 182 | var data2 = query2.ToList(); 183 | Assert.That(data2, Is.Not.Empty); 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/ArrayTypeTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using NHibernate.Extensions.Npgsql.UserTypes; 3 | using NHibernate.Extensions.UnitTest.TestDb; 4 | 5 | namespace NHibernate.Extensions.UnitTest; 6 | 7 | [TestFixture] 8 | public class ArrayTypeTest : BaseTest { 9 | 10 | [OneTimeSetUp] 11 | public override void OneTimeSetUp() { 12 | base.OneTimeSetUp(); 13 | } 14 | 15 | [Test] 16 | public void _01_CanDoCrud() { 17 | using (var session = TestDbSessionFactory.OpenSession()) { 18 | var entity = new TestEntity { 19 | Name = "Test 1", 20 | Tags = new [] { "hello", "world" }, 21 | JsonField = JsonSerializer.Deserialize("{ \"val\": 1 }"), 22 | JsonbField = JsonSerializer.Deserialize("{ \"val\": 1 }"), 23 | UpdateTime = DateTime.Now, 24 | Int16Arr = new short[] { 1, 2, 3 }, 25 | Int32Arr = new [] { 1, 2, 3 }, 26 | Int64Arr = new [] { 1L, 2L, 3L }, 27 | FloatArr = new [] { 1.1F, 2.2F, 3.3F }, 28 | DoubleArr = new [] { 1.1, 2.2, 3.3 }, 29 | BooleanArr = new [] { true, false } 30 | }; 31 | session.Save(entity); 32 | session.Flush(); 33 | session.Clear(); 34 | 35 | Assert.That(entity.Id > 0); 36 | 37 | Console.WriteLine($"entity id: {entity.Id}"); 38 | } 39 | 40 | using (var session = TestDbSessionFactory.OpenSession()) { 41 | var query = session.Query(); 42 | var entities = query.ToList(); 43 | Assert.That(entities, Is.Not.Null); 44 | Console.WriteLine($"Entity count: {entities.Count}"); 45 | 46 | using (var tx = session.BeginTransaction()) { 47 | foreach (var e in entities) { 48 | Console.WriteLine(JsonSerializer.Serialize(e)); 49 | session.Delete(e); 50 | } 51 | tx.Commit(); 52 | } 53 | } 54 | } 55 | 56 | [Test] 57 | public void _02_CanSaveTypedJson() { 58 | using var session = TestDbSessionFactory.OpenSession(); 59 | var json = new JsonValue { 60 | Value = new ConnectionString { 61 | Server = "127.0.0.1", 62 | Port = 1433, 63 | Database = "northwind", 64 | Username = "sa", 65 | Password = "password" 66 | } 67 | }; 68 | session.Save(json); 69 | session.Flush(); 70 | Assert.That(json.Id, Is.GreaterThan(0)); 71 | Console.WriteLine(json.Id); 72 | var val = session.Query().First(x => x.Id == json.Id); 73 | Assert.That(val, Is.Not.Null); 74 | Assert.That(json.Id, Is.EqualTo(val.Id)); 75 | session.Delete(json); 76 | session.Flush(); 77 | } 78 | 79 | [Test] 80 | public void _04_CanDoQueryDynamicJson() { 81 | using var session = TestDbSessionFactory.OpenSession(); 82 | var query = session.CreateSQLQuery("select id, value from public.json_values"); 83 | query.AddScalar("id", NHibernateUtil.Int64); 84 | query.AddScalar("value", NHibernateUtil.Custom(typeof(JsonbType))); 85 | var list = query.List(); 86 | foreach (var item in list) { 87 | Console.WriteLine(JsonSerializer.Serialize(item)); 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/AttrMapTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using NHibernate.Extensions.UnitTest.TestDb; 3 | 4 | namespace NHibernate.Extensions.UnitTest; 5 | 6 | [TestFixture] 7 | public class AttrMapTest { 8 | 9 | [Test] 10 | public void _01_CanSerializeXmlMap() { 11 | var serializer = HbmSerializer.Default; 12 | var stream = serializer.Serialize( 13 | typeof(SnowFlakeTestEntity).Assembly 14 | ); 15 | 16 | var err = serializer.Error.ToString(); 17 | 18 | Assert.That(err, Is.Empty); 19 | 20 | var reader = new StreamReader(stream); 21 | var xml = reader.ReadToEnd(); 22 | Console.WriteLine(xml); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using NHibernate.Cfg; 3 | using NHibernate.Linq; 4 | using NHibernate.Mapping.Attributes; 5 | using NHibernate.Tool.hbm2ddl; 6 | using NHibernate.NetCore; 7 | using NHibernate.Mapping.ByCode; 8 | 9 | using NHibernate.Extensions.UnitTest.Sqlite; 10 | using NHibernate.Extensions.UnitTest.TestDb; 11 | using Author = NHibernate.Extensions.UnitTest.TestDb.Author; 12 | 13 | namespace NHibernate.Extensions.UnitTest; 14 | 15 | public class BaseTest { 16 | 17 | protected IServiceProvider ServiceProvider { get; private set; } 18 | 19 | protected ISessionFactory TestDbSessionFactory => ServiceProvider.GetSessionFactory(); 20 | protected ISessionFactory SqliteSessionFactory => ServiceProvider.GetSessionFactory("sqlite"); 21 | 22 | [OneTimeSetUp] 23 | public virtual void OneTimeSetUp() { 24 | // global setup 25 | // NpgsqlConnection.GlobalTypeMapper.UseJsonNet(); 26 | var services = new ServiceCollection(); 27 | services.AddHibernate(CreateTestDbConfiguration()); 28 | services.AddHibernate("sqlite", CreateSqliteConfiguration()); 29 | // build service provider 30 | ServiceProvider = services.BuildServiceProvider(); 31 | } 32 | 33 | private Configuration CreateTestDbConfiguration() { 34 | // add default config 35 | var defaultConfigFile = Path.Combine( 36 | AppDomain.CurrentDomain.BaseDirectory, 37 | "hibernate.config" 38 | ); 39 | var config = new Configuration(); 40 | config.Configure(defaultConfigFile); 41 | // use default attr serializer; 42 | var serializer = HbmSerializer.Default; 43 | var xmlStream = serializer.Serialize( 44 | typeof(SnowFlakeTestEntity).Assembly 45 | ); 46 | // ensure serialize error is empty; 47 | var err = serializer.Error.ToString(); 48 | Assert.That(err, Is.Empty); 49 | // add to config 50 | using var reader = new StreamReader(xmlStream); 51 | var xml = reader.ReadToEnd(); 52 | Console.WriteLine(xml); 53 | config.AddXml(xml); 54 | return config; 55 | } 56 | 57 | private Configuration CreateSqliteConfiguration() { 58 | var configuration = new Configuration(); 59 | configuration.Configure("hibernate.sqlite.config"); 60 | var mapper = new ModelMapper(); 61 | mapper.AddMapping(); 62 | mapper.AddMapping(); 63 | var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); 64 | configuration.AddMapping(mapping); 65 | return configuration; 66 | } 67 | 68 | [Test] 69 | public void _01_CanResolveSessionFactories() { 70 | Assert.That(ServiceProvider, Is.Not.Null); 71 | Assert.That(TestDbSessionFactory, Is.Not.Null); 72 | Assert.That(SqliteSessionFactory, Is.Not.Null); 73 | } 74 | 75 | [Test] 76 | public async Task _02_CanQueryTestDb() { 77 | using (var session = TestDbSessionFactory.OpenSession()) { 78 | var author = await session.Query().FirstOrDefaultAsync(); 79 | // Assert.That(author, Is.Not.Null); 80 | } 81 | } 82 | 83 | [Test] 84 | public void _04_CanDoSchemaExport() { 85 | var export = new SchemaExport( 86 | ServiceProvider.GetService() 87 | ); 88 | export.Execute(true, false, false); 89 | } 90 | 91 | protected ISession OpenTestDbSession() { 92 | return TestDbSessionFactory.OpenSession(); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/ConfigTest.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.Extensions.Configuration; 3 | using NHibernate.Cfg; 4 | 5 | namespace NHibernate.Extensions.UnitTest; 6 | 7 | [TestFixture] 8 | public class ConfigTest { 9 | 10 | private IConfiguration configuration; 11 | 12 | [OneTimeSetUp] 13 | public void OneTimeSetUp() { 14 | configuration = new ConfigurationBuilder() 15 | .SetBasePath(Directory.GetCurrentDirectory()) 16 | .AddJsonFile("appsettings.json") 17 | .Build(); 18 | } 19 | 20 | [Test] 21 | public void _01_CanReadConfig() { 22 | Assert.That(configuration, Is.Not.Null); 23 | var nhibernate = configuration 24 | .GetSection("nhibernate").GetChildren(); 25 | Console.WriteLine(nhibernate); 26 | 27 | foreach (var section in nhibernate) { 28 | var cfg = new Configuration(); 29 | foreach (var pair in section.AsEnumerable(true)) { 30 | if (string.IsNullOrEmpty(pair.Value)) { 31 | continue; 32 | } 33 | if (pair.Key.StartsWith("mapping:assembly")) { 34 | var asm = pair.Value; 35 | // if (!asm.EndsWith(".dll")) { 36 | // asm += ".dll"; 37 | // } 38 | cfg.AddAssembly(asm); 39 | } 40 | else if (pair.Key.StartsWith("mapping:file")) { 41 | cfg.AddFile(pair.Value); 42 | } 43 | else if (pair.Key.StartsWith("mapping:resource")) { 44 | var resAndAsm = pair.Value.Split( 45 | ",", 46 | StringSplitOptions.RemoveEmptyEntries 47 | ); 48 | var res = resAndAsm[0].Trim(); 49 | var asm = resAndAsm[1].Trim(); 50 | if (!asm.EndsWith(".dll")) { 51 | asm += ".dll"; 52 | } 53 | cfg.AddResource(res, Assembly.LoadFrom(asm)); 54 | } 55 | else { 56 | cfg.SetProperty(pair.Key, pair.Value); 57 | } 58 | } 59 | var sf = cfg.BuildSessionFactory(); 60 | Assert.That(sf, Is.Not.Null); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/DapperTest.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using System.Text.Json; 3 | using Dapper; 4 | 5 | namespace NHibernate.Extensions.UnitTest; 6 | 7 | [TestFixture] 8 | public class DapperTest : BaseTest { 9 | 10 | [OneTimeSetUp] 11 | public override void OneTimeSetUp() { 12 | base.OneTimeSetUp(); 13 | Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 14 | } 15 | 16 | [OneTimeTearDown] 17 | public void OneTimeTearDown() { 18 | } 19 | 20 | [Test] 21 | public void CanQueryAuthors() { 22 | using (var session = TestDbSessionFactory.OpenSession()) { 23 | var conn = session.Connection; 24 | var authors = conn.Query( 25 | "select * from public.authors" 26 | ); 27 | Assert.That(authors.Count(), Is.GreaterThanOrEqualTo(0)); 28 | 29 | authors = conn.Query( 30 | "select * from public.authors where authorid = any(@Ids)", 31 | new { 32 | Ids = new [] { 1, 2, 3 } .AsEnumerable() 33 | } 34 | ); 35 | Assert.That(authors.Count(), Is.LessThanOrEqualTo(3)); 36 | } 37 | } 38 | 39 | [Test] 40 | public void CanUseTypeHandler() { 41 | using (var session = TestDbSessionFactory.OpenSession()) { 42 | var conn = session.Connection; 43 | Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 44 | SqlMapper.AddTypeHandler(new DateTimeHandler()); 45 | var entity = conn.Query( 46 | "select * from public.test_table order by update_time limit 1" 47 | ).FirstOrDefault(); 48 | Assert.That(entity, Is.Not.Null); 49 | 50 | var updated = conn.Execute( 51 | "update public.test_table set update_time = @updateTime where id = @id", 52 | new { id = entity.Id, updateTime = DateTime.Now } 53 | ); 54 | Assert.That(updated, Is.EqualTo(1)); 55 | } 56 | } 57 | 58 | } 59 | 60 | public class AuthorEntity { 61 | public int AuthorId { get; set; } 62 | public string Name { get; set; } 63 | } 64 | 65 | public class BookEntity { 66 | public int BookId { get; set; } 67 | public string Title { get; set; } 68 | public int AuthorId { get; set; } 69 | } 70 | 71 | public class TestTableEntity { 72 | public virtual long Id { get; set; } 73 | public virtual string Name { get; set; } 74 | public virtual string[] Tags { get; set; } 75 | public virtual JsonElement JsonField { get; set; } 76 | public virtual JsonElement JsonbField { get; set; } 77 | public virtual DateTime? UpdateTime { get; set; } 78 | public virtual short[] Int16Arr { get; set; } 79 | public virtual int[] Int32Arr { get; set; } 80 | public virtual long[] Int64Arr { get; set; } 81 | public virtual float[] FloatArr { get; set; } 82 | public virtual double[] DoubleArr { get; set; } 83 | public virtual bool[] BooleanArr { get; set; } 84 | } 85 | 86 | public class DateTimeHandler : Dapper.SqlMapper.TypeHandler { 87 | 88 | public override void SetValue(IDbDataParameter parameter, DateTime value) { 89 | Console.WriteLine("set value!"); 90 | parameter.Value = DateTime.SpecifyKind(value, DateTimeKind.Utc); 91 | } 92 | 93 | public override DateTime Parse(object value) { 94 | Console.WriteLine("parse!"); 95 | var localTime = (DateTime) value; 96 | return DateTime.SpecifyKind(localTime, DateTimeKind.Utc); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/DvdRentalTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class DvdRentalTest : BaseTest { 7 | 8 | [OneTimeSetUp] 9 | public override void OneTimeSetUp() { 10 | base.OneTimeSetUp(); 11 | } 12 | 13 | [Test] 14 | public void _01_CanSetupSessionFactory() { 15 | Assert.That(TestDbSessionFactory, Is.Not.Null); 16 | 17 | var dvdRentalSession = TestDbSessionFactory.OpenSession(); 18 | var connStr = dvdRentalSession.Connection.ConnectionString; 19 | Console.WriteLine(connStr); 20 | dvdRentalSession.Close(); 21 | 22 | var testDbSession = TestDbSessionFactory.OpenSession(); 23 | var connStr2 = testDbSession.Connection.ConnectionString; 24 | Console.WriteLine(connStr2); 25 | testDbSession.Close(); 26 | 27 | Assert.That(connStr, Is.Not.EqualTo(connStr2)); 28 | } 29 | 30 | [Test] 31 | public void _02_CanQueryActors() { 32 | var factory = TestDbSessionFactory; 33 | using var session = factory.OpenSession(); 34 | var actors = session.Query().ToList(); 35 | Assert.That(actors, Is.Not.Empty); 36 | } 37 | 38 | [Test] 39 | public void _03_CanInsertUpdateDeleteAuthors() { 40 | using var session = OpenTestDbSession(); 41 | var author = new Actor { 42 | FirstName = "Simon", 43 | LastName = "Zhang", 44 | LastUpdate = DateTime.Now 45 | }; 46 | session.Save(author); 47 | session.Flush(); 48 | Assert.That(author.ActorId, Is.GreaterThan(0)); 49 | session.Delete(author); 50 | session.Flush(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/InheritanceMappingTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class InheritanceMappingTest : BaseTest { 7 | 8 | [Test] 9 | public void _00_CanInitData() { 10 | using var session = TestDbSessionFactory.OpenSession(); 11 | var query = session.Query(); 12 | var data = query.ToList(); 13 | if (data.Count == 0) { 14 | var api = new DataApi { 15 | Name = "test api", 16 | Statement = "select * from public.data_apis", 17 | Parameters = "a, b" 18 | }; 19 | session.Save(api); 20 | var slpk = new Slpk { 21 | Name = "test slpk", 22 | Path = "c:/test/" 23 | }; 24 | session.Save(slpk); 25 | session.Flush(); 26 | session.Clear(); 27 | } 28 | } 29 | 30 | [Test] 31 | public void _01_CanQueryParent() { 32 | using var session = TestDbSessionFactory.OpenSession(); 33 | var query = session.Query().Select(e => new BaseResource { 34 | Id = e.Id, 35 | Name = e.Name, 36 | Type = e.Type 37 | }); 38 | var data = query.ToList(); 39 | Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(data)); 40 | } 41 | 42 | [Test] 43 | public void _02_CanQueryChild() { 44 | using var session = TestDbSessionFactory.OpenSession(); 45 | var query = session.Query(); 46 | var data = query.ToList(); 47 | Console.WriteLine(data.Count); 48 | } 49 | 50 | [Test] 51 | public void _03_CanQueryWithTypeFilter() { 52 | using var session = TestDbSessionFactory.OpenSession(); 53 | var query = session.Query() 54 | .Where(e => e.Type == "slpks") 55 | .Select(e => new BaseResource { 56 | Id = e.Id, 57 | Name = e.Name, 58 | Type = e.Type 59 | }); 60 | var data = query.ToList(); 61 | Console.WriteLine(data.Count); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/NHibernate.Extensions.UnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | false 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/NameQueryTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class NameQueryTest : BaseTest { 7 | 8 | [OneTimeSetUp] 9 | public override void OneTimeSetUp() { 10 | base.OneTimeSetUp(); 11 | } 12 | 13 | [Test] 14 | public void _01_CanQueryClass() { 15 | using (var session = TestDbSessionFactory.OpenSession()) { 16 | var query = session.GetNamedQuery("name_test_01"); 17 | var books = query.List(); 18 | 19 | Assert.That(books.Count > 0); 20 | } 21 | } 22 | 23 | [Test] 24 | public void _02_CanQueryClassWithParam() { 25 | using (var session = TestDbSessionFactory.OpenSession()) { 26 | var query = session.GetNamedQuery("name_test_02"); 27 | query.SetInt32("authorid", 2); 28 | var books = query.List(); 29 | Assert.That(books.Count > 0); 30 | } 31 | } 32 | 33 | [Test] 34 | public void _03_CanQueryScalar() { 35 | using (var session = TestDbSessionFactory.OpenSession()) { 36 | var query = session.GetNamedQuery("name_test_03"); 37 | var booksCount = query.List().First(); 38 | Assert.That(booksCount > 0); 39 | } 40 | } 41 | 42 | [Test] 43 | public void _04_CanQueryScalarWithParam() { 44 | using (var session = TestDbSessionFactory.OpenSession()) { 45 | var query = session.GetNamedQuery("name_test_04"); 46 | Assert.That(query.NamedParameters.Length > 0); 47 | query.SetInt32("authorid", 1); 48 | Assert.That(query.NamedParameters.Length > 0); 49 | var booksCount = query.List().First(); 50 | Assert.That(booksCount > 0); 51 | } 52 | } 53 | 54 | [Test] 55 | public void _05_CanQueryDynamicColumns() { 56 | using (var session = TestDbSessionFactory.OpenSession()) { 57 | var query = session.GetNamedQuery("name_test_05"); 58 | var books = query.List(); 59 | 60 | Assert.That(books.Count > 0); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/NpgTimeTests.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using NHibernate.Extensions.UnitTest.TestDb; 3 | using Npgsql; 4 | using NpgsqlTypes; 5 | 6 | namespace NHibernate.Extensions.UnitTest; 7 | 8 | [TestFixture] 9 | public class NpgTimeTests : BaseTest { 10 | 11 | [OneTimeSetUp] 12 | public override void OneTimeSetUp() { 13 | base.OneTimeSetUp(); 14 | } 15 | 16 | [Test] 17 | public void _01_CanQueryTimes() { 18 | using var session = OpenTestDbSession(); 19 | var data = session.Query().ToList(); 20 | foreach (var item in data) { 21 | Console.WriteLine(item); 22 | } 23 | } 24 | 25 | [Test] 26 | public void _02_CanSaveTimes() { 27 | var localTime = DateTime.Now; 28 | var utcTime = DateTime.UtcNow; 29 | Console.WriteLine($"local time: {localTime}, utc time: {utcTime}"); 30 | var entity = new NpgTime { 31 | // LocalTime = DateTime.SpecifyKind(localTime, DateTimeKind.Utc), 32 | LocalTime = localTime, 33 | UtcTime = utcTime 34 | }; 35 | using var session = OpenTestDbSession(); 36 | session.Save(entity); 37 | session.Flush(); 38 | } 39 | 40 | [Test] 41 | public void _03_CanSaveRawTime() { 42 | var localTime = DateTime.Now; 43 | var utcTime = DateTime.UtcNow; 44 | using var session = OpenTestDbSession(); 45 | using var conn = session.Connection as NpgsqlConnection; 46 | // conn.Open(); 47 | var cmd = conn.CreateCommand(); 48 | cmd.CommandText = "insert into public.npg_times(local_time, utc_time) values(:p0, :p1) returning id;"; 49 | var p0 = cmd.CreateParameter(); 50 | p0.ParameterName = ":p0"; 51 | p0.Value = localTime; 52 | p0.DbType = DbType.DateTime; 53 | p0.NpgsqlDbType = NpgsqlDbType.Timestamp; 54 | cmd.Parameters.Add(p0); 55 | 56 | var p1 = cmd.CreateParameter(); 57 | p1.ParameterName = ":p1"; 58 | p1.Value = utcTime; 59 | p1.DbType = DbType.DateTime; 60 | p1.NpgsqlDbType = NpgsqlDbType.TimestampTz; 61 | cmd.Parameters.Add(p1); 62 | 63 | var result = cmd.ExecuteNonQuery(); 64 | Console.WriteLine($"result {result}"); 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/NpgsqlTypeTest.cs: -------------------------------------------------------------------------------- 1 | using NpgsqlTypes; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class NpgsqlTypeTest { 7 | 8 | [Test] 9 | public void _01_CanReadArrayNpgsqlType() { 10 | // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags 11 | var type = NpgsqlDbType.Array | NpgsqlDbType.Bigint; 12 | var res = (NpgsqlDbType)((int)type - (int)NpgsqlDbType.Array); 13 | Assert.That(res == NpgsqlDbType.Bigint); 14 | } 15 | 16 | [Test] 17 | public void _02_EqualsArrayType() { 18 | var arrayTypeInt = (int)NpgsqlDbType.Array + (int)NpgsqlDbType.Bigint; 19 | // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags 20 | var arrayType = NpgsqlDbType.Array | NpgsqlDbType.Bigint; 21 | Assert.That((int)arrayType, Is.EqualTo(arrayTypeInt)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/QueryTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class QueryTest : BaseTest { 7 | 8 | [OneTimeSetUp] 9 | public override void OneTimeSetUp() { 10 | base.OneTimeSetUp(); 11 | } 12 | 13 | [Test] 14 | public void _01_CanQueryManyToOne() { 15 | using (var session = TestDbSessionFactory.OpenSession()) { 16 | var query = session.Query().Select(b => b.Author); 17 | var data = query.ToList(); 18 | Console.WriteLine(data.Count); 19 | } 20 | } 21 | 22 | [Test] 23 | public void _02_CanQueryOneToMany() { 24 | using (var session = TestDbSessionFactory.OpenSession()) { 25 | var query = session.Query().Select(a => new { 26 | Author = a, 27 | Books = a.Books 28 | }); 29 | var data = query.ToList(); 30 | Console.WriteLine(data.Count); 31 | } 32 | } 33 | 34 | [Test] 35 | public void _03_CanQueryReference() { 36 | using (var session = TestDbSessionFactory.OpenSession()) { 37 | var query = from book in session.Query() 38 | select new { 39 | BookId = book.BookId, 40 | AuthorId = book.Author.AuthorId 41 | }; 42 | var data = query.ToList(); 43 | foreach (var d in data) { 44 | Console.WriteLine($"{d.AuthorId}, {d.BookId}"); 45 | } 46 | } 47 | } 48 | 49 | [Test] 50 | public void _04_CanSaveOneToMany() { 51 | using (var session = TestDbSessionFactory.OpenSession()) { 52 | using (var tx = session.BeginTransaction()) { 53 | try { 54 | var author = new Author(); 55 | author.Name = "beginor"; 56 | session.Save(author); 57 | var book = new Book(); 58 | book.Title = "learning nhibernate"; 59 | book.Author = author; 60 | session.Save(book); 61 | session.Flush(); 62 | 63 | session.Delete(book); 64 | session.Delete(author); 65 | tx.Commit(); 66 | } 67 | catch (Exception) { 68 | tx.Rollback(); 69 | throw; 70 | } 71 | } 72 | } 73 | } 74 | 75 | [Test] 76 | public void _07_CanQueryMoniData() { 77 | using (var session = TestDbSessionFactory.OpenSession()) { 78 | var moniData = new MoniData { 79 | MoniTime = DateTime.Now, 80 | StationId = 1, 81 | ItemId = 1, 82 | Value = (decimal)0.003d, 83 | Description = "test" 84 | }; 85 | session.Save(moniData); 86 | session.Flush(); 87 | var query = session.Query(); 88 | var data = query.ToList(); 89 | Assert.That(data.Count > 0); 90 | session.Delete(moniData); 91 | session.Flush(); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/SnowFlakeTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using NHibernate.Extensions.UnitTest.TestDb; 3 | 4 | namespace NHibernate.Extensions.UnitTest; 5 | 6 | [TestFixture] 7 | public class SnowFlakeTest : BaseTest { 8 | 9 | [OneTimeSetUp] 10 | public override void OneTimeSetUp() { 11 | base.OneTimeSetUp(); 12 | } 13 | 14 | [Test] 15 | public void _01_CanQuerySnowFlakeId() { 16 | using (var session = TestDbSessionFactory.OpenSession()) { 17 | var entities = session.Query() 18 | .ToList(); 19 | foreach (var entity in entities) { 20 | Assert.That(entity.Id > 0); 21 | Console.WriteLine(JsonSerializer.Serialize(entity)); 22 | } 23 | } 24 | } 25 | 26 | [Test] 27 | public void _02_CanInsertSnowFlakeId() { 28 | using (var session = TestDbSessionFactory.OpenSession()) { 29 | var entity = new SnowFlakeTestEntity { 30 | Name = Guid.NewGuid().ToString("N") 31 | }; 32 | session.Save(entity); 33 | Assert.That(entity.Id > 0); 34 | Console.WriteLine($"Id: {entity.Id}"); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/Sqlite/Author.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.ByCode; 2 | using NHibernate.Mapping.ByCode.Conformist; 3 | 4 | namespace NHibernate.Extensions.UnitTest.Sqlite; 5 | 6 | public class Author { 7 | public virtual int AuthorId { get; set; } 8 | public virtual string Name { get; set; } 9 | public virtual ICollection Books { get; set; } = new List(); 10 | } 11 | 12 | public class AuthorMappingSqlite : ClassMapping { 13 | 14 | public AuthorMappingSqlite() { 15 | // Schema("main"); 16 | Table("authors"); 17 | Id(e => e.AuthorId, m => { 18 | m.Column("id"); 19 | m.Type(NHibernateUtil.Int32); 20 | m.Generator(Generators.Identity); 21 | }); 22 | Property(e => e.Name, m => { 23 | m.Column("name"); 24 | m.Type(NHibernateUtil.String); 25 | m.Length(16); 26 | m.NotNullable(true); 27 | }); 28 | Bag(p => p.Books, map => { 29 | map.Key(k => k.Column("author_id")); 30 | map.Inverse(true); 31 | map.Cascade(Cascade.DeleteOrphans); 32 | }, c => { 33 | c.OneToMany(); 34 | }); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/Sqlite/Book.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.ByCode; 2 | using NHibernate.Mapping.ByCode.Conformist; 3 | 4 | namespace NHibernate.Extensions.UnitTest.Sqlite; 5 | 6 | public class Book { 7 | public virtual int BookId { get; set; } 8 | public virtual string Title { get; set; } 9 | public virtual Author Author { get; set; } 10 | } 11 | 12 | public class BookMappingSqlite : ClassMapping { 13 | 14 | public BookMappingSqlite() { 15 | Table("books"); 16 | Id(e => e.BookId, map => { 17 | map.Column("id"); 18 | map.Type(NHibernateUtil.Int32); 19 | map.Generator(Generators.Identity); 20 | }); 21 | Property(p => p.Title, map => { 22 | map.Column("title"); 23 | map.Type(NHibernateUtil.String); 24 | }); 25 | ManyToOne(p => p.Author, map => { 26 | map.Column("author_id"); 27 | map.Fetch(FetchKind.Join); 28 | map.ForeignKey("fk_books_author_id"); 29 | }); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/SqliteDriverTest.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using Microsoft.Data.Sqlite; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using NHibernate.Cfg; 5 | using NHibernate.Tool.hbm2ddl; 6 | using NHibernate.Extensions.UnitTest.Sqlite; 7 | using NHibernate.NetCore; 8 | 9 | namespace NHibernate.Extensions.UnitTest; 10 | 11 | [TestFixture] 12 | public class SqliteDriverTest : BaseTest { 13 | 14 | private ISessionFactory SessionFactory => ServiceProvider.GetSessionFactory("sqlite"); 15 | 16 | [Test] 17 | public void _00_CanQuerySqlite() { 18 | var builder = new SqliteConnectionStringBuilder(); 19 | builder.DataSource = "./test_db.db3"; 20 | builder.ForeignKeys = true; 21 | var connStr = builder.ToString(); 22 | var conn = new SqliteConnection(connStr); 23 | conn.Open(); 24 | var cmd = conn.CreateCommand(); 25 | cmd.CommandText = "select * from main.authors"; 26 | var reader = cmd.ExecuteReader(); 27 | while (reader.Read()) { 28 | Console.WriteLine($"{reader.GetInt32("id")}, {reader.GetString("name")}"); 29 | } 30 | } 31 | 32 | [Test] 33 | public void _01_CanBuildSessionFactory() { 34 | Assert.That(SessionFactory, Is.Not.Null); 35 | using (var session = SessionFactory.OpenSession()) { 36 | var authors = session.Query() 37 | .Where(a => a.AuthorId > 0) 38 | .ToList(); 39 | Assert.That(authors, Is.Not.Empty); 40 | var count = session.Query().LongCount(a => a.AuthorId > 0); 41 | Assert.That(count, Is.GreaterThanOrEqualTo(0)); 42 | } 43 | } 44 | 45 | [Test] 46 | public void _02_CanSaveDelete() { 47 | using (var session = SessionFactory.OpenSession()) { 48 | var author = new Author { 49 | Name = "Unit Test" 50 | }; 51 | session.Save(author); 52 | session.Flush(); 53 | Assert.That(author.AuthorId, Is.GreaterThan(0)); 54 | session.Delete(author); 55 | session.Flush(); 56 | } 57 | } 58 | 59 | [Test] 60 | public void _03_CanDoSchemaExport() { 61 | var exporter = new SchemaExport(ServiceProvider.GetRequiredKeyedService("sqlite")); 62 | exporter.Execute(true, false, false); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/SqliteTest.cs: -------------------------------------------------------------------------------- 1 | namespace NHibernate.Extensions.UnitTest; 2 | 3 | [TestFixture] 4 | public class SqliteTest { 5 | 6 | [Test] 7 | public void _01_CanGetScheme() { 8 | var connStr = "Data Source=./test_db.db3;Foreign Keys=True;"; 9 | var conn = new Microsoft.Data.Sqlite.SqliteConnection(connStr); 10 | conn.Open(); 11 | Assert.That(conn, Is.Not.Null); 12 | var schema = conn.GetSchema(); 13 | Assert.That(schema, Is.Not.Null); 14 | conn.Close(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/Actor.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Schema = "public", Table = "actor")] 7 | public class Actor { 8 | 9 | [Id(Name = "ActorId", Column = "actor_id", Type = "long", Generator = "trigger-identity")] 10 | public virtual long ActorId { get; set; } 11 | 12 | [Property(Column = "first_name", Type="string", Length = 45)] 13 | public virtual string FirstName { get; set; } 14 | 15 | [Property(Column = "last_name", Type="string", Length = 45)] 16 | public virtual string LastName { get; set; } 17 | 18 | [Property(Column = "last_update", Type = "datetime")] 19 | public virtual DateTime? LastUpdate { get; set; } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/ArrTestEntity.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Schema = "public", Table = "arr_test")] 7 | public class ArrTestEntity { 8 | [Id(Name = nameof(Id), Column = "id", Type = "long", Generator = "trigger-identity")] 9 | public virtual long Id { get; set; } 10 | [Property(Column = "int_arr", Type = "int[]")] 11 | public virtual int[] IntArr { get; set; } 12 | [Property(Column = "str_arr", Type = "string[]")] 13 | public virtual string[] StrArr { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/Author.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Table = "authors", Schema = "public")] 7 | public class Author { 8 | 9 | [Id(Column = "author_id", Type = "long", Name = "AuthorId", Generator = "trigger-identity")] 10 | public virtual long AuthorId { get; set; } 11 | 12 | [Property(Column = "name", Type = "string")] 13 | public virtual string Name { get; set; } 14 | 15 | [Bag(Table = "books", Inverse = true)] 16 | [Key(Column = "author_id")] 17 | [OneToMany(ClassType = typeof(Book))] 18 | public virtual ICollection Books { get; set; } = new List(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/BaseResource.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Schema = "public", Table="base_resources")] 7 | [Discriminator(Column = "type")] 8 | public class BaseResource { 9 | [Id(Name = nameof(Id), Column = "id", Type = "long", Generator = "trigger-identity")] 10 | public virtual long Id { get; set; } 11 | [Property(Name=nameof(Name), Column = "name", Type = "string", Length = 32, NotNull = true)] 12 | public virtual string Name { get; set; } 13 | [Property(Name=nameof(Type), Column = "type", Type = "string", Length = 64, NotNull = true, Insert = false, Update = false)] 14 | public virtual string Type { get; set; } 15 | } 16 | 17 | [Subclass(DiscriminatorValue = "data_apis", ExtendsType = typeof(BaseResource), Lazy = true)] 18 | public class DataApi : BaseResource { 19 | [Join(Schema = "public", Table = "data_apis", Fetch = JoinFetch.Select)] 20 | [Key(Column = "id")] 21 | [Property(Name = nameof(Statement), Column = "statement", Length = 128, NotNull = true)] 22 | [Property(Name = nameof(Parameters), Column = "parameters", Length = 128, NotNull = true)] 23 | public virtual string Statement { get; set; } 24 | public virtual string Parameters { get; set; } 25 | } 26 | 27 | [Subclass(DiscriminatorValue = "slpks", ExtendsType = typeof(BaseResource), Lazy = true)] 28 | public class Slpk : BaseResource { 29 | [Join(Schema = "public", Table = "slpks", Fetch = JoinFetch.Select)] 30 | [Key(Column = "id")] 31 | [Property(Name = nameof(Path), Column = "path", Length = 128, NotNull = true)] 32 | public virtual string Path { get; set; } 33 | } 34 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/Book.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using MFetchMode = NHibernate.Mapping.Attributes.FetchMode; 3 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 4 | 5 | namespace NHibernate.Extensions.UnitTest.TestDb; 6 | 7 | [Class(Table = "books", Schema = "public")] 8 | public class Book { 9 | 10 | [Id(Name = "BookId", Column = "book_id", Type = "long", Generator = "trigger-identity")] 11 | public virtual long BookId { get; set; } 12 | 13 | [Property(Column = "title", Type = "string")] 14 | public virtual string Title { get; set; } 15 | 16 | [ManyToOne(Column = "author_id", Fetch = MFetchMode.Join)] 17 | public virtual Author Author { get; set; } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/JsonValue.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | using NHibernate.Extensions.Npgsql.UserTypes; 3 | using NHibernate.Mapping.Attributes; 4 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 5 | 6 | namespace NHibernate.Extensions.UnitTest.TestDb; 7 | 8 | 9 | [Class(Schema = "public", Table = "json_values")] 10 | public class JsonValue { 11 | 12 | [Id(Name = nameof(Id), Column = "id", Type = "long", Generator = "trigger-identity")] 13 | public virtual long Id { get; set; } 14 | 15 | [Property(Column = "value", TypeType = typeof(JsonbType))] 16 | public virtual ConnectionString Value { get; set; } 17 | } 18 | 19 | public class ConnectionString { 20 | [JsonPropertyName("server")] 21 | public string Server { get; set; } 22 | [JsonPropertyName("port")] 23 | public int Port { get; set; } 24 | [JsonPropertyName("database")] 25 | public string Database { get; set; } 26 | [JsonPropertyName("username")] 27 | public string Username { get; set; } 28 | [JsonPropertyName("password")] 29 | public string Password { get; set; } 30 | } 31 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/MoniData.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | /// Table, moni_data 7 | [Class(Schema = "public", Table = "moni_data")] 8 | public partial class MoniData { 9 | 10 | /// CompositeId for moni_time,station_id,item_id 11 | [CompositeId] 12 | [KeyProperty(Name = "MoniTime", Column = "moni_time", Type = "datetime" )] 13 | [KeyProperty(Name = "StationId", Column = "station_id", Type = "long" )] 14 | [KeyProperty(Name = "ItemId", Column = "item_id", Type = "long" )] 15 | protected virtual int CompositeId => GetHashCode(); 16 | 17 | /// moni_time, timestamp 18 | public virtual DateTime MoniTime { get; set; } 19 | 20 | /// station_id, int8 21 | public virtual long StationId { get; set; } 22 | 23 | /// item_id, int8 24 | public virtual long ItemId { get; set; } 25 | 26 | /// value, numeric 27 | [Property(Name = "Value", Column = "value", Type = "decimal", NotNull = false)] 28 | public virtual decimal? Value { get; set; } 29 | 30 | /// description, varchar 31 | [Property(Name = "Description", Column = "description", Type = "string", NotNull = false, Length = 64)] 32 | public virtual string Description { get; set; } 33 | 34 | public override int GetHashCode() { 35 | unchecked { 36 | int hashCode; 37 | hashCode = MoniTime.GetHashCode(); 38 | hashCode = (hashCode * 397) ^ StationId.GetHashCode(); 39 | hashCode = (hashCode * 397) ^ ItemId.GetHashCode(); 40 | return hashCode; 41 | } 42 | } 43 | 44 | public override bool Equals(object obj) { 45 | if (ReferenceEquals(null, obj)) { 46 | return false; 47 | } 48 | if (ReferenceEquals(this, obj)) { 49 | return true; 50 | } 51 | if (obj.GetType() != this.GetType()) { 52 | return false; 53 | } 54 | return Equals((MoniData)obj); 55 | } 56 | 57 | public virtual bool Equals(MoniData other) { 58 | if (other == null){ 59 | return false; 60 | } 61 | return this.GetHashCode() == other.GetHashCode(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/NpgTime.cs: -------------------------------------------------------------------------------- 1 | namespace NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | using NHibernate.Mapping.Attributes; 4 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 5 | 6 | [Class(Schema = "public", Table = "npg_times")] 7 | public class NpgTime { 8 | [Id(Name = nameof(Id), Column = "id", Type = "long", Generator = "trigger-identity")] 9 | public virtual long Id { get; set; } 10 | [Property(Name = nameof(LocalTime), Column = "local_time", Type = "timestamp", NotNull = false)] 11 | public virtual DateTime? LocalTime { get; set; } 12 | [Property(Name = nameof(UtcTime), Column = "utc_time", Type = "timestamptz", NotNull = false)] 13 | public virtual DateTime? UtcTime { get; set; } 14 | 15 | public override string ToString() { 16 | return $"{nameof(Id)}: {Id}, {nameof(LocalTime)}: {LocalTime}, {nameof(UtcTime)}: {UtcTime}"; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/SnowFlakeTestEntity.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Table = "snow_flake_test", Schema = "public")] 7 | public class SnowFlakeTestEntity { 8 | 9 | [Id(Name = "Id", Column = "id", Type="long", Generator = "trigger-identity")] 10 | public virtual long Id { get; set; } 11 | 12 | [Property(Column = "name", Type = "string", Length = 32)] 13 | public virtual string Name { get; set; } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/TestEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using NHibernate.Mapping.Attributes; 3 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 4 | 5 | namespace NHibernate.Extensions.UnitTest.TestDb; 6 | 7 | [Class(Table = "test_table", Schema = "public")] 8 | public class TestEntity { 9 | 10 | [Id(Name = nameof(Id), Column = "id", Type = "long", Generator = "trigger-identity")] 11 | public virtual long Id { get; set; } 12 | 13 | [Property(Column = "name", Type = "string", Length = 32)] 14 | public virtual string Name { get; set; } 15 | 16 | [Property(Column = "tags", Type = "string[]")] 17 | public virtual string[] Tags { get; set; } 18 | 19 | [Property(Column = "json_field", Type = "json")] 20 | public virtual JsonElement JsonField { get; set; } 21 | 22 | [Property(Column = "jsonb_field", Type = "jsonb")] 23 | public virtual JsonElement JsonbField { get; set; } 24 | 25 | [Property(Column = "update_time", Type = "timestamp")] 26 | public virtual DateTime? UpdateTime { get; set; } 27 | 28 | [Property(Column = "int16_arr", Type = "short[]")] 29 | public virtual short[] Int16Arr { get; set; } 30 | 31 | [Property(Column = "int32_arr", Type = "int[]")] 32 | public virtual int[] Int32Arr { get; set; } 33 | 34 | [Property(Column = "int64_arr", Type = "long[]")] 35 | public virtual long[] Int64Arr { get; set; } 36 | 37 | [Property(Column = "real_arr", Type = "float[]")] 38 | public virtual float[] FloatArr { get; set; } 39 | 40 | [Property(Column = "double_arr", Type = "double[]")] 41 | public virtual double[] DoubleArr { get; set; } 42 | 43 | [Property(Column = "bool_arr", Type = "bool[]")] 44 | public virtual bool[] BooleanArr { get; set; } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/VersionTable.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Mapping.Attributes; 2 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 3 | 4 | namespace NHibernate.Extensions.UnitTest.TestDb; 5 | 6 | [Class(Schema = "public", Table = "version_table")] 7 | public class VersionTable { 8 | 9 | [Id(Name = "Id", Column = "id", Generator = "trigger-identity")] 10 | public virtual long Id { get; set; } 11 | 12 | [Property(Column = "name", Type = "string", Length = 32)] 13 | public virtual string Name { get; set; } 14 | 15 | [Property(Column = "description", Type = "string", Length = 32)] 16 | public virtual string Description { get; set; } 17 | 18 | [Version(Column = "version", Type = "int")] 19 | public virtual int Version { get; set; } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/TestDb/XmlTestEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Xml; 2 | using NHibernate.Mapping.Attributes; 3 | using PropertyAttribute = NHibernate.Mapping.Attributes.PropertyAttribute; 4 | 5 | namespace NHibernate.Extensions.UnitTest; 6 | 7 | [Class(Schema = "public", Table = "xml_test")] 8 | public class XmlTestEntity { 9 | 10 | 11 | [Id(Name = "Id", Column = "id", Generator = "trigger-identity")] 12 | public virtual long Id { get; set; } 13 | 14 | [Property(Name = "Statement", Column = "statement", Type = "xml", Length = 32)] 15 | public virtual XmlDocument Statement { get; set; } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/VersionTest.cs: -------------------------------------------------------------------------------- 1 | using NHibernate.Extensions.UnitTest.TestDb; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class VersionTest : BaseTest { 7 | 8 | private ISessionFactory factory; 9 | 10 | [OneTimeSetUp] 11 | public override void OneTimeSetUp() { 12 | base.OneTimeSetUp(); 13 | factory = TestDbSessionFactory; 14 | } 15 | 16 | [Test] 17 | public void _01_CanQueryVersionTable() { 18 | using var session = factory.OpenSession(); 19 | var query = session.Query(); 20 | var data = query.ToList(); 21 | Assert.That(data, Is.Not.Empty); 22 | } 23 | 24 | [Test] 25 | public void _02_CanUpdateVersion() { 26 | var entity = new VersionTable { 27 | Name = "test " + DateTime.Now 28 | }; 29 | using (var session = factory.OpenSession()) { 30 | session.Save(entity); 31 | session.Flush(); 32 | Assert.That(entity.Id, Is.GreaterThan(0)); 33 | 34 | Assert.That(entity.Version, Is.GreaterThan(0)); 35 | var v1 = entity.Version; 36 | 37 | entity.Name = "update " + DateTime.Now; 38 | session.Update(entity); 39 | session.Flush(); 40 | var v2 = entity.Version; 41 | 42 | Assert.That(v2, Is.GreaterThan(v1)); 43 | 44 | session.Delete(entity); 45 | session.Flush(); 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/XmlTests.cs: -------------------------------------------------------------------------------- 1 | using System.Xml; 2 | 3 | namespace NHibernate.Extensions.UnitTest; 4 | 5 | [TestFixture] 6 | public class XmlTests : BaseTest { 7 | 8 | [Test] 9 | public void _01_CanQueryXmlEntity() { 10 | using var session = TestDbSessionFactory.OpenSession(); 11 | var entities = session.Query().ToList(); 12 | Assert.That(entities.Count >= 0); 13 | foreach (var entity in entities) { 14 | Console.WriteLine($"id: {entity.Id}"); 15 | Console.WriteLine($"statement: {entity.Statement}"); 16 | } 17 | } 18 | 19 | [Test] 20 | public void _02_CanSaveXmlEntity() { 21 | using var session = TestDbSessionFactory.OpenSession(); 22 | var xmlDoc = new XmlDocument(); 23 | xmlDoc.LoadXml(""); 24 | var entity = new XmlTestEntity { 25 | Statement = xmlDoc 26 | }; 27 | session.Save(entity); 28 | Assert.That(entity.Id, Is.GreaterThan(0)); 29 | Console.WriteLine(entity.Id); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nhibernate": [ 3 | { 4 | "connection.connection_string": "server=127.0.0.1;port=5432;database=test_db;user id=postgres;password=1a2b3c4D;", 5 | "connection.driver_class": "NHibernate.Extensions.Npgsql.NpgsqlDriver,NHibernate.Extensions.Npgsql", 6 | "dialect": "NHibernate.Dialect.PostgreSQL83Dialect", 7 | "show_sql": "true", 8 | "format_sql": "true", 9 | "adonet.batch_size": "100", 10 | "mapping": { 11 | "assembly": ["NHibernate.Extensions.UnitTest"] 12 | } 13 | }, 14 | { 15 | "name": "test2", 16 | "connection.connection_string": "server=127.0.0.1;port=5432;database=test_db;user id=postgres;password=1a2b3c4D;", 17 | "connection.driver_class": "NHibernate.Extensions.Npgsql.NpgsqlDriver,NHibernate.Extensions.Npgsql", 18 | "dialect": "NHibernate.Dialect.PostgreSQL83Dialect", 19 | "show_sql": "true", 20 | "format_sql": "true", 21 | "adonet.batch_size": "10", 22 | "mapping": { 23 | "assembly": ["NHibernate.Extensions.UnitTest"] 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/hibernate.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | server=127.0.0.1;port=5432;database=test_db;user id=postgres;password=aJv6YoG0CkM9YdqP; 5 | NHibernate.Extensions.Npgsql.NpgsqlDriver,NHibernate.Extensions.Npgsql 6 | NHibernate.Extensions.Npgsql.NpgsqlDialect,NHibernate.Extensions.Npgsql 7 | NHibernate.Extensions.Npgsql.LinqToHqlGeneratorsRegistry,NHibernate.Extensions.Npgsql 8 | true 9 | true 10 | 10 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/hibernate.sqlite.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Data Source=./test_db.db3;Foreign Keys=True; 5 | NHibernate.Extensions.Sqlite.SqliteDriver,NHibernate.Extensions.Sqlite 6 | NHibernate.Extensions.Sqlite.SqliteDialect,NHibernate.Extensions.Sqlite 7 | true 8 | true 9 | 10 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/sql/01_test_table.sql: -------------------------------------------------------------------------------- 1 | -- Table: public.test_table 2 | 3 | -- DROP TABLE public.test_table; 4 | 5 | CREATE TABLE public.test_table 6 | ( 7 | id serial NOT NULL, 8 | name character varying(32) COLLATE pg_catalog."default" NOT NULL, 9 | tags character varying[] COLLATE pg_catalog."default", 10 | json_field json, 11 | jsonb_field jsonb, 12 | update_time timestamp without time zone, 13 | int32_arr integer[], 14 | int16_arr smallint[], 15 | int64_arr bigint[], 16 | real_arr real[], 17 | double_arr double precision[], 18 | bool_arr boolean[], 19 | CONSTRAINT pk_test_table PRIMARY KEY (id) 20 | ) 21 | WITH ( 22 | OIDS = FALSE 23 | ) 24 | TABLESPACE pg_default; 25 | 26 | ALTER TABLE public.test_table 27 | OWNER to postgres; 28 | COMMENT ON TABLE public.test_table 29 | IS 'test table'; 30 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/sql/02_snow_flake_id.sql: -------------------------------------------------------------------------------- 1 | -- DROP SEQUENCE public.snow_flake_id_seq; 2 | 3 | CREATE SEQUENCE public.snow_flake_id_seq; 4 | 5 | ALTER SEQUENCE public.snow_flake_id_seq 6 | OWNER TO postgres; 7 | 8 | -- FUNCTION: public.snow_flake_id() 9 | 10 | -- DROP FUNCTION public.snow_flake_id(); 11 | 12 | CREATE OR REPLACE FUNCTION public.snow_flake_id() 13 | RETURNS bigint 14 | LANGUAGE 'sql' 15 | COST 100 16 | VOLATILE 17 | AS $BODY$ 18 | 19 | SELECT (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) * 1000)::bigint * 1000000 20 | + 5 * 10000 21 | + nextval('public.snow_flake_id_seq') % 1000 22 | as snow_flake_id 23 | 24 | $BODY$; 25 | 26 | ALTER FUNCTION public.snow_flake_id() 27 | OWNER TO postgres; 28 | 29 | COMMENT ON FUNCTION public.snow_flake_id() 30 | IS 'snow flake id '; 31 | 32 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/sql/03_snow_flake_test.sql: -------------------------------------------------------------------------------- 1 | -- Table: public.snow_flake_test 2 | 3 | -- DROP TABLE public.snow_flake_test; 4 | 5 | CREATE TABLE public.snow_flake_test 6 | ( 7 | id bigint NOT NULL DEFAULT snow_flake_id(), 8 | name character varying(32) COLLATE pg_catalog."default", 9 | CONSTRAINT snow_flake_test_pkey PRIMARY KEY (id) 10 | ) 11 | WITH ( 12 | OIDS = FALSE 13 | ) 14 | TABLESPACE pg_default; 15 | 16 | ALTER TABLE public.snow_flake_test 17 | OWNER to postgres; 18 | COMMENT ON TABLE public.snow_flake_test 19 | IS 'snow flake test table'; 20 | -------------------------------------------------------------------------------- /test/NHibernate.Extensions.UnitTest/test_db.db3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beginor/nhibernate-extensions/a46a9a2c7543721e9b73448294d2b036a4255b6b/test/NHibernate.Extensions.UnitTest/test_db.db3 --------------------------------------------------------------------------------