{"id":21245081,"url":"https://github.com/hnx8/dapperaid","last_synced_at":"2026-03-08T14:39:18.437Z","repository":{"id":107208305,"uuid":"179824506","full_name":"hnx8/DapperAid","owner":"hnx8","description":"SQL CRUD Query-Builder/Executor for Dapper","archived":false,"fork":false,"pushed_at":"2023-09-20T23:26:43.000Z","size":262,"stargazers_count":6,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-29T12:03:08.136Z","etag":null,"topics":["c-sharp","crud","csharp","dapper","database","mappings","orm","query-builder","sql-generation"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hnx8.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-06T11:25:10.000Z","updated_at":"2024-06-16T04:35:36.171Z","dependencies_parsed_at":null,"dependency_job_id":"b315118d-99a0-4414-8da7-8e34181e2374","html_url":"https://github.com/hnx8/DapperAid","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hnx8%2FDapperAid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hnx8%2FDapperAid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hnx8%2FDapperAid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hnx8%2FDapperAid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hnx8","download_url":"https://codeload.github.com/hnx8/DapperAid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225658521,"owners_count":17503663,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","crud","csharp","dapper","database","mappings","orm","query-builder","sql-generation"],"created_at":"2024-11-21T01:46:50.868Z","updated_at":"2026-03-08T14:39:18.410Z","avatar_url":"https://github.com/hnx8.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DapperAid\nDapperAidは、[Dapper](https://github.com/StackExchange/Dapper)によるデータベースのCRUD操作を支援するSQL自動生成・実行ライブラリです。\n- データベースのSelect, Insert, Update, Deleteの操作を、IDbConnection / IDbTransactionの拡張メソッドとして提供します。\n- 実行SQLは、POCOオブジェクトに付与した属性に基づき、内蔵のクエリビルダが自動生成します。\n- 実行SQLのWhere条件は、POCOオブジェクトのKey項目の値、または、ラムダ式（式木）の記述をもとに生成されます。\n- 属性付与/メソッド引数指定により、生成実行されるSQLの内容をカスタマイズできます。  \n（必要な部分だけ手書きのSQLを混在させることもある程度可能です）\n  - Select時のfor update指定、orderby列指定、offset / limit条件、groupby要否、distinct指定など\n  - Select, Insert, Update対象とするカラムの限定\n  - Insert時 / Update時の設定値(設定せずDBデフォルト値に任せることも可)\n  - Insert時のIdentity/AutoIncrement自動採番値把握(各DBMS対応)\n- その他オプション機能（使用任意）:\n  - 簡易コードファースト（POCO定義内容からCreateTableのSQLを生成）\n  - SQL実行ログ取得（クエリビルダが生成したSQLの内容をトレース確認可能）\n- 対応DBMS： Oracle, MySQL, Postgres, SQLite, SQLServer, MS-Access, DB2\n\nDapperAid is a SQL automatic generation and execution library that assists database CRUD operation using [Dapper](https://github.com/StackExchange/Dapper).\n- Provides Select, Insert, Update and Delete operations of the database as extension methods of IDbConnection / IDbTransaction.\n- Execution SQL is automatically generated by the built-in query builder based on the attribute given to the POCO object.\n- The execution SQL Where condition is generated based on the value of the key item of POCO object or the description of lambda expression (expression tree).\n- You can customize the contents of generated SQL by specifying attribute assignment / method argument specification.  \n(It is also possible to mix handwritten SQL in specific places.)\n  - Row-Lock, Order-by, offset / limit conditions, need of group-by, specification of distinct and so on at the time of Select\n  - Select / Insert / Update only specific columns\n  - Setting value at Insert / Update (It is also possible to leave it to the DB default value without setting)\n  - Retrieve inserted Identity / AutoIncrement value (for each DBMS)\n- Other extra features (use is optional) :\n  - A little code-first (Generate Create-Table SQL from POCO definition contents)\n  - SQL execution log acquisition (It is possible to view the SQL generated by the query builder)\n\n# Installation\nfrom NuGet  https://www.nuget.org/packages/DapperAid\n```\nPM\u003e Install-Package DapperAid\n```\n```\n\u003e dotnet add package DapperAid\n```\n\n# Examples\n## Sample table\n```cs\nusing System;\nusing System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing DapperAid.DataAnnotations;\n\n[Table(\"Members\")]\n[SelectSql(DefaultOtherClauses = \"order by Id\")]\nclass Member\n{\n    [Key]\n    [InsertValue(false, RetrieveInsertedId = true)]\n    [DapperAid.Ddl.DDL(\"INTEGER\")] // (for extra feature, generating Create-Table-SQL as SQLite Identity Column)\n    public int Id { get; set; }\n\n    public string Name { get; set; }\n\n    [Column(\"Phone_No\")]\n    public string Tel { get; set; }\n\n    [InsertValue(\"CURRENT_TIMESTAMP\"), UpdateValue(false)]\n    public DateTime? CreatedAt { get; set; }\n\n    [InsertValue(\"CURRENT_TIMESTAMP\"), UpdateValue(\"CURRENT_TIMESTAMP\")]\n    public DateTime? UpdatedAt { get; private set; }\n\n    [NotMapped]\n    public string TemporaryPassword { get; set; }\n}\n```\n- Members declared as \"Property\" are subject to automatic SQL generation / execution.\n  - A Readonly-property can only be specified as a Where-clause-column or update value.\n  - A Writeonly-Property can only be specified as a Selection column.\n- See [About Table Attributes](#attributes) for attribute details.\n\n## Initializing\n```cs\nusing DapperAid;\n\nQueryBuilder queryBuilderInstance = new QueryBuilder.Sqlite(); // (example for SQLite)\n```\nCreate an instance corresponding to your DBMS from below.\n\u003ca id=\"querybuilders\"\u003e\u003c/a\u003e\n  - new QueryBuilder.Oracle()\n  - new QueryBuilder.MySql()\n  - new QueryBuilder.Postgres()\n  - new QueryBuilder.SQLite()\n  - new QueryBuilder.SqlServer()\n  - new QueryBuilder.MsAccess()\n  - new QueryBuilder.DB2()\n\n  These instance generates appropriate SQL statement for your DBMS.  \n  (You can also customize the QueryBuilder class as needed)\n\n  If you want to tie an instance only to a specific DB connection, write as follows.\n```cs\n// When linking with a DB connection object\nconnection.UseDapperAid(queryBuilderInstance);\n\n// When linking with a DB connection string\nqueryBuilderInstance.MapDbConnectionString(yourDbDataSource.ConnectionString);\n```\n\n## Executing CRUD \n```cs\nusing System.Collections.Generic;\nusing System.Data;\n\nIDbConnection connection;\n```\n### `Select\u003cT\u003e([ where[, targetColumns][, otherClauses]])` : returns list\u0026lt;T\u0026gt;\n```cs\n    IReadOnlyList\u003cMember\u003e list1 = connection.Select\u003cMember\u003e();\n    // -\u003e select (all columns) from Members order by Id\n\n    IReadOnlyList\u003cMember\u003e list2 = connection.Select\u003cMember\u003e(\n        r =\u003e r.Name == \"TEST\");\n    // -\u003e select (all columns) from Members where \"Name\"=@Name(=\"TEST\") order by Id\n\n    IReadOnlyList\u003cMember\u003e list3 = connection.Select\u003cMember\u003e(\n        r =\u003e r.Name != \"TEST\", \n        r =\u003e new { r.Id, r.Name });\n    // -\u003e select \"Id\", \"Name\" from Members where \"Name\"\u003c\u003e@Name order by Id\n\n    IReadOnlyList\u003cMember\u003e list4 = connection.Select\u003cMember\u003e(\n        r =\u003e r.Tel != null,\n        $\"ORDER BY {nameof(Member.Name)} LIMIT 5 OFFSET 10\");\n    // -\u003e select (all columns) from Members where Phone_No is not null\n    //           ORDER BY Name LIMIT 5 OFFSET 10\n\n    IReadOnlyList\u003cMember\u003e list5 = connection.Select\u003cMember\u003e(\n        r =\u003e r.Tel != null,\n        r =\u003e new { r.Id, r.Name },\n        $\"ORDER BY {nameof(Member.Name)} LIMIT 5 OFFSET 10\");\n    // -\u003e select \"Id\", \"Name\" from Members where Phone_No is not null\n    //           ORDER BY Name LIMIT 5 OFFSET 10\n```\n### `SelectFirst\u003cT\u003e([ where[, targetColumns][, otherClauses]])` : returns one row or exception\n### `SelectFirstOrDefault\u003cT\u003e([ where[, targetColumns][, otherClauses]])` : returns one row or null\n```cs\n    Member first1 = connection.SelectFirst\u003cMember\u003e();\n    // -\u003e Execute connection.QueryFirst\u003cMember\u003e(sql) instead of connection.Query\u003cMember\u003e(sql).\n\n    Member? firstOrDefault1 = connection.SelectFirstOrDefault\u003cMember\u003e();\n    // -\u003e Execute connection.QueryFirstOrDefault\u003cMember\u003e(sql) instead of connection.Query\u003cMember\u003e(sql).\n\n    Member? selectForUpdate = connection.SelectFirst\u003cMember\u003e(\n        r =\u003e r.Id == 1,\n        otherClauses: \"FOR UPDATE\");\n    // -\u003e select (all columns) from Members where \"Id\"=@Id FOR UPDATE\n```\n### `Select\u003cTFrom, TColumns\u003e([ where[, otherClauses]])` : returns list\u0026lt;TColumns\u0026gt;\n```cs\n    class SelColumns {\n        public string Name { get; private set; }\n        public string Tel { get; private set; }\n        [Column(\"CURRENT_TIMESTAMP\")]\n        public DateTime Now { get; set; }\n    }\n\n    IReadOnlyList\u003cSelColumns\u003e listS1 = connection.Select\u003cMember, SelColumns\u003e(\n        r =\u003e r.Tel != null\n    );\n    // -\u003e select \"Name\", Phone_No as \"Tel\", CURRENT_TIMESTAMP as \"Now\"\n    //           from Members where Phone_No is not null order by Id\n```\n### `SelectFirst\u003cTFrom, TColumns\u003e([ where[, otherClauses]])` : returns one row or exception\n### `SelectFirstOrDefault\u003cTFrom, TColumns\u003e([ where[, otherClauses]])` : returns one row or null\n```cs\n    SelColumns first2 = connection.SelectFirst\u003cMember, SelColumns\u003e(\n        r =\u003e r.Tel == null\n    );\n    // -\u003e Execute connection.QueryFirst\u003cSelColumns\u003e(sql) instead of connection.Query\u003cSelColumns\u003e(sql).\n\n    SelColumns? firstOrDefault2 = connection.SelectFirstOrDefault\u003cMember, SelColumns\u003e(\n        r =\u003e r.Tel == null\n    );\n    // -\u003e Execute connection.QueryFirstOrDefault\u003cSelColumns\u003e(sql) instead of connection.Query\u003cSelColumns\u003e(sql).\n```\n### `Select(by Key [, targetColumns[, otherClauses]])` : returns one row or null\n```cs\n    Member? select1 = connection.Select(\n        () =\u003e new Member { Id = 1 });\n    // -\u003e select \"Id\", \"Name\", Phone_No as \"Tel\", \"CreatedAt\", \"UpdatedAt\" from Members where \"Id\"=@Id(=1)\n\n    Member? select2 = connection.Select(\n        () =\u003e new Member { Id = 1 },\n        r =\u003e new { r.Id, r.Name });\n    // -\u003e select \"Id\", \"Name\" from Members where \"Id\"=@Id\n\n    Member? selectForUpdate = connection.Select(\n        () =\u003e new Member { Id = 1 },\n        otherClauses: \"FOR UPDATE\");\n    // -\u003e select (all columns) from Members where \"Id\"=@Id FOR UPDATE\n```\n### `Count\u003cT\u003e([where])` : returns the number of rows\n```cs\n    ulong count1 = connection.Count\u003cMember\u003e();\n    // -\u003e select count(*) from Members\n\n    ulong count2 = connection.Count\u003cMember\u003e(\n        r =\u003e (r.Id \u003e= 3 \u0026\u0026 r.Id \u003c= 9));\n    // -\u003e select count(*) from Members where \"Id\"\u003e=@Id(=3) and \"Id\"\u003c=@P01(=9)\n```\n### `Insert(record[, targetColumns])` : returns 1(inserted row)\n```cs\n    var rec1 = new Member { Name = \"InsertTest\", Tel = \"177\" };\n    int insert1 = connection.Insert(rec1);\n    // -\u003e insert into Members(\"Name\", Phone_No, \"CreatedAt\", \"UpdatedAt\")  \n    //                values (@Name, @Tel, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n\n    var rec2 = new Member { Name = \"ParticularColumnOnly1\", CreatedAt = null };\n    int insert2 = connection.Insert(rec2,\n        r =\u003e new { r.Name, r.CreatedAt });\n    // -\u003e insert into Members(\"Name\", \"CreatedAt\") values (@Name, @CreatedAt(=null))\n```\n### `InsertAndRetrieveId(record[, targetColumns])` : returns 1(inserted row)\n```cs\n    var rec3 = new Member { Name = \"IdentityTest\", Tel = \"7777\" };\n    int insert3 = connection.InsertAndRetrieveId(rec3);\n    // -\u003e insert into Members(\"Name\", Phone_No, \"CreatedAt\", \"UpdatedAt\")\n    //    values (@Name, @Tel, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ; select LAST_INSERT_ROWID()\n    Trace.WriteLine(\"insertedID=\" + rec3.Id); // The value assigned to the \"Id\" column is set\n```\n- Note: In these examples, the [[InsertValue](#InsertValueattribute)] attribute is specified that  \n  the \"Id\" column is autoincrement and obtains the registered value.\n\n### `Insert(specifiedColumnValue)` : returns 1(inserted row)\n```cs\n    int insertX = connection.Insert(\n        () =\u003e new Member { Id = 888, Name = \"ParticularColumnOnly2\" });\n    // -\u003e insert into Members(\"Id\", \"Name\") values (@Id, @Name)\n```\n\n### `InsertRows(records[, targetColumns])` : returns the number of inserted rows\n```cs\n    int insertMulti = connection.InsertRows(new[] {\n        new Member { Name = \"MultiInsert1\", Tel = null },\n        new Member { Name = \"MultiInsert2\", Tel = \"999-999-9999\" },\n        new Member { Name = \"MultiInsert3\", Tel = \"88-8888-8888\" },\n    });\n    // -\u003e insert into Members(\"Name\", Phone_No, \"CreatedAt\", \"UpdatedAt\") values\n    //   ('MultiInsert1', null, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),\n    //   ('MultiInsert2', '999-999-9999', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),\n    //   ('MultiInsert3', '88-8888-8888', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n```\n- Note: MultiRow-Insert is a SQL92 feature and is supported by DB2, MySQL, PostgreSQL, SQL Server, SQLite (3.7.11 or later), Oracle (\"INSERT ALL\" statement), and so on. \n- Note: If there are many records (by default, more than 1000), the query will be executed in multiple statements. You can use the `queryBuilder.MultiInsertRowsPerQuery` property to change the number of records inserted by a single query.\n\n### \u003ca id=\"insertorupdate\"\u003e\u003c/a\u003e`InsertOrUpdate(record[, insertTargetColumns[, updateTargetColumns]])` : returns 1(inserted or updated row)\n```cs\n    var upsertRow = new Member { Id = 1, Name = \"UpsertTest\", Tel = \"7777\" };\n    int upsertSingle = connection.InsertOrUpdate(upsertRow);\n    // -\u003e insert into Members(\"Id\", \"Name\", Phone_No, \"CreatedAt\", \"UpdatedAt\")\n    //    values (@Id, @Name, @Tel, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)\n    //    on conflict(\"Id\") do update set \"Name\"=excluded.\"Name\", ...\n```\n- Note: This method generates the [UpSert(Merge)](https://en.wikipedia.org/wiki/Merge_(SQL)) statement. If the record already exists, the record is updated. Otherwise, a record is inserted. The generated SQL differs for each DBMS: \"MERGE INTO\" for SQLServer, Oracle, and DB2; \"INSERT ... ON CONFLICT UPDATE\" for Postgres and SQLite; \"INSERT ... ON DUPLICATE KEY UPDATE \" for MySQL.\n- Note: If you set the `queryBuilder.SupportsUpsert` property to false, updates will be performed using simple Update and Insert statements instead of Upsert(Merge).  \nIf you are using a DBMS that does not support UpSert such as SQLite less than 3.24(2018-06-04), PostgreSQL less than 9.5(2016-01-07), MS-Access, please set the property to false.\n\n### `InsertOrUpdateRows(records[, insertTargetColumns[, updateTargetColumns]])` : returns the number of inserted (or updated) rows\n```cs\n    var upsertData = new[] {\n        new Dept { Code = 110, Name = \"Sales\"},\n        new Dept { Code = 120, Name = \"Marketing\"},\n        new Dept { Code = 130, Name = \"Publicity\"},\n    };\n    int upsertMulti = connection.InsertOrUpdateRows(upsertData);\n    // -\u003e insert into Dept(\"Code\", \"Name\", ....)\n    //    values (110, \"Sales\", ...), (120, \"Marketing\", ...), (130, \"Publicity\", ...)\n    //    on conflict(\"Code\") do update set \"Name\"=excluded.\"Name\", .... ..\n```\n- Note: This method genetares and executes UpSert(Merge) statement. See also [`InsertOrUpdate()`](#insertorupdate).\n\n\n### `Update(record[, targetColumns])` : returns the number of updated rows\n```cs\n    var rec1 = new Member { Id = 555, ... };\n    int update1 = connection.Update(rec1);\n    // update Members set \"Name\"=@Name, Phone_No=@Tel, \"UpdatedAt\"=CURRENT_TIMESTAMP where \"Id\"=@Id\n\n    var rec2 = new Member { Id = 666, Tel = \"123-456-7890\" };\n    int update2 = connection.Update(rec2, r =\u003e new { r.Tel });\n    // -\u003e update Members set Phone_No=@Tel where \"Id\"=@Id\n```\n### `Update(specifiedColumnValue, where)` : returns the number of updated rows\n```cs\n    int update3 = connection.Update(\n        () =\u003e new Member { Name = \"updateName\" },\n        r =\u003e r.Tel == \"55555-5-5555\");\n    // -\u003e update Members set \"Name\"=@Name where Phone_No=@Tel\n```\n### `Delete(record)` : returns the number of deleted rows\n```cs\n    var delRec = new Member { Id = 999, ... };\n    int delete1 = connection.Delete(delRec);\n    // -\u003e delete from Members where \"Id\"=@Id\n```\n### `Delete\u003cT\u003e(where)` : returns the number of deleted rows\n```cs\n    int delete2 = connection.Delete\u003cMember\u003e(\n        r =\u003e r.Name == null);\n    // -\u003e delete from Members where \"Name\" is null\n```\n### `Truncate\u003cT\u003e()`\n```cs\n    connection.Truncate\u003cMember\u003e();\n    // -\u003e truncate table Members \n    //    (For DBMS without \"truncate\" syntax, execute delete instead)\n```\n### (Extra) `DDLAttribute.GenerateCreateSQL\u003cT\u003e()` : returns the \"create table\" script\n```cs\nusing DapperAid.Ddl;\n\n    var createTableSql = DDLAttribute.GenerateCreateSQL\u003cMember\u003e();\n    // -\u003e  create table Members\n    //     (\n    //      \"Id\" INTEGER,\n    //      \"Name\",\n    //      Phone_No,\n    //      \"CreatedAt\",\n    //      \"UpdatedAt\",\n    //      primary key( \"Id\")\n    //     )\n    connection.Execute(createTableSql);\n```\n- Note: If you use this feature, you should describe [[DDL](#ddlattribute)] attribute in each column  \n        and specify database column types, constraints, default values, etc.\n- Note: `DDLAttribute.GenerateTableDefTSV\u003cT\u003e()` method is also provided, and returns tab-delimited text of table definition contents.\n\n### (Extra) `LoggableDbConnection` class\n```cs\nusing System.Data;\nusing System.Data.SQLite; // (example for SQLite)\nusing DapperAid.DbAccess;\n\nIDbConnection GetYourDbConnection()\n{\n    // Prepare a normal DB connection \n    var connectionSb = new SQLiteConnectionStringBuilder { DataSource = \":memory:\" };\n    var conn = new SQLiteConnection(connectionSb.ToString());\n    conn.Open();\n\n    // Set into LoggableDbConnection object\n    return new LoggableDbConnection(conn,\n        errorLogger: (Exception ex, DbCommand cmd) =\u003e\n        {   // Write Error Log\n            Trace.WriteLine(ex.ToString() + (cmd != null ? \":\" + cmd.CommandText : null));\n        },\n        traceLogger: (string resultSummary, long mSec, DbCommand cmd) =\u003e\n        {   // Write SQL Execution Trace Log\n            Trace.WriteLine(resultSummary + \"(\" + mSec + \"ms)\" + (cmd != null ? \":\" + cmd.CommandText : null));\n        });\n}\n```\n- The log method specified in the argument is called when SQL is executed / error occurs.  \nBy using this, you can check the contents of the SQL generated by DapperAid.  \nImplement it to be logged.\n\n\n# About Where Clause\nExpression trees in LambdaExpression is converted to SQL search condition.  \nCondition values are bound to parameters.  \n## Comparison Operator\n```cs\n    int? val1 = 100; // (bound to @IntCol)\n    .Select\u003cT\u003e(t =\u003e t.IntCol == val1); // -\u003e where \"IntCol\"=@IntCol\n    .Select\u003cT\u003e(t =\u003e t.IntCol != val1); // -\u003e where \"IntCol\"\u003c\u003e@IntCol\n    .Select\u003cT\u003e(t =\u003e t.IntCol \u003c val1); // -\u003e where \"IntCol\"\u003c@IntCol\n    .Select\u003cT\u003e(t =\u003e t.IntCol \u003e val1); // -\u003e where \"IntCol\"\u003e@IntCol\n    .Select\u003cT\u003e(t =\u003e t.IntCol \u003c= val1); // -\u003e where \"IntCol\"\u003c=@IntCol\n    .Select\u003cT\u003e(t =\u003e t.IntCol \u003e= val1); // -\u003e where \"IntCol\"\u003e=@IntCol\n\n    // If the value is null, SQL is also generated as \"is\"\n    int? val2 = null; \n    .Select\u003cT\u003e(t =\u003e t.IntCol == val2); // -\u003e where \"IntCol\" is null\n    .Select\u003cT\u003e(t =\u003e t.IntCol != val2); // -\u003e where \"IntCol\" is not null\n\n    // can also compare columns and columns.\n    .Select\u003cT\u003e(t =\u003e t.IntCol == t.OtherCol); // -\u003e where \"IntCol\"=\"OtherCol\"\n```\nSQL-specific comparison operators `in`, `like`, and `between` are also supported.\n```cs\nusing DapperAid; // uses \"SqlExpr\" static class\n\n    string[] inValues = {\"111\", \"222\", \"333\"}; // (bound to @TextCol)\n    .Select\u003cT\u003e(t =\u003e t.TextCol == SqlExpr.In(inValues)); // -\u003e where \"TextCol\" in @TextCol\n    \n    string likeValue = \"%test%\"; // (bound to @TextCol)\n    .Select\u003cT\u003e(t =\u003e t.TextCol == SqlExpr.Like(likeValue)); // -\u003e where \"TextCol\" like @TextCol\n\n    int b1 = 1; // (bound to @IntCol)\n    int b2 = 99; // (bound to @P01)\n    .Select\u003cT\u003e(t =\u003e t.IntCol == SqlExpr.Between(b1, b2)); // -\u003e where \"IntCol\" between @IntCol and @P01\n\n    // when \"!=\" is used, SQL is also generated as \"not\"\n    .Select\u003cT\u003e(t =\u003e t.TextCol != SqlExpr.In(inValues)); // -\u003e where \"TextCol\" not in @TextCol\n```\n- Note: IN conditionals are further expanded by Dapper's List Support feature.\n    \n## Logical Operator\nSupports And(`\u0026\u0026`), Or(`||`), Not(`!`).\n```cs\n    .Select\u003cT\u003e(t =\u003e t.TextCol == \"111\" \u0026\u0026 t.IntCol \u003c 200);\n    // -\u003e where \"TextCol\"=@TextCol and \"IntCol\"\u003c@IntCol\n\n    .Select\u003cT\u003e(t =\u003e t.TextCol == \"111\" || t.IntCol \u003c 200);\n    // -\u003e where (\"TextCol\"=@TextCol) or (\"IntCol\"\u003c@IntCol)\n\n    .Select\u003cT\u003e(t =\u003e !(t.TextCol == \"111\" || t.IntCol \u003c 200));\n    // -\u003e where not((\"TextCol\"=@TextCol) or (\"IntCol\"\u003c@IntCol))\n```\nIt can also be combined with the condition judgment not based on SQL.\n```cs\n    // The part where the boolean value is found in advance is not converted to SQL, and is omitted\n    string text1 = \"111\";\n    .Select\u003cT\u003e(t =\u003e text1 == null || t.TextCol == text1); // -\u003e where \"TextCol\"=@TextCol\n    .Select\u003cT\u003e(t =\u003e text1 != null \u0026\u0026 t.TextCol == text1); // -\u003e where \"TextCol\"=@TextCol\n\n    // If the result is determined only by the left side, SQL is not generated\n    string text2 = null;\n    .Select\u003cT\u003e(t =\u003e text2 == null || t.TextCol == text2); // -\u003e where true\n    .Select\u003cT\u003e(t =\u003e text2 != null \u0026\u0026 t.TextCol == text2); // -\u003e where false\n```\nTernary operators (cond ? trueCond : falseCond) are also supported.\n```cs\n    int intVal = -1;\n    .Select(t.CondCol == 1 ? t.IntCol \u003e intVal : t.IntCol \u003c intVal) // -\u003e where ((\"CondCol\"=1 and \"IntCol\"\u003e@IntCol) or (\"CondCol\"\u003c\u003e1 and \"IntCol\"\u003c@IntCol))`\n    .Select(intVal \u003c 0 ? t.IntCol == null : t.IntCol \u003e intVal) // -\u003e where \"IntCol\"\u003e@IntCol\n    .Select(intVal \u003e 0 ? t.IntCol == null : t.IntCol \u003e intVal) // -\u003e where \"IntCol\" is null`\n```\n\n\n## SQL direct description\nYou can describe conditional expressions and subqueries directly.\n```cs\nusing DapperAid; // uses \"SqlExpr\" static class\n\n    .Select\u003cT\u003e(t =\u003e t.TextCol == SqlExpr.In\u003cstring\u003e(\"select text from otherTable where...\"));\n    // --\u003e where \"TextCol\" in(select text from otherTable where...)\n\n    .Select\u003cT\u003e(t =\u003e SqlExpr.Eval(\"ABS(IntCol) \u003c 5\"));\n    // --\u003e where ABS(IntCol) \u003c 5\n\n    .Select\u003cT\u003e(t =\u003e SqlExpr.Eval(\"(exists(select * from otherTable where...))\"));\n    // --\u003e where (exists(select * from otherTable where...))\n```\nYou can also bind parameter values by using `SqlExpr.In(...)` / `SqlExpr.Eval(...)`.\n```cs\n    int intVal = 99; // (bound to @P00, @P01 or such name)\n\n    .Select\u003cT\u003e(t =\u003e t.TextCol == SqlExpr.In\u003cstring\u003e(\"select text from otherTable where a=\", intVal, \" or b=\", intVal))\n    // --\u003e  where \"TextCol\" in(select text from otherTable where a=@P00 or b=@P01)\n\n    .Select\u003cT\u003e(t =\u003e SqlExpr.Eval(\"IntCol \u003c \", intVal, \" or IntCol2 \u003e \", intVal));\n    // --\u003e where IntCol \u003c @P00 or IntCol2 \u003e @P01 \n```\n```cs\n    var idRegex = \"userIdRegexPattern\"; // (bound to @P00)\n    var pwText = \"passswordText\"; // (bound to @P01)\n\n    .Select\u003cT\u003e(t =\u003e SqlExpr.Eval(\"id~\", idRegex, \" AND pw=CRYPT(\", pwText, \", pw)\"));\n    // --\u003e where id~@P00 AND pw=CRYPT(@P01, pw) -- works only Postgres\n```\nIf you want to descrive only the value expression, use `SqlExpr.Eval\u003cT\u003e(...)`.\n```cs\n    .Select\u003cTableX\u003e(t =\u003e t.pw == SqlExpr.Eval\u003cstring\u003e(\"CRYPT('password', pw)\"));\n    // --\u003e select (columns) from TableX where \"pw\"=CRYPT('password', pw)\n\n    .Select\u003cTableX\u003e(t =\u003e t.pw == SqlExpr.Eval\u003cstring\u003e(\"CRYPT(\", pwText, \", pw)\"));\n    // --\u003e select (columns) from TableX where \"pw\"=CRYPT(@P00, pw)\n```\n- Note: `SqlExpr.Eval\u003cT\u003e(...)` can also be used as the **Value** below. \n  - `Select\u003cTable\u003e(() =\u003e new Table { column = `**Value**`, ...})`\n  - `Insert\u003cTable\u003e(() =\u003e new Table { column = `**Value**`, ...})`\n  - `Update\u003cTable\u003e(() =\u003e new Table { column = `**Value**`, ...}[, where ])`\n```cs\n    var pwText = \"passswordText\"; // (bound to @P00)\n    var salt = \"hashsalt\"; // (bound to @P01)\n    \n    .Select(() =\u003e new TableX {\n        pw = SqlExpr.Eval\u003cstring\u003e(\"CRYPT(\", pwText, \", pw)\")\n    });\n    // --\u003e select (columns) from TableX where \"pw\"=CRYPT(@P00, pw)\n\n    .Insert(() =\u003e new TableX {\n        pw = SqlExpr.Eval\u003cstring\u003e(\"CRYPT(\", pwText, \",\", salt, \")\")\n    });\n    // --\u003e insert into TableX(\"pw\") values(CRYPT(@P00,@P01))\n\n    .Update(() =\u003e new TableX {\n        pw = SqlExpr.Eval\u003cstring\u003e(\"CRYPT(\", pwText, \",\", salt, \")\")\n    }, r =\u003e { ... });\n    // --\u003e update TableX set \"pw\"=CRYPT(@P00,@P01) where ...\n```\n\n# \u003ca id=\"attributes\"\u003e\u003c/a\u003eAbout Table Attributes\n```cs\nusing System.ComponentModel.DataAnnotations;\nusing System.ComponentModel.DataAnnotations.Schema;\nusing DapperAid.DataAnnotations;\nusing DapperAid.Ddl; // (for extra feature)\n```\n## for Class\n### `[Table]` : apply if tablename != classname or you want to customize the from clause\n```cs\n    [Table(\"TABLE_NAME\")] // specify table name\n    // -\u003e select .... from TABLE_NAME\n\n    [Table(\"TABLE_NAME\", Schema = \"SCHEMA_NAME\")] // specify schema\n    // -\u003e select .... from SCHEMA_NAME.TABLE_NAME\n\n    [Table(\"TABLE1 T1 INNER JOIN TABLE2 T2 ON T1.ID=T2.ID\")] // join\n    // -\u003e select .... from TABLE1 T1 INNER JOIN TABLE2 T2 ON T1.ID=T2.ID\n    // Note: Also specify the acquisition source table in the column definition\n```\n### `[SelectSql]` : apply if you want to customize select statement\n```cs\n    [SelectSql(Beginning = \"SELECT DISTINCT\")] // customize the beginning of select sql\n    // -\u003e SELECT DISTINCT ... from ....\n\n    [SelectSql(BaseWhereClauses = \"deleted_at IS NULL\")] // append where condition of select sql\n    // -\u003e select ... from .... where deleted_at IS NULL and .....\n\n    [SelectSql(GroupByKey = true)] // generate group-by clause\n    // -\u003e select ... from ... where ... GROUP BY (colums with [Key] attributes)\n\n    [SelectSql(DefaultOtherClauses = \"ORDER BY NAME NULLS LAST\")] // append the end of select sql by default\n    // -\u003e select ... from ... where ... ORDER BY NAME NULLS LAST \n    //    (when {otherClauses} is not specified)\n```\n### (for extra feature) `[DDL]` : apply if you want to specify a table constraint of DDL\n```cs\n    [DDL(\"FOREIGN KEY (C1,C2) REFERENCES MASTERTBL(C1,C2)\")] // specify FK\n    // -\u003e create table ...(\n    //     ...,\n    //     primary key ...,\n    //     FOREIGN KEY (C1,C2) REFERENCES MASTERTBL(C1,C2)\n    //    )\n```\n## for Properties\n### `[Column]` : apply if columnname != propertyname or you want to customize the column values to retrieve\n```cs\n    [Column(\"COLUMN_NAME\")] // specify column name\n    public string ColumnName { get; set; }\n    //   -\u003e select ... COLUMN_NAME as \"ColumnName\", ... \n\n    [Column(\"T1.CODE\")] // specify table alias and column name\n    public string T1Code { get; set; }\n    //   -\u003e select ... T1.CODE as \"T1Code\", ... \n\n    [Column(\"MONTH(DateOfBirth)\")] // customize value\n    public int BirthMonth { get; set; }\n    //   -\u003e select ... MONTH(DateOfBirth) as \"BirthMonth\", ... \n\n    [Column(\"COUNT(*)\")] // tally value\n    public int TotalCount { get; set; }\n    //   -\u003e select ... COUNT(*) as \"TotalCount\", ... \n```\n### `[Key]` : apply if you want to update/delete by record-object, or use [Select(GroupByKey = true)]\n```cs\n    [Key]\n    // -\u003e update/delete .... where (columns with [Key] attributes)=@(bindvalue)\n    \n    // when [SelectSql(GroupByKey = true)] is applied to the class\n    // -\u003e select .... where ... GROUP BY (colums with [Key] attributes)\n```\n- Note: You can also specify [Key] for multiple columns (as a composite key)\n\n### \u003ca id=\"InsertValueattribute\"\u003e\u003c/a\u003e`[InsertValue]` : apply if you want to modify the insert value\n```cs\n    [InsertValue(\"CURRENT_TIMESTAMP\")] // Specify the value to set with SQL instead of bind value\n    public DateTime CreatedAt { get; set; }\n    // -\u003e insert into ...(..., \"CreatedAt\", ...) values(..., CURRENT_TIMESTAMP, ...)\n\n    [InsertValue(\"date(@DateOfBirth)\")] // Edit bind value with SQL\n    public DateTime DateOfBirth\n    // -\u003e insert into ...(..., \"BirtyDay\", ...) values(..., date(@DateOfBirth), ...)\n\n    // Do not set column (DB default value is set)\n    [InsertValue(false)] \n\n    // Default value(Identity etc.) is set, and obtain the value when InsertAndRetrieveId() is called\n    [InsertValue(false, RetrieveInsertedId = true)] \n\n    // set sequence value and obtain (works only PostgreSQL, Oracle)\n    [InsertValue(\"nextval(SEQUENCENAME)\", RetrieveInsertedId = true)]\n```\n- Note: If you call Insert() with the target column explicitly specified,  \n  The bind value is set instead of the value by this attribute.\n\n### `[UpdateValue]` : apply if you want to modify the value on update\n```cs\n    [UpdateValue(\"CURRENT_TIMESTAMP\")] : // Specify the value to set with SQL instead of bind value\n    public DateTime UpdatedAt { get; set; }\n    // -\u003e update ... set ..., \"UpdatedAt\"=CURRENT_TIMESTAMP, ....\n\n    [UpdateValue(\"COALESCE(@DCnt, 0)\")] // Edit bind value with SQL\n    public Int? DCnt { get; set; }\n    // -\u003e update ... set ..., \"DCnt\"=COALESCE(@DCnt, 0), ...\n\n    // Do not set column (not be updated)\n    [UpdateValue(false)] \n```\n- Note: If you call Update() with the target column explicitly specified,  \n  The bind value is set instead of the value by this attribute.\n\n\n### `[NotMapped]` : Denotes that a property should be excluded from database mapping\n```cs\n    [NotMapped] // Do not select, insert, update \n    public Object NotMappedProperty { get; set; }\n```\n### (for extra feature) \u003ca id=\"ddlattribute\"\u003e\u003c/a\u003e`[DDL]` : apply if you want to specify database column types, constraints, default values, etc.\n```cs\n    [DDL(\"NUMERIC(5) DEFAULT 0 NOT NULL\")]\n    public int Value { get; set; }\n    // -\u003e create table ...(\n    //      :\n    //     Value NUMERIC(5) DEFAULT 0 NOT NULL,\n    //      : \n```\n# Misc.\n## when the error \"The value of type (TypeName) cannot be represented as a sql literal.\" occured\n- Call `QueryBuilder.AddSqlLiteralConverter()` to specify the function that converts the data value to an SQL representation.\n```cs\nusing NetTopologySuite.Geometries;\n\n    var queryBuilderInstance = new QueryBuilder.Postgres();\n    // Here is an example of geometry type SQL.\n    queryBuilderInstance.AddSqlLiteralConverter\u003cGeometry\u003e(geom =\u003e\n    {\n        var binaryHex = string.Concat(geom.AsBinary().Select(b =\u003e $\"{b:X2}\"));\n        return $\"'{binaryHex}'::geometry\";\n    });\n```\n## When you want to execute a query during transaction.\n- use extension methods in `IDbTransaction`. \nIt provides the same method as the `IDbConnection` extension method.\n## When you want to execute a asynchronus query.\n- use ～～Async methods.\n## When not using as an extension method.\n- use `QueryRunner` class.\nIt Provides almost the same content as an extension method as an instance method.\n## When you want to use only the SQL generation function.\n- Use the [`QueryBuilder`](#querybuilders) class appropriate for your DBMS.\n\n# License\n[MIT License](http://opensource.org/licenses/MIT).\n\n# About Author\nhnx8(H.Takahashi) is a software developer in Japan.  \n(I wrote English sentences relying on Google translation. Please let me know if you find a strange expression)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhnx8%2Fdapperaid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhnx8%2Fdapperaid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhnx8%2Fdapperaid/lists"}