{"id":37061252,"url":"https://github.com/busterwood/mapper","last_synced_at":"2026-01-14T06:56:01.394Z","repository":{"id":91117189,"uuid":"53650984","full_name":"busterwood/Mapper","owner":"busterwood","description":".NET composable mapping library for objects and data (System.Data)","archived":false,"fork":false,"pushed_at":"2018-02-06T11:43:53.000Z","size":316,"stargazers_count":3,"open_issues_count":5,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-01T09:16:14.854Z","etag":null,"topics":["ado","dotnet","mapper","orm","sql"],"latest_commit_sha":null,"homepage":null,"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/busterwood.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2016-03-11T08:18:31.000Z","updated_at":"2017-11-08T22:50:22.000Z","dependencies_parsed_at":"2023-06-03T03:30:10.713Z","dependency_job_id":null,"html_url":"https://github.com/busterwood/Mapper","commit_stats":null,"previous_names":[],"tags_count":54,"template":false,"template_full_name":null,"purl":"pkg:github/busterwood/Mapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busterwood%2FMapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busterwood%2FMapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busterwood%2FMapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busterwood%2FMapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/busterwood","download_url":"https://codeload.github.com/busterwood/Mapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busterwood%2FMapper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28412470,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["ado","dotnet","mapper","orm","sql"],"created_at":"2026-01-14T06:56:00.845Z","updated_at":"2026-01-14T06:56:01.385Z","avatar_url":"https://github.com/busterwood.png","language":"C#","readme":"# BusterWood.Mapper\n[![Build status](https://ci.appveyor.com/api/projects/status/vdlxdx8t62mfrrol/branch/master?svg=true)](https://ci.appveyor.com/project/busterwood/mapper/branch/master) [![Nuget](https://img.shields.io/nuget/v/BusterWood.Mapper.svg)](https://www.nuget.org/packages/BusterWood.Mapper)\n\n.NET composable mapping library for objects and data (System.Data).\n\nSort of a replacement for Dapper (150K) and Automapper (350K) but `Mapper` is *much smaller* at around 100K.\n\nPerformance is \"good\" as `Mapper` uses the DLR to create and JIT compile methods to do the mapping, and these methods are cached.\n\n## Copying an object\n\n`Mapper` contains an extension method for all objects called `Copy\u003cT\u003e()` which returns a *shallow* copy of the original object. The type being cloned *must* have a parameterless contructor, then all public properties and fields are copied.\n\n`Mapper` can also clone sequences of object via the `CopyAll\u003cT\u003e()` extension which takes a `IEnumerable\u003cT\u003e` and returns an `IEnumerable\u003cT\u003e`.\n\nTo allow for customized copying the following overloads of `CopyAll\u003cT\u003e()` take an extra action to be performed on each copy:\n* `CopyAll\u003cT\u003e(Func\u003cT, T\u003e)` calls the supplied function for each copied object \n* `CopyAll\u003cT\u003e(Func\u003cT, T, int\u003e)` calls the supplied function for each mapped object passing the zero based index of the object \n\n## Copying between different types\n\nYou can copy an object of one type to another type using the `Copy\u003cTFrom,TTo\u003e()` extension method.  The type being mapped *to* **must** have a parameterless contructor, then all readable public properties (and fields) of the source type are copied to properties (or fields) of the target type.  \n\n`Mapper` can also copy sequences of objects via the `CopyAll\u003cTFrom,TTo\u003e()` extension which takes a `IEnumerable\u003cTFrom\u003e` and returns an `IEnumerable\u003cTTo\u003e`.  `CopyAll\u003cTFrom,TTo\u003e()` has overloads that allow the mapping to be customized:\n\n* `CopyAll\u003cTFrom,TTo\u003e(Func\u003cTFrom,TTo\u003e)` calls the supplied function for each mapped object\n* `CopyAll\u003cTFrom,TTo\u003e(Func\u003cTFrom,TTo, int\u003e)` calls the supplied function for each mapped object passing the zero based index of the object \n\n## Type compatibility\n\nWhen copying data types must be compatible in *some sense*, the following lists the type compatibility rules:\n\n| Source Type                                       | Target Type                                                                                                              |\n|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| Any numeric type or enum                          | Any numeric type or any enum                                                                                             |\n| `Nullable\u003cT\u003e` where T is any numeric type or enum | any numeric type or any enum. `default(T)` is used as the value if value is null                                         |\n| `Nullable\u003cT\u003e` where T is any numeric type or enum | `Nullable\u003cT\u003e` where T is any numeric type or enum                                                                        |\n| any type other                                    | type must match, be [assignable](https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom(v=vs.110).aspx), or have an explicit static cast |\n\n## Name compatibility\n\nFor `Copy`, `CopyAll` and all the data mappings, the following rules apply when looking for the destination field or property to map to:\n\n1. the source name (case insensitive)\n2. if the name ends with 'ID' then try the name without 'ID'  (case insensitive)\n3. if the name does *not* end with 'ID' then try the name with 'Id' suffix added (case insensitive)\n4. the above names with underscores removed  (case insensitive)\n5. the above names with the target class name prefix removed (case insensitive)\n\nNote that the rules are following in the above sequence, and that rules 2 \u0026 3 only apply when the data type of the field being mapped is a primative type, and enum, or a nullable\u003cT\u003e of those types.\n\nFor example, if the source name is `ORDER_ID` then the following names would be considered  (shown in perference order):\n\n1. ORDER_ID\n2. ORDER_\n3. ORDERID\n4. ORDER\n5. ID     (* this will be considered when mapping from a DbDataReader to a type called `Order`)\n\nNote: name comparison is *case insensitive*.\n\n## ADO.NET DbConnection extensions\n\n`Mapper` adds `Query()` and `Execute()` extension methods, as well as `...Async()` variants.\n\nThe `Query(string sql, object parameters = null)` extension executes the supplied SQL (with optional parameters) and returns a `DbDataReader`.\n\nThe `Execute(string sql, object parameters = null)` extension executes the supplied SQL (with optional parameters) but *just returns the number of rows affected*.\n\n### Calling stored procedures\n\nThe `QueryProc(string procName, object parameters = null)` extension calls the named stored procedure (with optional parameters) and returns a `DbDataReader`.\n\nThe `ExecuteProc(string procName, object parameters = null)` extension calls the named stored procedure (with optional parameters) but *just returns the number of rows affected*.\n\nHow are these methods different from `Query` and `Execute`?  You don't need to specify the full command syntax, for example:\n\n```\nvar order = connection.Query(\"EXEC dbo.GetOrderById @id=@id\", new { id=1 }).SingleOrDefault\u003cOrder\u003e();\n```\n\n```\nvar order = connection.QueryProc(\"dbo.GetOrderById\", new { id=1 }).SingleOrDefault\u003cOrder\u003e();\n```\n\n### Automatic connection open and close\n\nAs a conveniance, if `Query()` and `Execute()` are called on a closed connection then `Mapper` will open the connection, use it, and close/dispose the connection afterwards.\n\n## ADO.NET DbCommand methods\n\n`Mapper` adds `AddParameters(object parameters)` extension method to `System.Data.Common.DbCommand`. `AddParameters` will add a `DbDataParameter` to the commands `Parameters` collection for each readable public property (and field) of `parameters`, setting the type and value.\n\n## ADO.NET DbDataReader extensions\n\n`Mapper` adds the following extension methods to `DbDataReader` (as returned by `Query()` and `Execute()`) to read and map the data:\n\n* `Single\u003cT\u003e(this DbDataReader reader, ...)` reads one and only one `T`\n* `SingleOrDefault\u003cT\u003e(this DbDataReader reader, ...)` reads one or zero `T`\n* `ToDictionary\u003cTKey, TValue\u003e(this DbDataReader reader, ...)` reads `TValue` items and creates a hash table with a unique `TKey` for each `TValue`\n* `ToList\u003cT\u003e(this DbDataReader reader, ...)` reads a list of `T`\n* `ToLookup\u003cTKey, TValue\u003e(this DbDataReader reader, ...)` reads `TValue` items but groups the items by key\n\nAdditional `...Async()` extension methods also exist.\n\n`T` can be:\n* a `class` with a parameterless constructor - in which case public set-able fields and properties are mapped\n* a `struct` - again, public set-able fields and properties are mapped\n* a single value, e.g. `long`, `string` or an enumeration\n* a `Nullable\u003cT\u003e` of primative value (e.g. 'int') or an enumeration\n\nNote that the above methods take an optional `Action\u003cDbDataReader,T\u003e` parameter that allow you to add custom mapping between the current record of the data reader and the mapped version of `T`.\n\nThe above extension methods `Close()` the reader *after reading the last result*.  This means that a reader with one result will be closed (disposed) automatically, but reading multiple results is still possible, for example:\n\n```csharp\n[Test]\npublic void can_read_mutliple_results()\n{\n    using (var cnn = new SqlConnection(mapperTest))\n    {\n         var rs = cnn.Query(\"select * from dbo.Currency where id \u003c= 4; select * from dbo.Currency where id \u003e 4;\");\n         var small = rs.ToList\u003cCurrency\u003e(); // read first result\n         var big = rs.ToList\u003cCurrency\u003e();  // read second result\n         Assert.IsTrue(rs.IsClosed);\n         Assert.AreEqual(4, small.Count);\n         Assert.AreEqual(6, big.Count);\n    }\n}\n```\n\n## ADO.NET SqlDataRecord methods\n\n`Mapper` adds a `ToSqlTable\u003cT\u003e()` extension method to `IEnumerable\u003cT\u003e` that convert it into a `IEnumerable\u003cSqlDataRecord\u003e` such that it can be passed as a [table valued parameter](https://msdn.microsoft.com/en-us/library/bb675163(v=vs.110).aspx) to SQL Server.\n\n```\nCurrency[] currencies = ....;\nvar type = new SqlTableType(\"dbo.CurrencyType\", new SqlMetaData(\"ID\", SqlDbType.Int),  new SqlMetaData(\"NAME\", SqlDbType.VarChar, 50));\nvar table = orders.ToSqlTable(type);\nconnection.ExecuteProc(\"dbo.UpdateCurrencies\", new { rows = table }); // stored proc that takes a @rows parameter\n```\n\nPrimative type, enums and strings can be converted directly, for example:\n\n```\nint[] orderIds = { 1, 2, 3}\nvar type = new SqlTableType(\"dbo.IdType\", new SqlMetaData(\"ID\", SqlDbType.Int));\nvar ids = orderIds.ToSqlTable(type);\nvar loaed = connection.QueryProc(\"dbo.GetCurrencies\", new { ids }).ToList\u003cCurrency\u003e(); // stored proc that takes a @ids parameter\n```\n\n## Examples\n\nQuery returning a list:\n```csharp\nList\u003cOrder\u003e list = connection.Query(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.ToList\u003cOrder\u003e();\n```\n\nAsynchronously query returning a list:\n```csharp\nList\u003cOrder\u003e list = await connection.QueryAsync(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.ToListAsync\u003cOrder\u003e();\n```\n\nQuery returning a dictionary for a unqiue key:\n```csharp\nDictionary\u003cint, Order\u003e byId = connection.Query(\"select * from dbo.[Order] where status = @Status\", new { Status = 1 })\n\t.ToDictionary\u003cOrder\u003e(order =\u003e order.Id);\n```\n\nAsynchronously query returning a dictionary for a unqiue key::\n```csharp\nDictionary\u003cint, Order\u003e byId = await connection.QueryAsync(\"select * from dbo.[Order] where status = @Status\", new { Status = 1 })\n\t.ToDictionaryAsync\u003cOrder\u003e(order =\u003e order.Id);\n```\n\nQuery returning `HashLookup` for a non-unqiue key:\n```csharp\nHashLookup\u003cint, Order\u003e byStatus = connection.Query(\"select * from dbo.[Order] where order_date \u003e @OrderDate\", new { OrderDate = new DateTime(2016, 8, 1) })\n\t.ToLookup\u003cOrder\u003e(order =\u003e order.Status);\n```\n\nAsynchronously query returning `HashLookup` for a non-unqiue key:\n```csharp\nHashLookup\u003cint, Order\u003e byStatus = await connection.QueryAsync(\"select * from dbo.[Order] where order_date \u003e @OrderDate\", new { OrderDate = new DateTime(2016, 8, 1) })\n\t.ToLookupAsync\u003cOrder\u003e(order =\u003e order.Status);\n```\n\nQuery returning exactly one row:\n```csharp\nOrder order = connection.Query(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.Single\u003cOrder\u003e();\n```\n\nAsynchronously query returning exactly one row:\n```csharp\nOrder order = await connection.QueryAsync(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.SingleAsync\u003cOrder\u003e();\n```\n\nQuery returning exactly one row of a primative type:\n```csharp\nint count = connection.Query(\"select count(*) from dbo.[Order] where order_type = @orderType\", new { orderType = 3 })\n\t.Single\u003cint\u003e();\n```\n\nQuery returning exactly zero or one rows:\n```csharp\nOrder order = connection.Query(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.SingleOrDefault\u003cOrder\u003e();\n```\n\nAsynchronously query returning zero or one rows:\n```csharp\nOrder order = await connection.QueryAsync(\"select * from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.SingleOrDefaultAsync\u003cOrder\u003e();\n```\n\nQuery returning zero or one rows of a enum:\n```csharp\nOrderType? orderType = connection.Query(\"select order_type_id from dbo.[Order] where order_id = @OrderId\", new { OrderId = 123 })\n\t.SingleOrDefault\u003cOrderType?\u003e();\n```\n\nCall a stored procedure that does not return results set(s)\n```csharp\nint rowsChanged = connection.ExecuteProc(\"update_user_name\", new { id=123, name=\"fred\" });\n```\n\nAsynchronously call a stored procedure that does not return results set(s)\n```csharp\nint rowsChanged = await connection.ExecuteProcAsync(\"update_user_name\", new { id=123, name=\"fred\" });\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusterwood%2Fmapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbusterwood%2Fmapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusterwood%2Fmapper/lists"}