{"id":13629383,"url":"https://github.com/Ludovicdln/Breezy","last_synced_at":"2025-04-17T09:33:22.838Z","repository":{"id":174547776,"uuid":"652379249","full_name":"Ludovicdln/Breezy","owner":"Ludovicdln","description":"Micro ORM with source generator","archived":false,"fork":false,"pushed_at":"2023-09-07T14:21:03.000Z","size":59,"stargazers_count":24,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-08T20:45:28.313Z","etag":null,"topics":["csharp-sourcegenerator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ludovicdln.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-06-12T00:21:16.000Z","updated_at":"2024-10-13T17:49:43.000Z","dependencies_parsed_at":null,"dependency_job_id":"f8f29a1f-5d58-4d21-b81a-80680339c6f1","html_url":"https://github.com/Ludovicdln/Breezy","commit_stats":null,"previous_names":["ludovicdln/breezy"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ludovicdln%2FBreezy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ludovicdln%2FBreezy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ludovicdln%2FBreezy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ludovicdln%2FBreezy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ludovicdln","download_url":"https://codeload.github.com/Ludovicdln/Breezy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249331615,"owners_count":21252618,"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":["csharp-sourcegenerator"],"created_at":"2024-08-01T22:01:09.031Z","updated_at":"2025-04-17T09:33:22.373Z","avatar_url":"https://github.com/Ludovicdln.png","language":"C#","funding_links":[],"categories":["Content","Source Generators"],"sub_categories":["39. [Breezy](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Breezy) , in the [Database](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#database) category","Database / ORM"],"readme":"\u003cdiv style=\"text-align:center\"\u003e\u003cimg src=\"https://zupimages.net/up/23/23/na2b.png\" width=\"900\" height=\"300\"\u003e\u003c/div\u003e\n\n[![NuGet Badge](https://buildstats.info/nuget/Breezy.SourceGenerator/)](https://www.nuget.org/packages/Breezy.SourceGenerator//1.0.2)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n\n\nBreezy is a lightweight Object-Relational Mapping \u003cb\u003e(ORM)\u003c/b\u003e library for mapping objects using [`Source Generator`](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) in C#. \u003cbr /\u003eIt provides seamless asynchronous operations for enhanced performance.\n\n## Installation\n\n##### Nugget Package : https://www.nuget.org/packages/Breezy.SourceGenerator/\n\nTo install Breezy, simply add the package reference to your project using NuGet Package Manager or by adding the following line to your .csproj file:\n\n```xml\n\u003cItemGroup\u003e\n\u003cPackageReference Include=\"Breezy.SourceGenerator\" Version=\"1.0.2\" /\u003e\n\u003c/ItemGroup\u003e\n````\n\n##  Getting Started\n\nBreezy simplifies the mapping of objects and performing database operations. Here's a simple example of querying houses using Breezy's asynchronous operations :\n\n````csharp\npublic static async Task\u003cIEnumerable\u003cHouse\u003e\u003e QueryAsync\u003cT\u003e(this DbConnection connection, string sql, object param, ICacheableQuery\u003cHouse\u003e cacheableQuery, CancellationToken cancellationToken = default) where T : House\n````\n\n```csharp\nusing Breezy;\n\nvar houses = await connection.QueryAsync\u003cHouse\u003e(\"SELECT * FROM house\");\n```\n\nIn the above example, the QueryAsync method executes the provided SQL query and maps the results to a list of House objects asynchronously.\n\n##  Mapping Objects with Relations (N to N || 1 to N)\n\nBreezy supports mapping objects with relationships. Here's an example of querying posts with tags using Breezy's asynchronous operations :\n\n````csharp\nusing Breezy;\n\nvar posts = await connection.QueryAsync\u003cPost\u003e(\n    @\"SELECT * FROM test.post p INNER JOIN posts_tags pt ON p.id = pt.post_id INNER JOIN tag t ON t.id = pt.tag_id\");\n````\n\nThe QueryAsync method executes the provided SQL query and maps the results to a list of Post objects. The Post class is defined as follows :\n\n````csharp\n[Table(\"post\")]\n[SplitOn(3, 4)]\npublic class Post\n{\n    public int Id { get; set; }\n    public string Title { get; set; }\n    public string Body { get; set; }\n    public List\u003cTag\u003e Tags { get; set; } = new();\n}\n\n[Table(\"tag\")]\npublic class Tag\n{\n    public int Id { get; set; }\n    public string Name { get; set; }\n    public List\u003cPost\u003e Posts { get; set; } = new();\n}\n````\n\nIn the Post class, the \u003cb\u003eTable attribute\u003c/b\u003e specifies the table name, and the \u003cb\u003eSplitOn attribute\u003c/b\u003e indicates the column indices to split when mapping the object from the database.\n\n\u003ci\u003eCircular reference doesn't throw exception ! \u003c/i\u003e\n\n#### Vs Dapper\n\n````csharp\nvar sql = @\"SELECT p.id, p.title, p.body, t.id, t.name\n                FROM post p \n                INNER JOIN posts_tags pt ON pt.post_id = p.id\n                INNER JOIN tag t ON t.id = pt.tag_id\";\n\t\t\t\t\n    var posts = await connection.QueryAsync\u003cPost, Tag, Post\u003e(sql, (post, tag) =\u003e {      \n        post.Tags.Add(tag);\n        return post;\n    }, splitOn: \"id\");\n\t\n    var result = posts.GroupBy(p =\u003e p.PostId).Select(g =\u003e\n    {\n        var groupedPost = g.First();\n        groupedPost.Tags = g.Select(p =\u003e p.Tags.Single()).ToList();\n        return groupedPost;\n    });\n    \n   // Dapper is less user friendly for theses using case\n````\n\n##  Mapping Objects with Reference Type(s)\n\n````csharp\npublic class UserReference\n{\n    public int Id { get; set; }\n    public Position Position { get; set; }\n}\n\npublic sealed class Position\n{\n    public string ZipCode { get; set; }\n    public string City { get; set; }\n    public string Address { get; set; }\n}\n````\n\n````csharp\nvar users = await connection.QueryAsync\u003cUserReference\u003e(\"SELECT u.id, u.zip_code, u.city, u.address FROM user_ref u\");\n````\n\nThe QueryAsync method executes the SQL query and automatically maps the result columns to the corresponding properties of the UserReference entity, including the reference type Position.\n\n## Querying with Anonymous Types\n\nBreezy allows you to query using anonymous types as parameters. Here's an example :\n\n````csharp\nvar houses = await connection.QueryAsync\u003cHouse\u003e(\"SELECT * FROM house h WHERE h.id = @Id\", new {Id = 1});\n````\n\nThe anonymous type is used to pass the \u003cb\u003eId\u003c/b\u003e parameter.\n\n\u003e*IMPORTANT :*\n\u003e Make sure that the column index in the SQL query match the property index in any class for the mapping to work correctly.\n\u003e \u003cbr /\u003e\u003cb\u003eYou need to add any relations at the end of you main object !\u003c/b\u003e\n\n## Caching for Performance Optimization\n\nBreezy supports implementing caching mechanisms, such as in-memory or distributed caching, to reduce the memory footprint and improve query execution time. You can implement your own caching strategy based on your specific requirements.\n\n````csharp\npublic interface ICacheableQuery\u003cT\u003e where T : class\n{\n\tpublic Task\u003cIEnumerable\u003cT\u003e\u003e GetCacheableResultsAsync(IdentityQuery identityQuery);\n\t\n\tpublic Task SetCacheableResultsAsync(IdentityQuery identityQuery, IEnumerable\u003cT\u003e results);\n}\t\n````\n\n````csharp\n// Check if the query result is already cached\n\nvar identityQuery = new IdentityQuery(sql);\n\nvar cacheableResults = await cacheableQuery.GetCacheableResultsAsync(identityQuery);\n\nif (cacheableResults.Any())\n    return cacheableResults;\n    \n// Execute the query    \n\nvar results = new List\u003cT\u003e();\n\nwhile (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) \n{ \n    // processing...\n}\n\n// Cache the query result for X ms/s\n\nawait cacheableQuery.SetCacheableResultsAsync(identityQuery, results);\n````\n\n\u003cdetails\u003e\n\u003csummary\u003eExample of implementation (Memory Cache)\u003c/summary\u003e\n\n````csharp\npublic sealed class MemoryCacheableQuery\u003cT\u003e : ICacheableQuery\u003cT\u003e where T : class\n{\n    private readonly Dictionary\u003cIdentityQuery, Tuple\u003cDateTime, IEnumerable\u003cT\u003e\u003e\u003e _cacheableData = new();\n    \n    public Task\u003cIEnumerable\u003cT\u003e\u003e GetCacheableResultsAsync(IdentityQuery identityQuery)\n    {\n        if (_cacheableData.TryGetValue(identityQuery, out var results))\n        {\n            var (addDate, collection) = results;\n\n            if ((DateTime.Now - addDate) \u003c TimeSpan.FromSeconds(10))\n                return Task.FromResult\u003cIEnumerable\u003cT\u003e\u003e(collection);\n\n            _cacheableData.Remove(identityQuery);\n        }\n\n        return Task.FromResult\u003cIEnumerable\u003cT\u003e\u003e(Array.Empty\u003cT\u003e());\n    }\n\n    public Task SetCacheableResultsAsync(IdentityQuery identityQuery, IEnumerable\u003cT\u003e results)\n    {\n        _cacheableData.Add(identityQuery, new Tuple\u003cDateTime, IEnumerable\u003cT\u003e\u003e(DateTime.Now, results));\n\n        return Task.CompletedTask;\n    }\n}\n````\n\u003c/details\u003e\n\n## Execute a Command that return result\n\nBreezy provides the ExecuteAsync method for executing SQL statements that can return results. Here's an example of using ExecuteAsync to insert data into a table and retrieve the last inserted ID:\n\n````csharp\npublic static async Task\u003cint\u003e ExecuteAsync(this DbConnection connection, string sql, object param, CancellationToken cancellationToken = default)\n````\n\n````csharp\nvar lastId = await connection.ExecuteAsync(\"INSERT INTO myTable (x, y) VALUES (x, y); SELECT LAST_INSERT_ID();\");\n````\n\n## Execute a Command that return results with Transaction\n\n````csharp\npublic static async Task\u003cint[]\u003e ExecuteAsync(this DbConnection connection, string[] sql, DbTransaction transaction, CancellationToken cancellationToken = default)\n````\n\n````csharp\nvar dbTransaction = await _mySqlConnection.BeginTransactionAsync();\n\nvar results = await connection.ExecuteAsync(new [] { \"INSERT INTO myTable (x, y) VALUES (x, y); SELECT LAST_INSERT_ID();\" }, { /* ... */ }, dbTransaction);\n````\n\n## Performance ~ 10k rows\n\n\n````\nBenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19044.2965/21H2/November2021Update)\nAMD Ryzen 5 3500X, 1 CPU, 6 logical and 6 physical cores\n.NET SDK=8.0.100-preview.2.23157.25\n[Host]     : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2\nDefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2\n````\n\n| ORM    | Method                        | Return           |            Mean |        StdDev |      Gen0 |     Gen1 |     Gen2 |  Allocated |\n|--------|-------------------------------|------------------|----------------:|--------------:|----------:|---------:|---------:|-----------:|\n| Breezy  | QueryAsync\u0026lt;T\u0026gt;           | No relation      |        491.1 ns |       4.08 ns |    0.0801 |        - |        - |      672 B |\n| Dapper | QueryAsync\u0026lt;T\u0026gt;           | No relation      | 14,005,807.3 ns |  85,785.13 ns |  437.5000 | 265.6250 | 125.0000 |  3899691 B |\n| Breezy  | QueryFirstOrDefault\u0026lt;T\u0026gt;  | No relation      |        589.8 ns |       7.28 ns |    0.0935 |        - |        - |      784 B |\n| Dapper | QueryFirstOrDefault\u0026lt;T\u0026gt;  | No relation      |    540,714.1 ns |  44,717.07 ns |    0.9766 |        - |        - |    13081 B |\n| Breezy  | QueryAsync\u0026lt;T\u0026gt;           | 1 To N relations |        588.5 ns |       9.26 ns |    0.0801 |        - |        - |      672 B |\n| Dapper | QueryAsync\u0026lt;T\u0026gt;           | 1 To N relations | 98,695,865.6 ns | 740,908.87 ns | 2000.0000 | 833.3333 | 500.0000 | 17760052 B |\n| Breezy  | QueryFirstOrDefault\u0026lt;T\u0026gt;  | 1 To N relations |        690.7 ns |      13.41 ns |    0.0935 |        - |        - |      784 B |\n| Dapper | QueryFirstOrDefault\u0026lt;T\u0026gt;  | 1 To N relations | 14,866,187.7 ns | 385,888.24 ns |         - |        - |        - |    30835 B |\n\n\n\n## Why Breezy ?\n\nI wanted to offer similary fonctionalities faster than [`Dapper`](https://github.com/DapperLib/Dapper) with source generator\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLudovicdln%2FBreezy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLudovicdln%2FBreezy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLudovicdln%2FBreezy/lists"}