{"id":18023685,"url":"https://github.com/eastcitysoftware/leger","last_synced_at":"2025-03-27T00:30:44.855Z","repository":{"id":257815098,"uuid":"867895258","full_name":"eastcitysoftware/leger","owner":"eastcitysoftware","description":"C# data access with no reflection, no magic, no surprises.","archived":false,"fork":false,"pushed_at":"2025-03-22T11:13:00.000Z","size":82,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-23T09:11:35.036Z","etag":null,"topics":["adonet","csharp","database","db","sql"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eastcitysoftware.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":"2024-10-05T00:21:39.000Z","updated_at":"2025-03-22T11:13:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"f4bcc055-d1c0-4e56-b568-58d408858bf9","html_url":"https://github.com/eastcitysoftware/leger","commit_stats":null,"previous_names":["pimbrouwers/leger","eastcitysoftware/leger"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eastcitysoftware%2Fleger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eastcitysoftware%2Fleger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eastcitysoftware%2Fleger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eastcitysoftware%2Fleger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eastcitysoftware","download_url":"https://codeload.github.com/eastcitysoftware/leger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245760557,"owners_count":20667886,"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":["adonet","csharp","database","db","sql"],"created_at":"2024-10-30T07:10:21.572Z","updated_at":"2025-03-27T00:30:44.846Z","avatar_url":"https://github.com/eastcitysoftware.png","language":"C#","readme":"# Léger\n\n[![NuGet Version](https://img.shields.io/nuget/v/Leger.svg)](https://www.nuget.org/packages/Leger)\n[![build](https://github.com/eastcitysoftware/leger/actions/workflows/build.yml/badge.svg)](https://github.com/eastcitysoftware/leger/actions/workflows/build.yml)\n\nLéger is a library that aims to make working with [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ado-net-overview) from C# *a lot* simpler. Functionality is delivered through an API that bears a resemblance to [Dapper](https://github.com/DapperLib/Dapper) with a focus on manual mapping and a few extra goodies built-in.\n\n## Key Features\n\n- No reflection, no magic, no surprises.\n- Simple and **uniform** execution model, delivered as `IDbConnection`, `IDbCommand`, `IDbTransaction` and [`IDbConnectionFactory`](#idbconnectionfactory) extension methods.\n- Safe value reading via `IDataRecord` and `IDataReader` [extensions](#idatareader-extension-methods).\n- [Enhanced](#exceptions) exception output.\n- [Async](#an-example-using-sqlite) versions of all data access methods.\n\n## Design Goals\n\n- Augment the base ADO.NET functionality as little as possible and adhering to internal naming conventions.\n- Encourage manual mappings by providing a succinct and safe methodology to obtain values from tabular data.\n- Provide an easy to reason about execution model.\n- Support asynchronous database workflows.\n\n## Getting Started\n\nInstall the Leger NuGet package:\n\n```\nPM\u003e  Install-Package Leger\n```\n\nOr using the dotnet CLI\n\n```\ndotnet add package Leger\n```\n\n### Quick Start\n\n```csharp\nusing System;\nusing System.Data.Sqlite;\nusing Leger;\n\nusing var connection = new SqliteConnection(\"{your connection string}\");\n\nvar books = connection.Query(\"\"\"\n    SELECT name FROM book WHERE author_id = @author_id\"\n    \"\"\",\n    new(\"author_id\", 1),\n    rd =\u003e rd.ReadString(\"name\"));\n\nforeach(var book in books)\n{\n    Console.WriteLine(book);\n}\n```\n\n## An Example using SQLite\n\nFor this example, assume we have an `IDbConnection` named `connection`:\n\n```csharp\nusing var connection = new SqliteConnection(\"Data Source=author.db\");\n```\n\nWith the following database schema:\n\n```sql\nCREATE TABLE author (\n    author_id INTEGER PRIMARY KEY,\n    full_name TEXT);\n```\n\nAnd domain model:\n\n```csharp\npublic record Author(\n    int AuthorId,\n    string FullName);\n\npublic static class AuthorReader\n{\n    public static Author Map(IDataRecord rd) =\u003e\n        new(AuthorId = rd.ReadInt32(\"author_id\"),\n            FullName = rd.ReadString(\"full_name\"));\n}\n```\n\n### Query for multiple strongly-type results\n\n```csharp\nvar authors = connection.Query(\"\"\"\n    SELECT author_id, full_name FROM author\n    \"\"\",\n    AuthorReader.Map);\n\n// async\nvar authors = await connection.QueryAsync(\"\"\"\n    SELECT author_id, full_name FROM author\n    \"\"\",\n    AuthorReader.Map);\n```\n\n### Query for a single strongly-type result\n\n```csharp\n// `QuerySingle` is optimized to dispose the `IDataReader` after safely reading the first `IDataRecord`.\nvar author = connection.QuerySingle(\"\"\"\n    SELECT author_id, full_name FROM author WHERE author_id = @author_id\n    \"\"\",\n    new(\"author_id\", authorId),\n    AuthorReader.Map);\n\n// async\nvar author = await connection.QuerySingleAsync(\"\"\"\n    SELECT author_id, full_name FROM author WHERE author_id = @author_id\n    \"\"\",\n    new(\"author_id\", authorId),\n    AuthorReader.Map);\n```\n\n### Execute a statement\n\n```csharp\nconnection.Execute(\"\"\"\n    INSERT INTO author (full_name) VALUES (@full_name)\n    \"\"\",\n    new(\"full_name\", \"John Doe\"));\n\n// async\nawait connection.ExecuteAsync(\"\"\"\n    INSERT INTO author (full_name) VALUES (@full_name)\n    \"\"\",\n    new(\"full_name\", \"John Doe\"));\n```\n\n### Execute a statement multiple times\n\n```csharp\npublic record NewAuthor(\n    string FullName);\n\nIEnumerable\u003cNewAuthor\u003e newAuthors = [new(\"John Doe\"), new(\"Jane Doe\")];\n\nconnection.ExecuteMany(\"\"\"\n    INSERT INTO author (full_name) VALUES (@full_name)\n    \"\"\",\n    newAuthors.Select(a =\u003e new DbParams(\"full_name\", a.FullName)));\n\n// async\nawait connection.ExecuteManyAsync(\"\"\"\n    INSERT INTO author (full_name) VALUES (@full_name)\n    \"\"\",\n    newAuthors.Select(a =\u003e new DbParams(\"full_name\", a.FullName)));\n```\n\n### Execute a statement transactionally\n\n```csharp\nusing var transaction = connection.CreateTransaction();\n\ntransaction.Execute(\"\"\"\n    UPDATE author SET full_name = @full_name where author_id = @author_id\n    \"\"\",\n    new(){\n        { \"author_id\", author.AuthorId },\n        { \"full_name\", author.FullName }\n    });\n\ntransaction.Commit();\n```\n\n### Execute a scalar command (single value)\n\n```csharp\nvar count = connection.Scalar(\"\"\"\n    SELECT COUNT(*) AS author_count FROM author\n    \"\"\",\n    new(\"full_name\", \"John Doe\"));\n\n// async\nvar count = await connection.ScalarAsync(\"\"\"\n    SELECT COUNT(*) AS author_count FROM author\n    \"\"\",\n    new(\"full_name\", \"John Doe\"));\n```\n\n## `IDbConnectionFactory`\n\nThe `IDbConnectionFactory` interface is provided to allow for the creation of `IDbConnection` instances. This is useful when you want to abstract the creation of connections and even moreso in multi-data source scenarios.\n\nImplementing the interface is straightforward, an example using `System.Data.Sqlite` is shown below:\n\n```csharp\npublic class SqliteConnectionFactory(connectionString)\n    : IDbConnectionFactory\n{\n    public IDbConnection CreateConnection() =\u003e\n        new SqliteConnection(connectionString);\n}\n```\n\nThe expectation is that the connection is left closed, and the API methods will open and close the connection as needed.\n\n## `IDataRecord` Extension Methods\n\nTo make obtaining values from `IDataRecord` more straight-forward, 2 sets of extension methods are available for:\n\n- Get value, automatically defaulted\n- Get value as Nullable\u003c'a\u003e\n\nAssume we have an active `IDataRecord` called `rd and are currently reading a row, the following extension methods are available to simplify reading values:\n\n```csharp\npublic static string ReadString(this IDataRecord rd, string field);\npublic static char ReadChar(this IDataRecord rd, string field);\npublic static bool ReadBoolean(this IDataRecord rd, string field);\npublic static bool ReadBool(this IDataRecord rd, string field);\npublic static byte ReadByte(this IDataRecord rd, string field);\npublic static short ReadInt16(this IDataRecord rd, string field);\npublic static short ReadShort(this IDataRecord rd, string field);\npublic static int ReadInt32(this IDataRecord rd, string field);\npublic static int ReadInt(this IDataRecord rd, string field);\npublic static int ReadInt(this IDataRecord rd, string field);\npublic static long ReadInt64(this IDataRecord rd, string field);\npublic static long ReadLong(this IDataRecord rd, string field);\npublic static decimal ReadDecimal(this IDataRecord rd, string field);\npublic static double ReadDouble(this IDataRecord rd, string field);\npublic static float ReadFloat(this IDataRecord rd, string field);\npublic static Guid ReadGuid(this IDataRecord rd, string field);\npublic static DateTime ReadDateTime(this IDataRecord rd, string field);\npublic static byte[] ReadBytes(this IDataRecord rd, string field);\n\npublic static bool? ReadNullableBoolean(this IDataRecord rd, string field);\npublic static bool? ReadNullableBool(this IDataRecord rd, string field);\npublic static byte? ReadNullableByte(this IDataRecord rd, string field);\npublic static short? ReadNullableInt16(this IDataRecord rd, string field);\npublic static short? ReadNullableShort(this IDataRecord rd, string field);\npublic static int? ReadNullableInt32(this IDataRecord rd, string field);\npublic static int? ReadNullableInt(this IDataRecord rd, string field);\npublic static int? ReadNullableInt(this IDataRecord rd, string field);\npublic static long? ReadNullableInt64(this IDataRecord rd, string field);\npublic static long? ReadNullableLong(this IDataRecord rd, string field);\npublic static decimal? ReadNullableDecimal(this IDataRecord rd, string field);\npublic static double? ReadNullableDouble(this IDataRecord rd, string field);\npublic static float? ReadNullableFloat(this IDataRecord rd, string field);\npublic static Guid? ReadNullableGuid(this IDataRecord rd, string field);\npublic static DateTime? ReadNullableDateTime(this IDataRecord rd, string field);\n```\n\n## Errors and Exceptions\n\nLeger provides enhanced exception output to help you quickly identify and resolve issues. The following exceptions are thrown:\n\n- `DatabaseExecutionException`\n    - Thrown when an error occurs during the execution of a command.\n    - Includes the SQL statement that was executed,\n    - Or, the field name and value that caused the error.\n- `DatabaseConnectionException` - thrown when a connection cannot be established.\n- `DatabaseTransactionException` - thrown when an error occurs during a transaction.\n\n## Contribute\n\nThank you for considering contributing to Leger, and to those who have already contributed! We appreciate (and actively resolve) PRs of all shapes and sizes.\n\nWe kindly ask that before submitting a pull request, you first submit an [issue](https://github.com/eastcitysoftware/leger/issues) or open a [discussion](https://github.com/eastcitysoftware/leger/discussions).\n\nIf functionality is added to the API, or changed, please kindly update the relevant [document](https://github.com/eastcitysoftware/leger/tree/master/docs). Unit tests must also be added and/or updated before a pull request can be successfully merged.\n\nOnly pull requests which pass all build checks and comply with the general coding guidelines can be approved.\n\nIf you have any further questions, submit an [issue](https://github.com/eastcitysoftware/leger/issues) or open a [discussion](https://github.com/eastcitysoftware/leger/discussions).\n\n## License\n\nLicensed under [Apache License 2.0](https://github.com/eastcitysoftware/leger/blob/master/LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feastcitysoftware%2Fleger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feastcitysoftware%2Fleger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feastcitysoftware%2Fleger/lists"}