Skip to content

Commit

Permalink
Merge pull request #3596 from AutoMapper/upgrade_guide
Browse files Browse the repository at this point in the history
11.0 upgrade guide
  • Loading branch information
jbogard committed Jan 5, 2022
2 parents bdc0120 + fbffc7a commit ad5a6f8
Show file tree
Hide file tree
Showing 298 changed files with 2,305 additions and 2,253 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Expand Up @@ -11,7 +11,7 @@ jobs:
build:
strategy:
fail-fast: false
runs-on: windows-latest
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Expand Up @@ -8,7 +8,7 @@ jobs:
build:
strategy:
fail-fast: false
runs-on: windows-latest
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
2 changes: 2 additions & 0 deletions AutoMapper.sln
Expand Up @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
CONTRIBUTING.md = CONTRIBUTING.md
Directory.Build.props = Directory.Build.props
ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md
nuget.config = nuget.config
Push.ps1 = Push.ps1
README.md = README.md
.github\workflows\release.yml = .github\workflows\release.yml
Expand All @@ -36,6 +37,7 @@ Global
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B8051389-CB47-46FB-B234-9D49506704AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8051389-CB47-46FB-B234-9D49506704AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8051389-CB47-46FB-B234-9D49506704AA}.Debug|ARM.ActiveCfg = Debug|Any CPU
{B8051389-CB47-46FB-B234-9D49506704AA}.Debug|x64.ActiveCfg = Debug|Any CPU
{B8051389-CB47-46FB-B234-9D49506704AA}.Debug|x86.ActiveCfg = Debug|Any CPU
Expand Down
2 changes: 1 addition & 1 deletion ISSUE_TEMPLATE.md
@@ -1,7 +1,7 @@
<!--
If you're new to AutoMapper, please ask a question on StackOverflow first and come back here if the people there consider it a bug.
If you've just upgraded to 10.0, please read the upgrade guide first (https://docs.automapper.org/en/latest/10.0-Upgrade-Guide.html).
If you've just upgraded to 11.0, please read the upgrade guide first (https://docs.automapper.org/en/latest/11.0-Upgrade-Guide.html).
Try the [MyGet](https://docs.automapper.org/en/latest/The-MyGet-build.html) build.
Try to provide [a minimal, complete, and verifiable example](https://stackoverflow.com/help/mcve), preferably a [gist](https://gist.github.com/lbargaoanu/9c7233441c3a3413cc2b9b9ebb5964a9) that we can execute and see fail. [Here](https://gist.github.com/lbargaoanu/0cbc531306223f7ffc5468becf2642d6) is an example for ProjectTo.
For feature requests, just clear out the below.
Expand Down
6 changes: 3 additions & 3 deletions README.md
@@ -1,8 +1,8 @@
<img src="https://s3.amazonaws.com/automapper/logo.png" alt="AutoMapper">
![AutoMapper](https://s3.amazonaws.com/automapper/logo.png)

[![CI](https://github.com/automapper/automapper/workflows/CI/badge.svg)](https://github.com/AutoMapper/AutoMapper/actions?query=workflow%3ACI)
[![NuGet](http://img.shields.io/nuget/v/AutoMapper.svg)](https://www.nuget.org/packages/AutoMapper/)
[![MyGet (dev)](https://img.shields.io/myget/automapperdev/v/AutoMapper.svg)](https://myget.org/feed/automapperdev/package/nuget/AutoMapper)
[![NuGet](http://img.shields.io/nuget/vpre/AutoMapper.svg?label=NuGet)](https://www.nuget.org/packages/AutoMapper/)
[![MyGet (dev)](https://img.shields.io/myget/automapperdev/vpre/AutoMapper.svg?label=MyGet)](https://myget.org/feed/automapperdev/package/nuget/AutoMapper)

### What is AutoMapper?

Expand Down
17 changes: 16 additions & 1 deletion docs/11.0-Upgrade-Guide.md
Expand Up @@ -2,6 +2,13 @@

[Release notes](https://github.com/AutoMapper/AutoMapper/releases/tag/v11.0.0).

## AutoMapper now targets .Net Standard 2.1 and doesn't work on .Net Framework

## `ForAllMaps`, `ForAllPropertyMaps`, `Advanced` and other "missing" APIs

Some APIs were hidden for normal usage. To light them up, you need to add an `using` for `AutoMapper.Internal` and call the [`Internal` extension method](https://github.com/AutoMapper/AutoMapper/blob/9f2f16067ab201a5a8b9bc982f3a37e8790da7a0/src/AutoMapper/Internal/InternalApi.cs#L15) on the configuration object.
Most users don't need these advanced methods. Some expose internals and are not subject to the usual semantic versioning rules. To avoid such tight coupling to AutoMapper, you should try to stick to the public API.

## Mapping _into_ existing collections

When calling `Map` with an existing readonly collection, such as `IEnumerable<>`, the setter will be used to replace it. If you actually have to map _into_ that collection, you need to change its type to a writable
Expand All @@ -21,7 +28,7 @@ That was misleading for a lot of people. You can opt-in per map with `AsProxy` (

## `MapToAttribute` and `IgnoreMapAttribute` were removed

These were older attributes, unrelated to the newer attributes API. You can switch to the fluent API or implement the attributes in your own code. Check the tests for an example.
These were older attributes, unrelated to the newer attributes API. You can switch to the fluent API or implement the attributes in your own code. Check the tests for sample code ([here](https://github.com/AutoMapper/AutoMapper/search?q=MapToAttribute) and [here](https://github.com/AutoMapper/AutoMapper/search?q=IgnoreMapAttribute)).

## Global pre and postfixes are now applied in all maps

Expand All @@ -30,3 +37,11 @@ They used to be applied only in the global configuration, now they are applied i
## `ForAllOtherMembers` was removed

That was used to disable mapping by convention, not something we want to support. When only used for validation, it can be replaced with `MemberList.None`.

## C# Indexers (`Item` property)

These used to be ignored by default, but that's expensive and most types don't have them. So you have to explicitly ignore them. Globally, with `ShouldMapProperty` or `GlobalIgnores`, or per member.

## Configuration performance

While you should get improvements without code changes, you can do even better. Definitely use `CreateProjection` with `ProjectTo`. If you're an advanced user and you're confident in your test coverage, you can [disable](https://gist.github.com/lbargaoanu/9948bf66d452ba6b816252f9965143ee) any features you don't need. Needless to say, do measure to see if these help in your particular case.
2 changes: 1 addition & 1 deletion docs/Configuration-validation.md
Expand Up @@ -55,4 +55,4 @@ To skip validation altogether for this map, use `MemberList.None`.

## Custom validations

You can add custom validations through an extension point. See [here](https://github.com/AutoMapper/AutoMapper/blob/7a00700de61cd3234b6a6eb4bf0f6bbe402369b1/src/UnitTests/ConfigurationValidation.cs#L30).
You can add custom validations through an extension point. See [here](https://github.com/AutoMapper/AutoMapper/blob/bdc0120497d192a2741183415543f6119f50a982/src/UnitTests/CustomValidations.cs#L42).
11 changes: 10 additions & 1 deletion docs/Configuration.md
Expand Up @@ -215,4 +215,13 @@ var configuration = new MapperConfiguration(cfg => {});
configuration.CompileMappings();
```

For a few hundred mappings, this may take a couple of seconds.
For a few hundred mappings, this may take a couple of seconds. If it's a lot more than that, you probably have some really big execution plans.

### Long compilation times

Compilation times increase with the size of the execution plan and that depends on the number of properties and their complexity. Ideally, you would fix your model so you have many small DTOs, each for a particular use case. But you can also decrease the size of the execution plan without changing your classes.

You can set `MapAtRuntime` per member or `MaxExecutionPlanDepth` globally (the default is one, set it to zero).

These will reduce the size of the execution plan by replacing the execution plan for a child object with a method call. The compilation will be faster, but the mapping itself might be slower. Search the repo for more details and use a profiler to better understand the effect.
Avoiding `PreserveReferences` and `MaxDepth` also helps.
6 changes: 1 addition & 5 deletions docs/Custom-type-converters.md
Expand Up @@ -85,8 +85,4 @@ public class TypeTypeConverter : ITypeConverter<string, Type>

In the first mapping, from string to Int32, we simply use the built-in Convert.ToInt32 function (supplied as a method group). The next two use custom ITypeConverter implementations.

The real power of custom type converters is that they are used any time AutoMapper finds the source/destination pairs on any mapped types. We can build a set of custom type converters, on top of which other mapping configurations use, without needing any extra configuration. In the above example, we never have to specify the string/int conversion again. Where as [Custom Value Resolvers](Custom-value-resolvers.html) have to be configured at a type member level, custom type converters are global in scope.

## System Type Converters

The .NET Framework also supports the concepts of type converters, through the [TypeConverter](http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx) class. AutoMapper supports these types of type converters, in configuration checking and mapping, without the need for any manual configuration. AutoMapper uses the [TypeDescriptor.GetConverter](http://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.getconverter.aspx) method for determining if the source/destination type pair can be mapped.
The real power of custom type converters is that they are used any time AutoMapper finds the source/destination pairs on any mapped types. We can build a set of custom type converters, on top of which other mapping configurations use, without needing any extra configuration. In the above example, we never have to specify the string/int conversion again. Where as [Custom Value Resolvers](Custom-value-resolvers.html) have to be configured at a type member level, custom type converters are global in scope.
177 changes: 3 additions & 174 deletions docs/Dependency-injection.md
@@ -1,3 +1,5 @@
# Dependency Injection

## Examples

### ASP.NET Core
Expand Down Expand Up @@ -28,181 +30,8 @@ There is a third-party [NuGet package](https://www.nuget.org/packages/AutoMapper

Also, check [this blog](https://dotnetfalcon.com/autofac-support-for-automapper/).

### Ninject

For those using Ninject here is an example of a Ninject module for AutoMapper

```c#
public class AutoMapperModule : NinjectModule
{
public override void Load()
{
Bind<IValueResolver<SourceEntity, DestModel, bool>>().To<MyResolver>();

var mapperConfiguration = CreateConfiguration();
Bind<MapperConfiguration>().ToConstant(mapperConfiguration).InSingletonScope();

// This teaches Ninject how to create automapper instances say if for instance
// MyResolver has a constructor with a parameter that needs to be injected
Bind<IMapper>().ToMethod(ctx =>
new Mapper(mapperConfiguration, type => ctx.Kernel.Get(type)));
}

private MapperConfiguration CreateConfiguration()
{
var config = new MapperConfiguration(cfg =>
{
// Add all profiles in current assembly
cfg.AddMaps(GetType().Assembly);
});

return config;
}
}
```

### Simple Injector

The workflow is as follows:

1) Register your types via MyRegistrar.Register
2) The MapperProvider allows you to directly inject an instance of IMapper into your other classes
3) SomeProfile resolves a value using PropertyThatDependsOnIocValueResolver
4) PropertyThatDependsOnIocValueResolver has IService injected into it, which is then able to be used

The ValueResolver has access to IService because we register our container via MapperConfigurationExpression.ConstructServicesUsing

```c#
public class MyRegistrar
{
public void Register(Container container)
{
// Injectable service
container.RegisterSingleton<IService, SomeService>();

// Automapper
container.RegisterSingleton(() => GetMapper(container));
}

private AutoMapper.IMapper GetMapper(Container container)
{
var mp = container.GetInstance<MapperProvider>();
return mp.GetMapper();
}
}

public class MapperProvider
{
private readonly Container _container;

public MapperProvider(Container container)
{
_container = container;
}

public IMapper GetMapper()
{
var mce = new MapperConfigurationExpression();
mce.ConstructServicesUsing(_container.GetInstance);

mce.AddMaps(typeof(SomeProfile).Assembly);

var mc = new MapperConfiguration(mce);
mc.AssertConfigurationIsValid();

IMapper m = new Mapper(mc, t => _container.GetInstance(t));

return m;
}
}

public class SomeProfile : Profile
{
public SomeProfile()
{
var map = CreateMap<MySourceType, MyDestinationType>();
map.ForMember(d => d.PropertyThatDependsOnIoc, opt => opt.MapFrom<PropertyThatDependsOnIocValueResolver>());
}
}

public class PropertyThatDependsOnIocValueResolver : IValueResolver<MySourceType, object, int>
{
private readonly IService _service;

public PropertyThatDependsOnIocValueResolver(IService service)
{
_service = service;
}
### [Other DI engines](https://github.com/AutoMapper/AutoMapper/wiki/DI-examples)

int IValueResolver<MySourceType, object, int>.Resolve(MySourceType source, object destination, int destMember, ResolutionContext context)
{
return _service.MyMethod(source);
}
}
```

### Castle Windsor

For those using Castle Windsor here is an example of an installer for AutoMapper

```c#
public class AutoMapperInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// Register all mapper profiles
container.Register(
Classes.FromAssemblyInThisApplication(GetType().Assembly)
.BasedOn<Profile>().WithServiceBase());

// Register IConfigurationProvider with all registered profiles
container.Register(Component.For<IConfigurationProvider>().UsingFactoryMethod(kernel =>
{
return new MapperConfiguration(configuration =>
{
kernel.ResolveAll<Profile>().ToList().ForEach(configuration.AddProfile);
});
}).LifestyleSingleton());

// Register IMapper with registered IConfigurationProvider
container.Register(
Component.For<IMapper>().UsingFactoryMethod(kernel =>
new Mapper(kernel.Resolve<IConfigurationProvider>(), kernel.Resolve)));
}
}
```

### Catel.IoC

For those using Catel.IoC here is how you register AutoMapper. First define the configuration using [profiles](Configuration.html#profile-instances). And then you let AutoMapper know in what assemblies those profiles are defined by registering AutoMapper in the ServiceLocator at startup:
```c#
ServiceLocator.Default.RegisterInstance(typeof(IMapper), new Mapper(CreateConfiguration()));
```

Configuration Creation Method:
```c#
public static MapperConfiguration CreateConfiguration()
{
var config = new MapperConfiguration(cfg =>
{
// Add all profiles in current assembly
cfg.AddMaps(GetType().Assembly);
});

return config;
}
```

Now you can inject AutoMapper at runtime into your services/controllers:
```c#
public class EmployeesController {
private readonly IMapper _mapper;

public EmployeesController(IMapper mapper) => _mapper = mapper;

// use _mapper.Map or _mapper.ProjectTo
}
```
## Low level API-s

AutoMapper supports the ability to construct [Custom Value Resolvers](Custom-value-resolvers.html), [Custom Type Converters](Custom-type-converters.html), and [Value Converters](Value-converters.html) using static service location:
Expand Down
2 changes: 1 addition & 1 deletion docs/Enum-Mapping.md
Expand Up @@ -43,7 +43,7 @@ internal class YourProfile : Profile

### Default Convention

The package [AutoMapper.Extensions.EnumMapping](https://www.nuget.org/packages/AutoMapper.Extensions.EnumMapping/) will map all values from Source type to Destination type if both enum types have the same value (or by name or by value). All Source enum values which have no Source equivalent, will throw an exception if EnumMappingValidation is enabled.
The package [AutoMapper.Extensions.EnumMapping](https://www.nuget.org/packages/AutoMapper.Extensions.EnumMapping/) will map all values from Source type to Destination type if both enum types have the same value (or by name or by value). All Source enum values which have no Target equivalent, will throw an exception if EnumMappingValidation is enabled.

### ReverseMap Convention

Expand Down
2 changes: 2 additions & 0 deletions docs/Flattening.md
Expand Up @@ -149,6 +149,8 @@ So this allows you to reuse the configuration in the existing maps for the child

The order of the parameters in the `IncludeMembers` call is relevant. When mapping a destination member, the first match wins, starting with the source object itself and then with the included child objects in the order you specified. So in the example above, `Name` is mapped from the source object itself and `Description` from `InnerSource` because it's the first match.

Note that this matching is static, it happens at configuration time, not at `Map` time, and the runtime types of the child objects are not considered.

IncludeMembers integrates with `ReverseMap`. An included member will be reversed to
```c#
ForPath(destination => destination.IncludedMember, member => member.MapFrom(source => source))
Expand Down
2 changes: 1 addition & 1 deletion docs/Getting-started.md
Expand Up @@ -14,7 +14,7 @@ First, you need both a source and destination type to work with. The destinatio

AutoMapper will ignore null reference exceptions when mapping your source to your target. This is by design. If you don't like this approach, you can combine AutoMapper's approach with [custom value resolvers](Custom-value-resolvers.html) if needed.

Once you have your types you can create a map for the two types using a `MapperConfiguration` and CreateMap. You only need one `MapperConfiguration` instance typically per AppDomain and should be instantiated during startup. More examples of initial setup see in [Setup](Setup.html).
Once you have your types you can create a map for the two types using a `MapperConfiguration` and CreateMap. You only need one `MapperConfiguration` instance typically per AppDomain and should be instantiated during startup. More examples of initial setup can be seen in [Setup](Setup.html).

```c#
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
Expand Down

0 comments on commit ad5a6f8

Please sign in to comment.