Skip to content

Commit

Permalink
Update readme and rename methods on IDBCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
busterwood committed Mar 18, 2016
1 parent af5cb5b commit 5894f50
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 37 deletions.
5 changes: 5 additions & 0 deletions Mapper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapper", "Mapper\Mapper.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mapper.UnitTests", "Mapper.UnitTests\Mapper.UnitTests.csproj", "{AD2B32F7-7DF4-4B7F-9115-BDB6ACA20EB1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{09E1E16A-03B8-421F-AEB7-BB64F8A83D7A}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
23 changes: 11 additions & 12 deletions Mapper/CommandExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.SqlServer.Server;

namespace Mapper
{
public static class CommandExtension
public static class DbCommandExtensions
{
private static readonly MostlyReadDictionary<Type, Delegate> Methods = new MostlyReadDictionary<Type, Delegate>();

/// <summary>Executes the <paramref name="cmd"/> reading exactly one item</summary>
/// <exception cref="InvalidOperationException"> when zero values read or more than one value can be read</exception>
public static T Single<T>(this IDbCommand cmd)
public static T ReadSingle<T>(this IDbCommand cmd)
{
Contract.Requires(cmd != null);
Contract.Ensures(Contract.Result<T>() != null);
Expand All @@ -30,7 +29,7 @@ public static T Single<T>(this IDbCommand cmd)

/// <summary>Executes the <paramref name="cmd"/> reading exactly one item</summary>
/// <exception cref="InvalidOperationException"> when zero values read or more than one value can be read</exception>
public static async Task<T> SingleAsync<T>(this SqlCommand cmd)
public static async Task<T> ReadSingleAsync<T>(this SqlCommand cmd)
{
Contract.Requires(cmd != null);
Contract.Ensures(Contract.Result<T>() != null);
Expand All @@ -42,7 +41,7 @@ public static async Task<T> SingleAsync<T>(this SqlCommand cmd)

/// <summary>Executes the <paramref name="cmd"/> reading one item</summary>
/// <remarks>Returns the default vaue of T if no values be read, i.e may return null</remarks>
public static T SingleOrDefault<T>(this IDbCommand cmd)
public static T ReadSingleOrDefault<T>(this IDbCommand cmd)
{
Contract.Requires(cmd != null);
using (var reader = cmd.ExecuteReader())
Expand All @@ -53,7 +52,7 @@ public static T SingleOrDefault<T>(this IDbCommand cmd)

/// <summary>Executes the <paramref name="cmd"/> reading one item</summary>
/// <remarks>Returns the default vaue of T if no values be read, i.e may return null</remarks>
public static async Task<T> SingleOrDefaultAsync<T>(this SqlCommand cmd)
public static async Task<T> ReadSingleOrDefaultAsync<T>(this SqlCommand cmd)
{
Contract.Requires(cmd != null);
using (var reader = await cmd.ExecuteReaderAsync())
Expand All @@ -63,7 +62,7 @@ public static async Task<T> SingleOrDefaultAsync<T>(this SqlCommand cmd)
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records into a list</summary>
public static List<T> ToList<T>(this IDbCommand cmd)
public static List<T> ReadList<T>(this IDbCommand cmd)
{
Contract.Requires(cmd != null);
Contract.Ensures(Contract.Result<List<T>>() != null);
Expand All @@ -74,7 +73,7 @@ public static List<T> ToList<T>(this IDbCommand cmd)
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records into a list</summary>
public static async Task<List<T>> ToListAsync<T>(this SqlCommand cmd)
public static async Task<List<T>> ReadListAsync<T>(this SqlCommand cmd)
{
Contract.Requires(cmd != null);
Contract.Ensures(Contract.Result<List<T>>() != null);
Expand All @@ -85,7 +84,7 @@ public static async Task<List<T>> ToListAsync<T>(this SqlCommand cmd)
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records into a dictionary, using the supplied <paramref name="keyFunc"/> to generate the key</summary>
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IDbCommand cmd, Func<TValue, TKey> keyFunc)
public static Dictionary<TKey, TValue> ReadDictionary<TKey, TValue>(this IDbCommand cmd, Func<TValue, TKey> keyFunc)
{
Contract.Requires(cmd != null);
Contract.Requires(keyFunc != null);
Expand All @@ -97,7 +96,7 @@ public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IDbComman
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records into a dictionary, using the supplied <paramref name="keyFunc"/> to generate the key</summary>
public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TKey, TValue>(this SqlCommand cmd, Func<TValue, TKey> keyFunc)
public static async Task<Dictionary<TKey, TValue>> ReadDictionaryAsync<TKey, TValue>(this SqlCommand cmd, Func<TValue, TKey> keyFunc)
{
Contract.Requires(cmd != null);
Contract.Requires(keyFunc != null);
Expand All @@ -109,7 +108,7 @@ public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TKey, TValu
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records in a lookup, grouped by key, using the supplied <paramref name="keyFunc"/> to generate the key</summary>
public static HashLookup<TKey, TValue> ToLookup<TKey, TValue>(this IDbCommand cmd, Func<TValue, TKey> keyFunc)
public static HashLookup<TKey, TValue> ReadLookup<TKey, TValue>(this IDbCommand cmd, Func<TValue, TKey> keyFunc)
{
Contract.Requires(cmd != null);
Contract.Requires(keyFunc != null);
Expand All @@ -121,7 +120,7 @@ public static HashLookup<TKey, TValue> ToLookup<TKey, TValue>(this IDbCommand cm
}

/// <summary>Executes the <paramref name="cmd"/> and reads all the records in a lookup, grouped by key, using the supplied <paramref name="keyFunc"/> to generate the key</summary>
public static async Task<HashLookup<TKey, TValue>> ToLookupAsync<TKey, TValue>(this SqlCommand cmd, Func<TValue, TKey> keyFunc)
public static async Task<HashLookup<TKey, TValue>> ReadLookupAsync<TKey, TValue>(this SqlCommand cmd, Func<TValue, TKey> keyFunc)
{
Contract.Requires(cmd != null);
Contract.Requires(keyFunc != null);
Expand Down
28 changes: 14 additions & 14 deletions Mapper/ConnectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static T QuerySingle<T>(this IDbConnection cnn, string sql, object parame
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.Single<T>();
return cmd.ReadSingle<T>();
}
}

Expand All @@ -35,7 +35,7 @@ public static Task<T> QuerySingleAsync<T>(this SqlConnection cnn, string sql, ob
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.SingleAsync<T>();
return cmd.ReadSingleAsync<T>();
}
}

Expand All @@ -47,7 +47,7 @@ public static T QuerySingleOrDefault<T>(this IDbConnection cnn, string sql, obje
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.SingleOrDefault<T>();
return cmd.ReadSingleOrDefault<T>();
}
}

Expand All @@ -59,7 +59,7 @@ public static Task<T> QuerySingleOrDefaultAsync<T>(this SqlConnection cnn, strin
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.SingleOrDefaultAsync<T>();
return cmd.ReadSingleOrDefaultAsync<T>();
}
}

Expand All @@ -73,7 +73,7 @@ public static List<T> QueryList<T>(this IDbConnection cnn, string sql, object pa
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.ToList<T>();
return cmd.ReadList<T>();
}
}

Expand All @@ -87,7 +87,7 @@ public static Task<List<T>> QueryListAsync<T>(this SqlConnection cnn, string sql
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.ToListAsync<T>();
return cmd.ReadListAsync<T>();
}
}

Expand All @@ -102,7 +102,7 @@ public static Dictionary<TKey, TValue> QueryDictionary<TKey, TValue>(this IDbCon
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, null);
return cmd.ToDictionary(keyFunc);
return cmd.ReadDictionary(keyFunc);
}
}

Expand All @@ -117,7 +117,7 @@ public static Task<Dictionary<TKey, TValue>> QueryDictionaryAsync<TKey, TValue>(
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, null);
return cmd.ToDictionaryAsync(keyFunc);
return cmd.ReadDictionaryAsync(keyFunc);
}
}

Expand All @@ -133,7 +133,7 @@ public static Dictionary<TKey, TValue> QueryDictionary<TKey, TValue>(this IDbCon
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.ToDictionary(keyFunc);
return cmd.ReadDictionary(keyFunc);
}
}

Expand All @@ -149,7 +149,7 @@ public static Task<Dictionary<TKey, TValue>> QueryDictionaryAsync<TKey, TValue>(
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.ToDictionaryAsync(keyFunc);
return cmd.ReadDictionaryAsync(keyFunc);
}
}

Expand All @@ -164,7 +164,7 @@ public static ILookup<TKey, TValue> QueryLookup<TKey, TValue>(this IDbConnection
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, null);
return cmd.ToLookup(keyFunc);
return cmd.ReadLookup(keyFunc);
}
}

Expand All @@ -180,7 +180,7 @@ public static ILookup<TKey, TValue> QueryLookup<TKey, TValue>(this IDbConnection
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return cmd.ToLookup(keyFunc);
return cmd.ReadLookup(keyFunc);
}
}

Expand All @@ -195,7 +195,7 @@ public static async Task<HashLookup<TKey, TValue>> QueryLookupAsync<TKey, TValue
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, null);
return await cmd.ToLookupAsync(keyFunc);
return await cmd.ReadLookupAsync(keyFunc);
}
}

Expand All @@ -211,7 +211,7 @@ public static async Task<HashLookup<TKey, TValue>> QueryLookupAsync<TKey, TValue
using (var cmd = cnn.CreateCommand())
{
SetupCommand(cmd, cnn, sql, parameters);
return await cmd.ToLookupAsync(keyFunc);
return await cmd.ReadLookupAsync(keyFunc);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Mapper/Mapper.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package >
<metadata>
<id>Mapper</id>
<version>1.0.1.2</version>
<version>1.0.1.3</version>
<authors>BusterWood</authors>
<description>A convention-based object cloner, object-object mapper (like AutoMapper), IDataReader to object mapper, object to IDbDataParameter mapper, etc.</description>
<projectUrl>https://github.com/busterwood/mapper</projectUrl>
Expand Down
2 changes: 1 addition & 1 deletion Mapper/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.1.2")]
[assembly: AssemblyFileVersion("1.0.1.3")]

[assembly: InternalsVisibleTo("Mapper.UnitTests")]
80 changes: 71 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,94 @@

## Cloning

`Mapper` contains an extension method for all objects called `Clone` which performs a *shallow* clone. The object being clone *must* have a parameterless contructor, then all public properties and fields are copied.
`Mapper` contains an extension method for all objects called `Clone` which performs a *shallow* clone. The type being cloned *must* have a parameterless contructor, then all public properties and fields are copied.
`Mapper` can also clone sequences of object via the `MapSome<T>()` extension which takes a `IEnumerable<T>` and returns an `IEnumerable<T>`.

## Mapping

You can copy an object of one type to another type using the `Map<TFrom,TTo>()` extension method.
You can copy an object of one type to another type using the `Map<TFrom,TTo>()` 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.

A property (or field) types must be compatible in some sense, the following list the type compatibility rules:

| Source Type | Target Type |
|---------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| Any numeric type or enum | Any numeric type or any enum |
| `Nullable<T>` 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 |
| `Nullable<T>` where T is any numeric type or enum | `Nullable<T>` where T is any numeric type or enum |
| any type other | type must match or be [assignable](https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom(v=vs.110).aspx) |

## Name compatibility

For `Map`, `MapSome` and all the data mappings, the following rules apply when looking for candidate names:
1. the source name (case insensitive)
2. if the name ends with 'ID' then try the name without 'ID' (case insensitive)
3. the name above names with underscores removed (case insensitive)

For example, if the source name is `ORDER_ID` then the following names would be considered (shown in perference order):
1. ORDER_ID
2. ORDER_
3. ORDERID
4. ORDER

## Data extensions

The headline examples is much like Dapper, but methods are strongly typed:
The headline examples is much like Dapper, but methods have stronly typed return values:

Select a list:
```
List<Order> list = connection.QueryList<Order>("select * from dbo.[Order] where order_id = @OrderId", new { OrderId = 123 });
```

Select a dictionary keyed by the primary key
Select a dictionary keyed by the primary key:
```
Dictionary<int, Order> list = connection.QueryDictionary<int, Order>("select * from dbo.[Order] where status = @Status", new { Status = 1 }, order => order.Id);
```

Select a key to multiple value `ILookup`
Select a key to multiple value `ILookup`:
```
ILookup<int, Order> list = connection.QueryLookup<int, Order>("select * from dbo.[Order] where order_date > @OrderDate", new { OrderDate = new DateTime(2016, 8, 1) }, order => order.Status);
```

## Composability
TODO: `IDataReader` extensions
TODO: `IDataCommand` extensions
TODO: `SqlDataRecord` extensions
## Data Composability

`Mapper` has a series of extension methods for ADO.Net types:

### IDataReader methods
`IDataReader` has the following extension methods:
* `Single<T>()` for reading exactly one row
* `SingleOrDefault<T>()` for reading zero or one rows
* `ToList<T>()` for reading all records into a `List<T>`
* `ToDictinary<TKey,TValue>(Func<TKey,TValue> keyFunc)` for reading all records into a `Dictinary<TKey,TValue>` using the supplied function to get work out the key. Note that the key must be unique.
* `ToLookup<TKey,TValue>(Func<TKey,TValue> keyFunc)` for reading all records into a `ILookup<TKey,TValue>` using the supplied function to get work out the key. Each key may have multiple values.

### SqlDataReader async methods

Additionally `SqlDataReader` has the same set of methods as `IDataReader` but with `Async` suffix.

## IDbCommand methods

`Mapper` adds `AddParameters(object parameters)` extension method to `IDbCommand`. `AddParameters` will add a `IDataParameter` to the commands `Parameters` collection for each readable public property (and field) of `parameters`, setting the type and value.

For convenience `Mapper` adds the following extension method to `IDbCommand`:
* `ReadSingle<T>()` for exeucting the command and reading exactly one row
* `ReadSingleOrDefault<T>()` for exeucting the command and reading zero or one rows
* `ReadList<T>()` for exeucting the command and reading all records into a `List<T>`
* `ReadDictinary<TKey,TValue>(Func<TKey,TValue> keyFunc)` for exeucting the command and reading all records into a `Dictinary<TKey,TValue>` using the supplied function to get work out the key. Note that the key must be unique.
* `ReadLookup<TKey,TValue>(Func<TKey,TValue> keyFunc)` for exeucting the command and reading all records into a `ILookup<TKey,TValue>` using the supplied function to get work out the key. Each key may have multiple values.

### SqlCommand async methods

Additionally `SqlCommand` has the same set of methods as `IDbDataReader` but with `Async` suffix.

### IDbConnetion methods

For convenience `Mapper` adds the following extension method to `IDbConnection`:
* `QuerySingle<T>()` for executing the command and reading exactly one row
* `QuerySingleOrDefault<T>()` for executing the command and reading zero or one rows
* `Queryist<T>()` for executing the command and reading all records into a `List<T>`
* `QueryDictinary<TKey,TValue>(Func<TKey,TValue> keyFunc)` for executing the command and reading all records into a `Dictinary<TKey,TValue>` using the supplied function to get work out the key. Note that the key must be unique.
* `QueryLookup<TKey,TValue>(Func<TKey,TValue> keyFunc)` for executing the command and reading all records into a `ILookup<TKey,TValue>` using the supplied function to get work out the key. Each key may have multiple values.

### SqlDataRecord methods

`Mapper` has a extension method `ToTableType<T>()` for converting a source `IEnumerable<T>` into an `IEnumerable<SqlDataRecord>` 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.

0 comments on commit 5894f50

Please sign in to comment.