-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Importing documentation from the Wiki (#106)
- Loading branch information
1 parent
6c32f81
commit c2ff88a
Showing
46 changed files
with
2,773 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
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: | ||
|
||
```C# | ||
Mapper.WhenMapping.LookForDerivedTypesIn( | ||
typeof(DerivedType1).Assembly, | ||
typeof(DerivedType2).Assembly); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
Mappers can be cloned, to enable 'derived' mappers to inherit, add to and override a 'root' configuration: | ||
|
||
``` C# | ||
var baseMapper = Mapper.CreateNew(); | ||
|
||
// Setup the base configuration: | ||
baseMapper.WhenMapping | ||
.From<Order>() | ||
.To<OrderDto>() | ||
.Map((o, dto) => o.ProdId) | ||
.To(dto => dto.ProductId) | ||
.And | ||
.Map((o, dto) => o.Id) | ||
.To(dto => dto.OrderNumber); | ||
|
||
// Clone a new mapper for mapping UK orders: | ||
var ukOrderMapper = baseMapper.CloneSelf(); | ||
|
||
// Setup UK-specific configuration: | ||
ukOrderMapper.WhenMapping | ||
.To<OrderDto>() | ||
.Map(ctx => DateTime.UtcNow.AddHours(1)) | ||
.To(dto => dto.DateCreated); | ||
|
||
// Clone a new mapper for mapping US orders: | ||
var usOrderMapper = baseMapper.CloneSelf(); | ||
|
||
// Setup US-specific configuration: | ||
usOrderMapper.WhenMapping | ||
.To<OrderDto>() | ||
.Map(ctx => DateTime.UtcNow.AddHours(-4)) | ||
.To(dto => dto.DateCreated); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
The following collection types are supported by default: | ||
|
||
- Arrays | ||
- `IEnumerable` | ||
- `IEnumerable<T>` | ||
- `ICollection` | ||
- `ICollection<T>` | ||
- `List<T>` | ||
- `IList<T>` | ||
- `ReadOnlyCollection<T>` | ||
- `IReadOnlyCollection<T>` | ||
- `Collection<T>` | ||
- `HashSet<T>` | ||
- `ISet<T>` | ||
- [`Dictionary<string, T>`](Dictionary-Mapping) | ||
- [`IDictionary<string, T>`](Dictionary-Mapping) | ||
|
||
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. | ||
|
||
[Updates](Performing-Updates) and [merges](Performing-Merges) of collections of identifiable objects (i.e. Entities) are performed in an id-aware manner. | ||
|
||
## Null Source Collections | ||
|
||
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: | ||
|
||
```C# | ||
// Map null-source collections to null for all source | ||
// and target types: | ||
Mapper.WhenMapping.MapNullCollectionsToNull(); | ||
|
||
// Map null collections to null only when mapping from | ||
// Order to OrderDto: | ||
Mapper.WhenMapping | ||
.From<Order>() | ||
.To<OrderDto>() | ||
.MapNullCollectionsToNull(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
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). | ||
|
||
`MapperConfiguration` provides the same configuration API, and exposes services you inject. | ||
|
||
### Configuration and Caching | ||
|
||
Mapper configuration is set up by implementing the abstract `Configure` method: | ||
|
||
```C# | ||
public class ProductMappingConfiguration : MapperConfiguration | ||
{ | ||
protected override void Configure() | ||
{ | ||
// Configure default Mapper ProductDto -> Productmapping: | ||
WhenMapping | ||
.From<ProductDto>() | ||
.To<Product>() | ||
.Map((dto, p) => dto.Spec) | ||
.To(p => p.Specification) | ||
.And | ||
.Ignore(p => p.Price, p => p.CatNum); | ||
|
||
// Cache all Product -> ProductDto mapping plans: | ||
GetPlansFor<Product>().To<ProductDto>(); | ||
} | ||
} | ||
``` | ||
|
||
In this example the default mapper is configured - the one used via the [static](Static-vs-Instance-Mappers) Mapper API. | ||
|
||
### Applying Configurations | ||
|
||
`MapperConfiguration` classes can be discovered and applied in various ways. | ||
|
||
To apply a particular `MapperConfiguration` Type, supply it explicitly: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.UseConfigurations.From<ProductMappingConfiguration>(); | ||
``` | ||
|
||
To apply all `MapperConfiguration` Types from an Assembly, supply a Type from that Assembly: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.UseConfigurations.FromAssemblyOf<Product>(); | ||
``` | ||
|
||
To apply all `MapperConfiguration` Types from multiple Assemblies, supply the Assemblies: | ||
|
||
```C# | ||
// Scan all Assemblies from the AppDomain: | ||
Mapper.WhenMapping | ||
.UseConfigurations.From(assembly1, assembly2, assembly3); | ||
|
||
// Scan all the given Assemblies which match the filter - | ||
// Assembly scanning can be expensive, so this can be useful! | ||
Mapper.WhenMapping | ||
.UseConfigurations.From( | ||
GetLotsOfAssemblies(), | ||
assembly => assembly.FullName.Contains(nameof(MyNamespace))); | ||
``` | ||
|
||
To apply all `MapperConfiguration` Types from the Assemblies current loaded into the `AppDomain`, use: | ||
|
||
```C# | ||
// Scan all Assemblies from the AppDomain: | ||
Mapper.WhenMapping | ||
.UseConfigurations.FromCurrentAppDomain(); | ||
|
||
// Scan all Assemblies from the AppDomain which match the filter - | ||
// Assembly scanning can be expensive, so this is advisable! | ||
Mapper.WhenMapping | ||
.UseConfigurations.FromCurrentAppDomain( | ||
assembly => assembly.FullName.Contains("MyCompanyName")); | ||
``` | ||
|
||
### Ordering Configurations | ||
|
||
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: | ||
|
||
```C# | ||
// Configure aspects of Parent -> Parent mapping, which includes | ||
// mapping Child -> Child. Automatically apply ChildMapperConfiguration, | ||
// then apply this configuration afterwards. | ||
[ApplyAfter(typeof(ChildMapperConfiguration))] | ||
public class ParentMapperConfiguration : MapperConfiguration | ||
{ | ||
} | ||
|
||
// Configure aspects of Child -> Child mapping: | ||
public class ChildMapperConfiguration : MapperConfiguration | ||
{ | ||
} | ||
``` | ||
|
||
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`. | ||
|
||
### Accessing Services | ||
|
||
[Configured Service Providers](Dependency-Injection) are available to `MapperConfiguration` classes. For example: | ||
|
||
```C# | ||
// Get a Dependency Injection container: | ||
var diContainer = GetDiContainer(); | ||
|
||
// Register the container: | ||
Mapper.WhenMapping.UseServiceProvider(diContainer); | ||
|
||
// Scan for MapperConfigurations: | ||
Mapper.WhenMapping | ||
.UseConfigurations.FromAssemblyOf<Product>(); | ||
``` | ||
|
||
...the DI container and its registered services are now available to the `MapperConfiguration` class via the `GetService<TService>()` and `GetServiceProvider<TContainer>()` methods: | ||
|
||
```C# | ||
public class MyMappingConfiguration : MapperConfiguration | ||
{ | ||
protected override void Configure() | ||
{ | ||
// Get a reference to the configured container: | ||
var diContainer = GetServiceProvider<IUnityContainer>(); | ||
|
||
// Get a reference to a configured ILogger - this just passes | ||
// the request to the container and returns the result: | ||
var logger = GetService<ILogger>(); | ||
|
||
// Create a new mapper for Product mapping: | ||
var productMapper = CreateNewMapper(); | ||
|
||
// Configure productMapper Product -> ProductDto mapping: | ||
productMapper.WhenMapping | ||
.From<ProductDto>() | ||
.To<Product>() | ||
.Map((dto, p) => dto.Spec) | ||
.To(p => p.Specification); | ||
|
||
// Cache all Product -> ProductDto mapping plans: | ||
productMapper.GetPlansFor<Product>().To<ProductDto>(); | ||
|
||
// Create a new mapper for Order mapping: | ||
var orderMapper = CreateNewMapper(); | ||
|
||
// Configure orderMapper Order -> OrderDto create new mapping: | ||
orderMapper.WhenMapping | ||
.From<Order>() | ||
.ToANew<OrderDto>() | ||
.Map((o, dto) => o.Items.Sum(i => i.Cost)) | ||
.To(dto => dto.TotalCost); | ||
|
||
// Cache the Order -> OrderDto create new mapping plan: | ||
orderMapper.GetPlanFor<Order>().ToANew<OrderDto>(); | ||
|
||
// Register named IMapper instances with the container: | ||
diContainer.RegisterInstance("ProductMapper", productMapper); | ||
diContainer.RegisterInstance("OrderMapper", orderMapper); | ||
|
||
logger.LogDebug("Product and Order mapping configured and registered"); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
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. | ||
|
||
Various configuration options are available: | ||
|
||
- [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)` | ||
|
||
- [Instance](Static-vs-Instance-Mappers) configuration (`mapper.WhenMapping`) configures that particular instance | ||
|
||
- [Inline](Inline-configuration) configuration combines the configuration of the mapper performing the mapping with the inline configuration you supply | ||
|
||
- [Class](Configuration-Classes) configuration splits configuration up into dedicated configuration classes. | ||
|
||
The same API is available in all four contexts. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
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. | ||
|
||
Constructor arguments can be configured by type or name, and constant values or expressions can be specified. | ||
|
||
For example, to configure mapping these types: | ||
|
||
```C# | ||
public class CustomerDto | ||
{ | ||
public string CustomerNum { get; set; } | ||
public string Name { get; set; } | ||
} | ||
|
||
public class Customer | ||
{ | ||
public Customer(Guid customerId, string customerName) | ||
{ | ||
} | ||
} | ||
``` | ||
|
||
...use: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.From<CustomerDto>() // Apply to CustomerDto mappings | ||
.ToANew<Customer>() // Apply to Customer creations | ||
.Map((dto, c) => dto.CustomerNum) // Map CustomerDto.CustomerNum | ||
.ToCtor<Guid>() // To Customer's Guid constructor param | ||
.And // Not done configuring yet... | ||
.Map((dto, c) => dto.Name) // Map CustomerDto.Name | ||
.ToCtor("customerName"); // To Customer's 'customerName' param | ||
``` | ||
|
||
...or, if inline configuration is preferred: | ||
|
||
```C# | ||
// Source, target and mapping types are implicit from the mapping: | ||
Mapper | ||
.Map(customerDto).ToANew<Customer>(cfg => cfg | ||
.Map((dto, c) => dto.CustomerNum) // Map CustomerDto.CustomerNum | ||
.ToCtor<Guid>() // To Customer's Guid constructor param | ||
.And // Not done configuring yet... | ||
.Map((dto, c) => dto.Name) // Map CustomerDto.Name | ||
.ToCtor("customerName")); // To Customer's 'customerName' param | ||
``` | ||
|
||
In these examples the `string` `CustomerNum` is parsed and [converted](Type-Conversion) to the `Guid` `customerId` out of the box. | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
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: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.SwallowAllExceptions(); | ||
``` | ||
|
||
Alternatively, to have a mapper call a callback in the event of an exception use: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.PassExceptionsTo(ctx => | ||
{ | ||
Debug.Print(string.Format( | ||
"Error mapping from {0} to {1}: {2}", | ||
ctx.Source, | ||
ctx.Target, | ||
ctx.Exception)); | ||
|
||
throw ctx.Exception; | ||
}); | ||
``` | ||
|
||
To only swallow exceptions thrown when mapping particular types, use: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.From<PersonViewModel>() // Apply to PersonViewModel mappings (optional) | ||
.To<Person>() // Apply to Person creation, updates and merges | ||
.SwallowAllExceptions(); | ||
``` | ||
|
||
...and to have a callback called for a particular type, use: | ||
|
||
```C# | ||
Mapper.WhenMapping | ||
.To<Person>() | ||
.PassExceptionsTo(ctx => | ||
Debug.Log(new PersonException(ctx.Source.Id, ctx.Exception))); | ||
``` |
Oops, something went wrong.