Skip to content

Commit c2ff88a

Browse files
authored
Importing documentation from the Wiki (#106)
1 parent 6c32f81 commit c2ff88a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2773
-0
lines changed

docs/Assembly-Scanning.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
By default, the assembly in which a base type is declared is searched for derived types. If there are additional assemblies which should be searched, use:
2+
3+
```C#
4+
Mapper.WhenMapping.LookForDerivedTypesIn(
5+
typeof(DerivedType1).Assembly,
6+
typeof(DerivedType2).Assembly);
7+
```

docs/Cloning-Mappers.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Mappers can be cloned, to enable 'derived' mappers to inherit, add to and override a 'root' configuration:
2+
3+
``` C#
4+
var baseMapper = Mapper.CreateNew();
5+
6+
// Setup the base configuration:
7+
baseMapper.WhenMapping
8+
.From<Order>()
9+
.To<OrderDto>()
10+
.Map((o, dto) => o.ProdId)
11+
.To(dto => dto.ProductId)
12+
.And
13+
.Map((o, dto) => o.Id)
14+
.To(dto => dto.OrderNumber);
15+
16+
// Clone a new mapper for mapping UK orders:
17+
var ukOrderMapper = baseMapper.CloneSelf();
18+
19+
// Setup UK-specific configuration:
20+
ukOrderMapper.WhenMapping
21+
.To<OrderDto>()
22+
.Map(ctx => DateTime.UtcNow.AddHours(1))
23+
.To(dto => dto.DateCreated);
24+
25+
// Clone a new mapper for mapping US orders:
26+
var usOrderMapper = baseMapper.CloneSelf();
27+
28+
// Setup US-specific configuration:
29+
usOrderMapper.WhenMapping
30+
.To<OrderDto>()
31+
.Map(ctx => DateTime.UtcNow.AddHours(-4))
32+
.To(dto => dto.DateCreated);
33+
```

docs/Collections.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
The following collection types are supported by default:
2+
3+
- Arrays
4+
- `IEnumerable`
5+
- `IEnumerable<T>`
6+
- `ICollection`
7+
- `ICollection<T>`
8+
- `List<T>`
9+
- `IList<T>`
10+
- `ReadOnlyCollection<T>`
11+
- `IReadOnlyCollection<T>`
12+
- `Collection<T>`
13+
- `HashSet<T>`
14+
- `ISet<T>`
15+
- [`Dictionary<string, T>`](Dictionary-Mapping)
16+
- [`IDictionary<string, T>`](Dictionary-Mapping)
17+
18+
Generic `List<T>` instances are created for interface-type members except `ISet<T>`, which uses a `HashSet<T>`. If a member is already populated with a non-readonly collection (e.g. an array), the existing object will be reused.
19+
20+
[Updates](Performing-Updates) and [merges](Performing-Merges) of collections of identifiable objects (i.e. Entities) are performed in an id-aware manner.
21+
22+
## Null Source Collections
23+
24+
By default, if the source collection matching a target collection is null, the target collection is populated with an empty collection. You can configure setting the target collection to null instead like this:
25+
26+
```C#
27+
// Map null-source collections to null for all source
28+
// and target types:
29+
Mapper.WhenMapping.MapNullCollectionsToNull();
30+
31+
// Map null collections to null only when mapping from
32+
// Order to OrderDto:
33+
Mapper.WhenMapping
34+
.From<Order>()
35+
.To<OrderDto>()
36+
.MapNullCollectionsToNull();
37+
```

docs/Configuration-Classes.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
Multiple, dedicated configuration classes can be created by deriving from the abstract `MapperConfiguration` base class. This enables configuration to be split up and placed nearer where mapping is performed (although [inline](Inline-Configuration) is nearer).
2+
3+
`MapperConfiguration` provides the same configuration API, and exposes services you inject.
4+
5+
### Configuration and Caching
6+
7+
Mapper configuration is set up by implementing the abstract `Configure` method:
8+
9+
```C#
10+
public class ProductMappingConfiguration : MapperConfiguration
11+
{
12+
protected override void Configure()
13+
{
14+
// Configure default Mapper ProductDto -> Productmapping:
15+
WhenMapping
16+
.From<ProductDto>()
17+
.To<Product>()
18+
.Map((dto, p) => dto.Spec)
19+
.To(p => p.Specification)
20+
.And
21+
.Ignore(p => p.Price, p => p.CatNum);
22+
23+
// Cache all Product -> ProductDto mapping plans:
24+
GetPlansFor<Product>().To<ProductDto>();
25+
}
26+
}
27+
```
28+
29+
In this example the default mapper is configured - the one used via the [static](Static-vs-Instance-Mappers) Mapper API.
30+
31+
### Applying Configurations
32+
33+
`MapperConfiguration` classes can be discovered and applied in various ways.
34+
35+
To apply a particular `MapperConfiguration` Type, supply it explicitly:
36+
37+
```C#
38+
Mapper.WhenMapping
39+
.UseConfigurations.From<ProductMappingConfiguration>();
40+
```
41+
42+
To apply all `MapperConfiguration` Types from an Assembly, supply a Type from that Assembly:
43+
44+
```C#
45+
Mapper.WhenMapping
46+
.UseConfigurations.FromAssemblyOf<Product>();
47+
```
48+
49+
To apply all `MapperConfiguration` Types from multiple Assemblies, supply the Assemblies:
50+
51+
```C#
52+
// Scan all Assemblies from the AppDomain:
53+
Mapper.WhenMapping
54+
.UseConfigurations.From(assembly1, assembly2, assembly3);
55+
56+
// Scan all the given Assemblies which match the filter -
57+
// Assembly scanning can be expensive, so this can be useful!
58+
Mapper.WhenMapping
59+
.UseConfigurations.From(
60+
GetLotsOfAssemblies(),
61+
assembly => assembly.FullName.Contains(nameof(MyNamespace)));
62+
```
63+
64+
To apply all `MapperConfiguration` Types from the Assemblies current loaded into the `AppDomain`, use:
65+
66+
```C#
67+
// Scan all Assemblies from the AppDomain:
68+
Mapper.WhenMapping
69+
.UseConfigurations.FromCurrentAppDomain();
70+
71+
// Scan all Assemblies from the AppDomain which match the filter -
72+
// Assembly scanning can be expensive, so this is advisable!
73+
Mapper.WhenMapping
74+
.UseConfigurations.FromCurrentAppDomain(
75+
assembly => assembly.FullName.Contains("MyCompanyName"));
76+
```
77+
78+
### Ordering Configurations
79+
80+
Calling `GetPlansFor<Source>().To<Target>()` caches the mapping function at the point you call it. If Types configured in the object graph are configured in more than one `MapperConfiguration`, you might need to define an order in which configuration classes are applied. Use:
81+
82+
```C#
83+
// Configure aspects of Parent -> Parent mapping, which includes
84+
// mapping Child -> Child. Automatically apply ChildMapperConfiguration,
85+
// then apply this configuration afterwards.
86+
[ApplyAfter(typeof(ChildMapperConfiguration))]
87+
public class ParentMapperConfiguration : MapperConfiguration
88+
{
89+
}
90+
91+
// Configure aspects of Child -> Child mapping:
92+
public class ChildMapperConfiguration : MapperConfiguration
93+
{
94+
}
95+
```
96+
97+
Chains of `ApplyAfter` attributes will be followed, with all configurations automatically applied in the defined order. Defining circular references between configuration types will throw a `MappingConfigurationException`.
98+
99+
### Accessing Services
100+
101+
[Configured Service Providers](Dependency-Injection) are available to `MapperConfiguration` classes. For example:
102+
103+
```C#
104+
// Get a Dependency Injection container:
105+
var diContainer = GetDiContainer();
106+
107+
// Register the container:
108+
Mapper.WhenMapping.UseServiceProvider(diContainer);
109+
110+
// Scan for MapperConfigurations:
111+
Mapper.WhenMapping
112+
.UseConfigurations.FromAssemblyOf<Product>();
113+
```
114+
115+
...the DI container and its registered services are now available to the `MapperConfiguration` class via the `GetService<TService>()` and `GetServiceProvider<TContainer>()` methods:
116+
117+
```C#
118+
public class MyMappingConfiguration : MapperConfiguration
119+
{
120+
protected override void Configure()
121+
{
122+
// Get a reference to the configured container:
123+
var diContainer = GetServiceProvider<IUnityContainer>();
124+
125+
// Get a reference to a configured ILogger - this just passes
126+
// the request to the container and returns the result:
127+
var logger = GetService<ILogger>();
128+
129+
// Create a new mapper for Product mapping:
130+
var productMapper = CreateNewMapper();
131+
132+
// Configure productMapper Product -> ProductDto mapping:
133+
productMapper.WhenMapping
134+
.From<ProductDto>()
135+
.To<Product>()
136+
.Map((dto, p) => dto.Spec)
137+
.To(p => p.Specification);
138+
139+
// Cache all Product -> ProductDto mapping plans:
140+
productMapper.GetPlansFor<Product>().To<ProductDto>();
141+
142+
// Create a new mapper for Order mapping:
143+
var orderMapper = CreateNewMapper();
144+
145+
// Configure orderMapper Order -> OrderDto create new mapping:
146+
orderMapper.WhenMapping
147+
.From<Order>()
148+
.ToANew<OrderDto>()
149+
.Map((o, dto) => o.Items.Sum(i => i.Cost))
150+
.To(dto => dto.TotalCost);
151+
152+
// Cache the Order -> OrderDto create new mapping plan:
153+
orderMapper.GetPlanFor<Order>().ToANew<OrderDto>();
154+
155+
// Register named IMapper instances with the container:
156+
diContainer.RegisterInstance("ProductMapper", productMapper);
157+
diContainer.RegisterInstance("OrderMapper", orderMapper);
158+
159+
logger.LogDebug("Product and Order mapping configured and registered");
160+
}
161+
}
162+
```

docs/Configuration.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Many aspects of mapping can be configured, but no up-front type registration is required - a mapping function is created and cached the first time two types are mapped.
2+
3+
Various configuration options are available:
4+
5+
- [Static](Static-vs-Instance-Mappers) configuration (`Mapper.WhenMapping`) configures a default, instance-scoped mapper behind the scenes. This configures the mapper used when you call `Mapper.Map(source)`
6+
7+
- [Instance](Static-vs-Instance-Mappers) configuration (`mapper.WhenMapping`) configures that particular instance
8+
9+
- [Inline](Inline-configuration) configuration combines the configuration of the mapper performing the mapping with the inline configuration you supply
10+
11+
- [Class](Configuration-Classes) configuration splits configuration up into dedicated configuration classes.
12+
13+
The same API is available in all four contexts.
14+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
By default, types are created using the 'greediest' public constructor - the one with the most parameters that have [matching](Member-Matching) source members. If there are no available constructors whose parameters can all be matched - and no parameterless constructor - the member for which the type would be created is ignored.
2+
3+
Constructor arguments can be configured by type or name, and constant values or expressions can be specified.
4+
5+
For example, to configure mapping these types:
6+
7+
```C#
8+
public class CustomerDto
9+
{
10+
public string CustomerNum { get; set; }
11+
public string Name { get; set; }
12+
}
13+
14+
public class Customer
15+
{
16+
public Customer(Guid customerId, string customerName)
17+
{
18+
}
19+
}
20+
```
21+
22+
...use:
23+
24+
```C#
25+
Mapper.WhenMapping
26+
.From<CustomerDto>() // Apply to CustomerDto mappings
27+
.ToANew<Customer>() // Apply to Customer creations
28+
.Map((dto, c) => dto.CustomerNum) // Map CustomerDto.CustomerNum
29+
.ToCtor<Guid>() // To Customer's Guid constructor param
30+
.And // Not done configuring yet...
31+
.Map((dto, c) => dto.Name) // Map CustomerDto.Name
32+
.ToCtor("customerName"); // To Customer's 'customerName' param
33+
```
34+
35+
...or, if inline configuration is preferred:
36+
37+
```C#
38+
// Source, target and mapping types are implicit from the mapping:
39+
Mapper
40+
.Map(customerDto).ToANew<Customer>(cfg => cfg
41+
.Map((dto, c) => dto.CustomerNum) // Map CustomerDto.CustomerNum
42+
.ToCtor<Guid>() // To Customer's Guid constructor param
43+
.And // Not done configuring yet...
44+
.Map((dto, c) => dto.Name) // Map CustomerDto.Name
45+
.ToCtor("customerName")); // To Customer's 'customerName' param
46+
```
47+
48+
In these examples the `string` `CustomerNum` is parsed and [converted](Type-Conversion) to the `Guid` `customerId` out of the box.
49+
50+
If configuring constructor parameters is awkward (perhaps because there's a lot of them), you can also [configure an object factory](Configuring-Object-Creation) for a particular object type.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
By default, an `Exception` thrown during a mapping is wrapped in a [`MappingException`](/agileobjects/AgileMapper/blob/master/AgileMapper/MappingException.cs) and rethrown. To configure a mapper to swallow exceptions and return null instead, use:
2+
3+
```C#
4+
Mapper.WhenMapping
5+
.SwallowAllExceptions();
6+
```
7+
8+
Alternatively, to have a mapper call a callback in the event of an exception use:
9+
10+
```C#
11+
Mapper.WhenMapping
12+
.PassExceptionsTo(ctx =>
13+
{
14+
Debug.Print(string.Format(
15+
"Error mapping from {0} to {1}: {2}",
16+
ctx.Source,
17+
ctx.Target,
18+
ctx.Exception));
19+
20+
throw ctx.Exception;
21+
});
22+
```
23+
24+
To only swallow exceptions thrown when mapping particular types, use:
25+
26+
```C#
27+
Mapper.WhenMapping
28+
.From<PersonViewModel>() // Apply to PersonViewModel mappings (optional)
29+
.To<Person>() // Apply to Person creation, updates and merges
30+
.SwallowAllExceptions();
31+
```
32+
33+
...and to have a callback called for a particular type, use:
34+
35+
```C#
36+
Mapper.WhenMapping
37+
.To<Person>()
38+
.PassExceptionsTo(ctx =>
39+
Debug.Log(new PersonException(ctx.Source.Id, ctx.Exception)));
40+
```

0 commit comments

Comments
 (0)