@@ -866,6 +866,51 @@ var track = db.SingleById<Track>(1);
866866var tracks = db .SelectByIds <Track >(new []{ 1 ,2 ,3 });
867867```
868868
869+ ### Ensure APIs
870+
871+ The new ` Ensure() ` API on OrmLite's typed ` SqlExpression<T> ` can be used to ensure that a condition is always applied irrespective
872+ of other conditions, e.g:
873+
874+ #### Typed API
875+
876+ ``` csharp
877+ var q = db .From <Rockstar >();
878+ q .Ensure (x => x .Id == 1 ); // always applied
879+
880+ // ...
881+ q .Where (x => x .Age == 27 );
882+ q .Or (x => x .LivingStatus == LivingStatus .Dead );
883+
884+ var rows = db .Select (q );
885+ ```
886+
887+ #### Custom Parameterized SQL Expression
888+
889+ Custom SQL Ensure parameterized expressions:
890+
891+ ``` csharp
892+ q .Ensure (" Id = {0}" , 1 );
893+ ```
894+
895+ #### Multiple Ensure expressions
896+
897+ ``` csharp
898+ var q = db
899+ .From <Rockstar >()
900+ .Join <RockstarAlbum >((r ,a ) => r .Id == a .RockstarId );
901+
902+ q .Ensure <Rockstar ,RockstarAlbum >((r ,a ) => a .Name == " Nevermind" && r .Id == a .RockstarId );
903+
904+ q .Where (x => x .Age == 27 )
905+ .Or (x => x .LivingStatus == LivingStatus .Dead );
906+
907+ q .Ensure (x => x .Id == 3 );
908+
909+ var rows = db .Select (q );
910+ ```
911+
912+ These APIs are useful for mandatory filters like "Soft Deletes" and Multitenant records.
913+
869914## Nested Typed Sub SqlExpressions
870915
871916The ` Sql.In() ` API supports nesting and combining of multiple Typed SQL Expressions together
@@ -898,23 +943,39 @@ var names = new List<string>{ "foo", "bar", "qux" };
898943var results = db .SqlList <Table >(" SELECT * FROM Table WHERE Name IN (@names)" , new { names });
899944```
900945
946+ ### Spread Util
947+
948+ The ` SqlSpread() ` API is useful to generate an escaped list of parameterized values for use in SQL ` IN() ` statements and SQL functions:
949+
950+ ``` csharp
951+ var dialect = db .Dialect ();
952+ dialect .SqlSpread (1 , 2 , 3 ); // = 1,2,3
953+ dialect .SqlSpread (" A" , " B" , " C" ); // = 'A','B','C'
954+ dialect .SqlSpread (" A'B" , " C\" D" ); // = 'A''B','C\"D'
955+ ```
956+
901957### Custom SQL using PostgreSQL Arrays
902958
903- If using PostgreSQL you can take advantage of its complex Array Types and utilize its [ Array Functions and Operators ] ( https://www.postgresql.org/docs/9.6/functions-array .html ) , e.g:
959+ The ` PgSql.Array() ` provides a typed API for generating [ PostgreSQL Array Expressions ] ( https://www.postgresql.org/docs/current/arrays .html ) , e.g:
904960
905961``` csharp
906- var ids = new []{ 1 , 2 , 3 };
907- var q = Db .From <Table >()
908- .Where (" ARRAY[{0}] && ref_ids" , ids .Join (" ," ))
909- var results = db .Select (q );
962+ PgSql .Array (1 ,2 ,3 ) // = ARRAY[1,2,3]
963+ var strings = new []{ " A" ," B" ," C" };
964+ PgSql .Array (strings ) // = ARRAY['A','B','C']
910965```
911966
912- When comparing a string collection you can use ` SqlInValues ` to create a quoted SQL IN list, e.g :
967+ Which you can safely use in Custom SQL Expressions that use PostgreSQL's native ARRAY support :
913968
914969``` csharp
915- var q = Db .From <Table >()
916- .Where ($" ARRAY[{new SqlInValues (cities ).ToSqlInString ()}] && cities" );
917- var results = db .Select (q );
970+ q .And ($" {PgSql .Array (anyTechnologyIds )} && technology_ids" )
971+ q .And ($" {PgSql .Array (labelSlugs )} && labels" );
972+ ```
973+
974+ If you want and empty collection to return ` null ` instead of an empty ` ARRAY[] ` you can use the ` nullIfEmpty ` overload:
975+
976+ ``` csharp
977+ PgSql .Array (new string [0 ], nullIfEmpty :true ) // = null
978+ PgSql .Array (new []{" A" ," B" ," C" }, nullIfEmpty :true ) // = ARRAY['A','B','C']
918979```
919980
920981### Lazy Queries
@@ -972,7 +1033,7 @@ var q = db.From<Customer>()
9721033var dbCustomers = db .Select <Customer >(q );
9731034```
9741035
975- This query rougly maps to the following SQL:
1036+ This query roughly maps to the following SQL:
9761037
9771038``` sql
9781039SELECT Customer.*
@@ -1338,6 +1399,59 @@ Custom Key/Value Dictionary:
13381399Dictionary < string ,string > rows = db .Dictionary <string ,string >(q );
13391400```
13401401
1402+ ### Dictionary APIs
1403+
1404+ OrmLite's Dictionary APIs allow you to customize which parts of a Data Model should be modified by
1405+ converting it into then manipulating an Object Dictionary, e.g:
1406+
1407+ #### Insert by Dictionary
1408+
1409+ ``` csharp
1410+ var row = new Person { FirstName = " John" , LastName = " Smith" };
1411+ Dictionary < string ,object > obj = row .ToObjectDictionary ();
1412+ obj [nameof (Person .LastName )] = null ;
1413+
1414+ row .Id = (int ) db .Insert <Person >(obj , selectIdentity :true );
1415+ ```
1416+
1417+ #### Update by Dictionary
1418+
1419+ ``` csharp
1420+ Person row = db .SingleById <Person >(row .Id );
1421+ var obj = row .ToObjectDictionary ();
1422+ obj [nameof (Person .LastName )] = " Smith" ;
1423+ db .Update <Person >(obj );
1424+ ```
1425+
1426+ #### UpdateOnly by Dictionary
1427+
1428+ ``` csharp
1429+ // By Primary Key Id
1430+ var fields = new Dictionary <string , object > {
1431+ [nameof (Person .Id )] = 1 ,
1432+ [nameof (Person .FirstName )] = " John" ,
1433+ [nameof (Person .LastName )] = null ,
1434+ };
1435+
1436+ db .UpdateOnly <Person >(fields );
1437+
1438+ // By Custom Where Expression
1439+ var fields = new Dictionary <string , object > {
1440+ [nameof (Person .FirstName )] = " John" ,
1441+ [nameof (Person .LastName )] = null ,
1442+ };
1443+
1444+ db .UpdateOnly <Person >(fields , p => p .LastName == " Hendrix" );
1445+ ```
1446+
1447+ #### Delete by Dictionary
1448+
1449+ ``` csharp
1450+ db .Delete <Rockstar >(new Dictionary <string , object > {
1451+ [" Age" ] = 27
1452+ });
1453+ ```
1454+
13411455### BelongsTo Attribute
13421456
13431457The ` [BelongTo] ` attribute can be used for specifying how Custom POCO results are mapped when the resultset is ambiguous, e.g:
@@ -2247,6 +2361,41 @@ CREATE TABLE "PocoTable"
22472361
22482362> OrmLite replaces any variable placeholders with the value in each RDBMS DialectProvider's ` Variables ` Dictionary.
22492363
2364+ ### Custom Insert and Update Expressions
2365+
2366+ The ` [CustomInsert] ` and ` [CustomUpdate] ` attributes can be used to override what values rows are inserted during INSERT's and UPDATE's.
2367+
2368+ We can use this to insert a salted and hashed password using PostgreSQL native functions:
2369+
2370+ ``` csharp
2371+ public class CustomSqlUser
2372+ {
2373+ [AutoIncrement ]
2374+ public int Id { get ; set ; }
2375+
2376+ public string Email { get ; set ; }
2377+
2378+ [CustomInsert (" crypt({0}, gen_salt('bf'))" ),
2379+ CustomUpdate (" crypt({0}, gen_salt('bf'))" )]
2380+ public string Password { get ; set ; }
2381+ }
2382+
2383+ var user = new CustomSqlUser {
2384+ 2385+ Password = " secret"
2386+ };
2387+ db .Insert (user );
2388+ ```
2389+
2390+ We can then use ` Sql.Custom() ` to create a partially typed custom query to match on the hashed password, e.g:
2391+
2392+ ``` csharp
2393+ var quotedSecret = db .Dialect ().GetQuotedValue (" secret" );
2394+ var q = db .From <CustomSqlUser >()
2395+ .Where (x => x .Password == Sql .Custom ($" crypt({quotedSecret }, password)" ));
2396+ var row = db .Single (q );
2397+ ```
2398+
22502399#### Pre / Post Custom SQL Hooks when Creating and Dropping tables
22512400
22522401Pre / Post Custom SQL Hooks allow you to inject custom SQL before and after tables are created or dropped, e.g:
@@ -3155,6 +3304,22 @@ PostgreSqlDialect.Provider.RegisterConverter<List<DateTime>>(new PostgreSqlDateT
31553304PostgreSqlDialect .Provider .RegisterConverter <List <DateTimeOffset >>(new PostgreSqlDateTimeOffsetTimeStampTzArrayConverter ());
31563305```
31573306
3307+ ### PostgreSQL Params
3308+
3309+ The ` PgSql.Param() ` API provides a resolve the correct populated ` NpgsqlParameter ` and ` NpgsqlDbType ` from a C# Type
3310+ which can be used to query custom PostgreSQL Data Types in APIs that accept ` IDbDataParameter ` parameters, e.g:
3311+
3312+ ``` csharp
3313+ public class FunctionResult
3314+ {
3315+ public int [] Val { get ; set ; }
3316+ }
3317+
3318+ var p = PgSql .Param (" paramValue" , testVal );
3319+ var sql = " SELECT * FROM my_func(@paramValue)" ;
3320+ var rows = db .Select <FunctionResult >(sql , new [] { p });
3321+ ```
3322+
31583323### Hstore support
31593324
31603325To use ` hstore ` , its extension needs to be enabled in your PostgreSQL RDBMS by running:
0 commit comments