{"id":13629626,"url":"https://github.com/jitbit/MapDataReader","last_synced_at":"2025-04-17T09:35:16.845Z","repository":{"id":61203145,"uuid":"549248191","full_name":"jitbit/MapDataReader","owner":"jitbit","description":"Super fast mapping DataReader to strongly typed object, Using AOT source generator.","archived":false,"fork":false,"pushed_at":"2024-05-01T10:06:18.000Z","size":103,"stargazers_count":64,"open_issues_count":8,"forks_count":11,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-31T14:43:30.991Z","etag":null,"topics":["csharp-sourcegenerator","datareader","orm","reflection"],"latest_commit_sha":null,"homepage":"","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/jitbit.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":"2022-10-10T22:50:52.000Z","updated_at":"2024-10-13T13:56:53.000Z","dependencies_parsed_at":"2024-05-01T11:57:34.731Z","dependency_job_id":"9d291e07-6c7f-493a-a995-4a5d62f231db","html_url":"https://github.com/jitbit/MapDataReader","commit_stats":{"total_commits":69,"total_committers":1,"mean_commits":69.0,"dds":0.0,"last_synced_commit":"24973c741865a6cdd02ba698bc6205e2f9f52e74"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jitbit%2FMapDataReader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jitbit%2FMapDataReader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jitbit%2FMapDataReader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jitbit%2FMapDataReader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jitbit","download_url":"https://codeload.github.com/jitbit/MapDataReader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223751313,"owners_count":17196612,"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","datareader","orm","reflection"],"created_at":"2024-08-01T22:01:15.145Z","updated_at":"2024-11-08T20:31:28.459Z","avatar_url":"https://github.com/jitbit.png","language":"C#","readme":"# MapDataReader\nSuper fast mapping DataReader to a strongly typed object. High performance, lighweight (12Kb dll), uses AOT source generation and no reflection, mapping code is generated at compile time.\n\n[![.NET](https://github.com/jitbit/MapDataReader/actions/workflows/dotnet.yml/badge.svg)](https://github.com/jitbit/MapDataReader/actions/workflows/dotnet.yml)\n[![Nuget](https://img.shields.io/nuget/v/MapDataReader)](https://www.nuget.org/packages/MapDataReader/)\n![Net stanrdard 2.0](https://img.shields.io/badge/netstandard-2.0-brightgreen)\n\n## Benchmarks\n\n20X faster than using reflection, even with caching. Benchmark for a tiny class with 5 string properties:\n\n| Method         |      Mean |     Error |   StdDev |   Gen0 | Allocated |\n|--------------- |----------:|----------:|---------:|-------:|----------:|\n|  Reflection    | 951.16 ns | 15.107 ns | 0.828 ns | 0.1459 |     920 B |\n|  MapDataReader |  44.15 ns |  2.840 ns | 0.156 ns | 0.0089 |      56 B |\n\n## Install via [Nuget](https://www.nuget.org/packages/MapDataReader/)\n\n```\nInstall-Package MapDataReader\n```\n\n## Usage with `IDataReader`\n\n```csharp\nusing MapDataReader;\n\n[GenerateDataReaderMapper] // \u003c-- mark your class with this attribute\npublic class MyClass\n{\n\tpublic int ID { get; set; }\n\tpublic string Name { get; set; }\n\tpublic int Size { get; set; }\n\tpublic bool Enabled { get; set; }\n}\n\nvar dataReader = new SqlCommand(\"SELECT * FROM MyTable\", connection).ExecuteReader();\n\nList\u003cMyClass\u003e results = dataReader.ToMyClass(); // \"ToMyClass\" method is generated at compile time\n```\n\nSome notes for the above\n\n* The `ToMyClass()` method above - is an `IDataReader` extension method generated at compile time. You can even \"go to definition\" in Visual Studio and examine its code.\n* The naming convention is `ToCLASSNAME()` we can't use generics here, since `\u003cT\u003e` is not part of method signatures in C# (considered in later versions of C#). If you find a prettier way - please contribute!\n* Maps properies with public setters only.\n* The datareader is being closed after mapping, so don't reuse it.\n* Supports `enum` properties based on `int` and other implicit casting (sometimes a DataReader may decide to return `byte` for small integer database value, and it maps to `int` perfectly via some unboxing magic)\n* Properly maps `DBNull` to `null`.\n* Complex-type properties may not work.\n\n## Bonus API: `SetPropertyByName`\n\nThis package also adds a super fast `SetPropertyByName` extension method generated at compile time for your class.\n\nUsage:\n\n```csharp\nvar x = new MyClass();\nx.SetPropertyByName(\"Size\", 42); //20X faster than using reflection\n```\n\n|                  Method |      Mean |     Error |    StdDev | Allocated |\n|------------------------ |----------:|----------:|----------:|----------:|\n|       SetPropReflection | 98.294 ns | 5.7443 ns | 0.3149 ns |         - |\n| SetPropReflectionCached | 71.137 ns | 1.9736 ns | 0.1082 ns |         - |\n|    SetPropMapDataReader |  4.711 ns | 0.4640 ns | 0.0254 ns |         - |\n\n---\n\n## Tip: Using it with Dapper\n\nIf you're already using the awesome [Dapper ORM](https://github.com/DapperLib/Dapper) by Marc Gravel, Sam Saffron and Nick Craver, this is how you can use our library to speed up DataReader-to-object mapping in Dapper:\n\n```csharp\n// override Dapper extension method to use fast MapDataReader instead of Dapper's built-in reflection\npublic static List\u003cT\u003e Query\u003cT\u003e(this SqlConnection cn, string sql, object parameters = null)\n{\n\tif (typeof(T) == typeof(MyClass)) //our own class that we marked with attribute?\n\t\treturn cn.ExecuteReader(sql, parameters).ToMyClass() as List\u003cT\u003e; //use MapDataReader\n\n\tif (typeof(T) == typeof(AnotherClass)) //another class we have enabled?\n\t\treturn cn.ExecuteReader(sql, parameters).ToAnotherClass() as List\u003cT\u003e; //again\n\n\t//fallback to Dapper by default\n\treturn SqlMapper.Query\u003cT\u003e(cn, sql, parameters).AsList();\n}\n```\nWhy the C# compiler will choose your method over Dapper's?\n\nWhen the C# compiler sees two extension methods with the same signature, it uses the one that's \"closer\" to your code. \"Closiness\" - is determined by multiple factors - same namespace, same assembly, derived class over base class, implementation over interface etc. Adding an override like this will silently switch your existing code from using Dapper/reflection to using our source generator (b/c it uses a more specific connection type and lives in your project's namescape), while still keeping the awesomeness of Dapper and you barely have to rewrite any of your code.\n\n---\n\n## P.S. But what's the point?\n\nWhile reflection-based ORMs like Dapper are very fast after all the reflaction objects have been cached, they still do a lot of reflection-based heavy-lifting when you query the database *for the first time*. Which slows down application startup *significantly*. Which, in turn, can become a problem if you deploy the application multiple times a day.\n\nOr - if you run your ASP.NET Core app on IIS - this causes 503 errors during IIS recycles, see https://github.com/dotnet/aspnetcore/issues/41340 and faster app startup helps a lot.\n\nAlso, reflection-caching causes memory pressure becasue of all the concurrent dictionaries used for caching.\n\nAnd even with all the caching, a simple straightforward code like `obj.x = y` will always be faster then looking up a cached delegate in a thousands-long dictionary by a string key and invoking it via reflection.\n\nEven if you don't care about the startup performance of your app, `MapDataReader` is still 5-7% faster than `Dapper` (note - we're not even using Dapper's command-cache store here, just the datareader parser, actual real world Dapper scenario will be even slower)\n\n|          Method |          Mean |         Error |       StdDev |   Gen0 |   Gen1 | Allocated |\n|---------------- |--------------:|--------------:|-------------:|-------:|-------:|----------:|\n| DapperWithCache |     142.09 us |  8,013.663 ns |   439.256 ns | 9.0332 | 1.2207 |   57472 B |\n|   MapDataReader |     133.22 us | 28,679.198 ns | 1,572.004 ns | 9.0332 | 1.2207 |   57624 B |\n\n","funding_links":[],"categories":["Source Generators","Do not want to test 112 ( old ISourceGenerator )"],"sub_categories":["Database / ORM","1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjitbit%2FMapDataReader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjitbit%2FMapDataReader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjitbit%2FMapDataReader/lists"}