@@ -9,13 +9,13 @@ Slapper.AutoMapper maps dynamic data to static types.
9
9
10
10
### What is it? ###
11
11
12
- Slapper.AutoMapper ( Pronounced Slapper-Dot-Automapper ) is a mapping library that can convert dynamic data into
12
+ Slapper.AutoMapper ( Pronounced Slap-er dot aw-toe-map-er ) is a mapping library that can convert dynamic data into
13
13
static types and populate complex nested child objects.
14
14
15
15
It primarily converts C# dynamics and ` IDictionary<string, object> ` to strongly typed objects and supports
16
16
populating an entire object graph by using underscore notation to underscore into nested objects.
17
17
18
- Why use an IDictionary? Because a C# dynamic ( well really an ExpandoObject ) can easily be cast to one allowing
18
+ Why use an IDictionary? Because a C# dynamic ( well really an ExpandoObject ) can easily be cast to an ` IDictionary<string, object> ` allowing
19
19
this library to be used in a variety of ways not only with dictionaries of property names and values but with dynamics as well.
20
20
21
21
Okay, so what... doesn't other ORMs do this?
@@ -26,16 +26,16 @@ a Customer and it's list of Orders and it's list of OrderDetails.
26
26
27
27
### Is this an ORM? ###
28
28
29
- No, this is not an ORM in itself but can easily be extended to create one. This library can be thought of as a building
29
+ No, this is not an ORM but can be easily extended to create one. This library can be thought of as a building
30
30
block of an ORM or used as an extension to an existing ORM or Micro-ORM.
31
31
32
- ORMs typically query the database and then map the data into objects. Slapper just handles the mapping part and essentially
32
+ ORMs typically query the database and then map the data into objects. Slapper handles the mapping part and essentially
33
33
only has one input: a dictionary of property names and values.
34
34
35
35
### What problems does this solve? ###
36
36
37
37
Simply put, it allows you to convert dynamic data into strongly typed objects with ease and populating complex nested child
38
- objects in your object hierarchy comes free out of the box --something severely lacking in almost every Micro-ORM solution!
38
+ objects in your object hierarchy comes for free out of the box --something severely lacking in almost every Micro-ORM solution!
39
39
40
40
### Auto mapping? ###
41
41
@@ -67,10 +67,9 @@ you are wishing to populate. Slapper.AutoMapper does not handle data type conver
67
67
data into the library.
68
68
69
69
And that's it, feel free to explore the examples below and the unit tests and hack away. This library is licensed with the MIT license
70
- so feel free to re-use the code in your own projects any way you please. I only ask that you keep the license comment at the top of the
71
- file or any file that uses significant portions of this projects code for proper attribution.
70
+ so feel free to re-use the code in your own projects any way you please as long as you provide proper attribution.
72
71
73
- Slapper.AutoMapper is also available on NuGet as a compiled dll if you prefer that. Check it out at : http://www.nuget.org/packages/Slapper.AutoMapper/
72
+ Slapper.AutoMapper is also available on NuGet available here : http://www.nuget.org/packages/Slapper.AutoMapper/
74
73
75
74
Now let the slapping commence! :)
76
75
@@ -85,21 +84,21 @@ The following simple example maps a dictionary of property names and values to a
85
84
``` csharp
86
85
public class Person
87
86
{
88
- public int Id ;
89
- public string FirstName ;
90
- public string LastName ;
87
+ public int Id ;
88
+ public string FirstName ;
89
+ public string LastName ;
91
90
}
92
91
93
92
[Test ]
94
93
public void Can_Map_Matching_Field_Names_With_Ease ()
95
94
{
96
95
// Arrange
97
96
var dictionary = new Dictionary <string , object >
98
- {
99
- { " Id" , 1 },
100
- { " FirstName" , " Clark" },
101
- { " LastName" , " Kent" }
102
- };
97
+ {
98
+ { " Id" , 1 },
99
+ { " FirstName" , " Clark" },
100
+ { " LastName" , " Kent" }
101
+ };
103
102
104
103
// Act
105
104
var person = Slapper .AutoMapper .Map <Person >( dictionary );
@@ -121,9 +120,9 @@ When mapping dynamics use the `MapDynamic<T>()` method instead of the `Map<T>()`
121
120
``` csharp
122
121
public class Person
123
122
{
124
- public int Id ;
125
- public string FirstName ;
126
- public string LastName ;
123
+ public int Id ;
124
+ public string FirstName ;
125
+ public string LastName ;
127
126
}
128
127
129
128
[Test ]
@@ -149,7 +148,7 @@ public void Can_Map_Matching_Field_Names_Using_Dynamic()
149
148
### Mapping Nested Types Using a Dictionary ###
150
149
151
150
The following example maps a list of dictionaries of property names and values to a Customer class and using underscore notation ("_ "),
152
- Slapper.AutoMapper properly populates the nested child types. This is really what I would consider this libraries secret sauce.
151
+ Slapper.AutoMapper properly populates the nested child types. This is really what I would consider this library's secret sauce.
153
152
You can just as easily use a list of dynamics which is demonstrated below too which is what is typically returned back from Micro ORMs.
154
153
155
154
As an example, the following SQL would return similar results to what is in the dictionaries in the example below ( Note the use of SQL aliases ).
@@ -159,90 +158,90 @@ SQL and the mapping to C# objects at the same time by use of SQL aliases.*
159
158
160
159
``` sql
161
160
SELECT c .CustomerId ,
162
- c .FirstName ,
163
- c .LastName ,
164
- o .OrderId AS Orders_OrderId,
165
- o .OrderTotal AS Orders_OrderTotal,
166
- od .OrderDetailId AS Orders_OrderDetails_OrderId,
167
- od .OrderDetailId AS Orders_OrderDetails_OrderDetailId,
168
- od .OrderDetailTotal AS Orders_OrderDetails_OrderDetailTotal
161
+ c .FirstName ,
162
+ c .LastName ,
163
+ o .OrderId AS Orders_OrderId,
164
+ o .OrderTotal AS Orders_OrderTotal,
165
+ od .OrderDetailId AS Orders_OrderDetails_OrderId,
166
+ od .OrderDetailId AS Orders_OrderDetails_OrderDetailId,
167
+ od .OrderDetailTotal AS Orders_OrderDetails_OrderDetailTotal
169
168
FROM Customer c
170
- JOIN Order o ON c .CustomerId = o .CustomerId
171
- JOIN OrderDetail od ON o .OrderId = od .OrderId
169
+ JOIN Order o ON c .CustomerId = o .CustomerId
170
+ JOIN OrderDetail od ON o .OrderId = od .OrderId
172
171
```
173
172
174
173
This example is indicative of the results you would commonly encounter when querying a database and joining on an Orders
175
174
and OrderDetails table --you would get back duplicate results in some fields. Notice how the CustomerId in both dictionaries
176
175
are the same. Because of Slapper.AutoMapper's default conventions, it will identify the CustomerId field as being the
177
176
identifier ( or primary key so to speak ). This means that when it attempts to convert the second dictionary to a Customer
178
- object, it will see that it has already created a Customer object with an CustomerId of 1 and will simply re-use the previous
177
+ object, it will see that it has already created a Customer object with a CustomerId of 1 and will simply re-use the previous
179
178
instance resulting in only one Customer object being returned back. This is how Slapper.AutoMapper effectively groups results
180
- together and is the key to this libraries awesomeness.
179
+ together and is the key to this library's awesomeness.
181
180
182
181
183
182
``` csharp
184
183
public class Customer
185
184
{
186
- public int CustomerId ;
187
- public string FirstName ;
188
- public string LastName ;
189
- public IList <Order > Orders ;
185
+ public int CustomerId ;
186
+ public string FirstName ;
187
+ public string LastName ;
188
+ public IList <Order > Orders ;
190
189
}
191
190
192
191
public class Order
193
192
{
194
- public int OrderId ;
195
- public decimal OrderTotal ;
196
- public IList <OrderDetail > OrderDetails ;
193
+ public int OrderId ;
194
+ public decimal OrderTotal ;
195
+ public IList <OrderDetail > OrderDetails ;
197
196
}
198
197
199
198
public class OrderDetail
200
199
{
201
- public int OrderDetailId ;
202
- public decimal OrderDetailTotal ;
200
+ public int OrderDetailId ;
201
+ public decimal OrderDetailTotal ;
203
202
}
204
203
205
204
[Test ]
206
205
public void I_Can_Map_Nested_Types_And_Resolve_Duplicate_Entries_Properly ()
207
206
{
208
- // Arrange
209
- var dictionary = new Dictionary <string , object >
210
- {
211
- { " CustomerId" , 1 },
212
- { " FirstName" , " Bob" },
213
- { " LastName" , " Smith" },
214
- { " Orders_OrderId" , 1 },
215
- { " Orders_OrderTotal" , 50 . 50 m },
216
- { " Orders_OrderDetails_OrderDetailId" , 1 },
217
- { " Orders_OrderDetails_OrderDetailTotal" , 25 . 00 m }
218
- };
219
-
220
- var dictionary2 = new Dictionary <string , object >
221
- {
222
- { " CustomerId" , 1 },
223
- { " FirstName" , " Bob" },
224
- { " LastName" , " Smith" },
225
- { " Orders_OrderId" , 1 },
226
- { " Orders_OrderTotal" , 50 . 50 m },
227
- { " Orders_OrderDetails_OrderDetailId" , 2 },
228
- { " Orders_OrderDetails_OrderDetailTotal" , 25 . 50 m }
229
- };
230
-
231
- var list = new List <IDictionary <string , object >> { dictionary , dictionary2 };
232
-
233
- // Act
234
- var customers = Slapper .AutoMapper .Map <Customer >( list );
235
-
236
- // Assert
207
+ // Arrange
208
+ var dictionary = new Dictionary <string , object >
209
+ {
210
+ { " CustomerId" , 1 },
211
+ { " FirstName" , " Bob" },
212
+ { " LastName" , " Smith" },
213
+ { " Orders_OrderId" , 1 },
214
+ { " Orders_OrderTotal" , 50 . 50 m },
215
+ { " Orders_OrderDetails_OrderDetailId" , 1 },
216
+ { " Orders_OrderDetails_OrderDetailTotal" , 25 . 00 m }
217
+ };
218
+
219
+ var dictionary2 = new Dictionary <string , object >
220
+ {
221
+ { " CustomerId" , 1 },
222
+ { " FirstName" , " Bob" },
223
+ { " LastName" , " Smith" },
224
+ { " Orders_OrderId" , 1 },
225
+ { " Orders_OrderTotal" , 50 . 50 m },
226
+ { " Orders_OrderDetails_OrderDetailId" , 2 },
227
+ { " Orders_OrderDetails_OrderDetailTotal" , 25 . 50 m }
228
+ };
229
+
230
+ var list = new List <IDictionary <string , object >> { dictionary , dictionary2 };
231
+
232
+ // Act
233
+ var customers = Slapper .AutoMapper .Map <Customer >( list );
234
+
235
+ // Assert
237
236
238
- // There should only be a single customer
239
- Assert .That ( customers .Count () == 1 );
237
+ // There should only be a single customer
238
+ Assert .That ( customers .Count () == 1 );
240
239
241
- // There should only be a single Order
242
- Assert .That ( customers .FirstOrDefault ().Orders .Count == 1 );
240
+ // There should only be a single Order
241
+ Assert .That ( customers .FirstOrDefault ().Orders .Count == 1 );
243
242
244
- // There should be two OrderDetails
245
- Assert .That ( customers .FirstOrDefault ().Orders .FirstOrDefault ().OrderDetails .Count == 2 );
243
+ // There should be two OrderDetails
244
+ Assert .That ( customers .FirstOrDefault ().Orders .FirstOrDefault ().OrderDetails .Count == 2 );
246
245
}
247
246
248
247
[Test ]
@@ -294,10 +293,10 @@ Auto mapping allows Slapper to figure out how to effectively group objects toget
294
293
duplicate results. Now internally, no actual grouping is happening but this is the easiest way to conceptualize
295
294
how it works.
296
295
297
- * For the curious, the actual implementation relies upon an instance cache implemented as a Dictionary where the key is the all of
298
- the identifier's hashes summed together and the value being the instance.*
296
+ * For the curious, the actual implementation relies upon an instance cache implemented as a Dictionary where the key is all of
297
+ the identifier's hashes summed together and the value is the instance.*
299
298
300
- A classes identifier(s) play an important role in the ability of the mapper to effectively group objects together. If no
299
+ A class' identifier(s) play an important role in the ability of the mapper to effectively group objects together. If no
301
300
identifiers are found, the mapper will still map the results to the requested type but there will be duplicates in the results.
302
301
303
302
@@ -331,13 +330,13 @@ The following example specifies two identifiers for the Customer object by using
331
330
```csharp
332
331
public class Customer
333
332
{
334
- public int CustomerId ;
333
+ public int CustomerId ;
335
334
336
- public string CustomerType ;
335
+ public string CustomerType ;
337
336
338
- public string FirstName ;
337
+ public string FirstName ;
339
338
340
- public string LastName ;
339
+ public string LastName ;
341
340
}
342
341
343
342
Slapper .AutoMapper .Configuration .AddIdentifiers ( typeof ( Customer ), new List <string > { " CustomerId" , " CustomerType" } );
@@ -347,7 +346,7 @@ Slapper.AutoMapper.Configuration.AddIdentifiers( typeof( Customer ), new List<st
347
346
348
347
Slapper .AutoMapper also supports attribute - based identifiers .
349
348
350
- By default , the library uses it 's own Id attribute that allows you to simply decorate the identifiers on your class with
349
+ By default , the library uses its own Id attribute that allows you to simply decorate the identifiers on your class with
351
350
a `[Slapper.AutoMapper.Id]` attribute.
352
351
353
352
If you wish to use your own attribute instead of the default one, just set the Type to use on the following field:
@@ -361,15 +360,15 @@ The following example specifies two identifiers for the Customer object:
361
360
``` csharp
362
361
public class Customer
363
362
{
364
- [Slapper .AutoMapper .Id ]
365
- public int CustomerId ;
363
+ [Slapper .AutoMapper .Id ]
364
+ public int CustomerId ;
366
365
367
- [Slapper .AutoMapper .Id ]
368
- public string CustomerType ;
366
+ [Slapper .AutoMapper .Id ]
367
+ public string CustomerType ;
369
368
370
- public string FirstName ;
369
+ public string FirstName ;
371
370
372
- public string LastName ;
371
+ public string LastName ;
373
372
}
374
373
````
375
374
@@ -379,20 +378,20 @@ Usage - Caching
379
378
#### Caching Explained ####
380
379
381
380
Slapper .AutoMapper internally maintains a cache of every object it creates , referred to as the instance cache .
382
- This cache plays an important role in Slapper 's ability to easily lookup existing objects and ultimately assists
381
+ This cache plays an important role in Slapper 's ability to easily look up existing objects and ultimately assists
383
382
in the ability for Slapper.AutoMapper to populate complex nested types.
384
383
385
384
Slapper.AutoMapper itself never removes an instance from this cache, so if you tell it to create 50,000 objects,
386
- then there are going to be 50,000 objects in the cache for the lifetime of the current thread or Http context .
385
+ then there are going to be 50,000 objects in the cache for the lifetime of the current thread or HttpContext .
387
386
388
387
The instance cache exists for the lifetime of the current thread and each of your application's threads will
389
- get it's own unique cache making use of this library thread safe.
388
+ get its own unique cache making use of this library thread safe.
390
389
391
390
#### Cache Backing Store ####
392
391
393
- The instance cache backing store will either make use of the HttpContext if one exists or the CallContext of the
394
- executing thread. The library makes use of reflection in order to persist the cache in the HttpContext when
395
- neccessary so that the library does not require a dependency on the System.Web library.
392
+ The instance cache backing store will either use the HttpContext if one exists or the CallContext of the
393
+ executing thread. The library uses reflection to persist the cache in the HttpContext when
394
+ necessary so that the library does not require a dependency on the System.Web library.
396
395
397
396
#### Clearing the Cache ###
398
397
0 commit comments