Skip to content

Commit

Permalink
Merge pull request #4280 from AutoMapper/basic_include
Browse files Browse the repository at this point in the history
ProjectTo runtime polymorphic mapping with Include/IncludeBase
  • Loading branch information
jbogard committed Feb 5, 2024
2 parents 104a192 + 99be09f commit f937b0f
Show file tree
Hide file tree
Showing 73 changed files with 5,382 additions and 367 deletions.
2 changes: 1 addition & 1 deletion AutoMapper.sln
Expand Up @@ -26,7 +26,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.UnitTests", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.IntegrationTests", "src\IntegrationTests\AutoMapper.IntegrationTests.csproj", "{24B47F4C-0035-4F29-AAD9-4C47E1AAD98E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.DI.Tests", "src\AutoMapper.Extensions.Microsoft.DependencyInjection.Tests\AutoMapper.DI.Tests.csproj", "{BEBD620A-8BAA-463F-BE0F-8319AD3C1644}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMapper.DI.Tests", "src\AutoMapper.DI.Tests\AutoMapper.DI.Tests.csproj", "{BEBD620A-8BAA-463F-BE0F-8319AD3C1644}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "src\TestApp\TestApp.csproj", "{35CED3AE-B825-4703-992D-A58B5BE646DC}"
EndProject
Expand Down
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -10,7 +10,6 @@ AutoMapper is a simple little library built to solve a deceptively complex probl

This is the main repository for AutoMapper, but there's more:

* [Microsoft DI Extensions](https://github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection)
* [Collection Extensions](https://github.com/AutoMapper/AutoMapper.Collection)
* [Expression Mapping](https://github.com/AutoMapper/AutoMapper.Extensions.ExpressionMapping)
* [EF6 Extensions](https://github.com/AutoMapper/AutoMapper.EF6)
Expand Down
14 changes: 13 additions & 1 deletion docs/13.0-Upgrade-Guide.md
Expand Up @@ -12,4 +12,16 @@ Besides the build-time impact, there is also a behaviour change. Non-generic `Ma

## `AllowAdditiveTypeMapCreation` was removed

Be sure to call `CreateMap` once for a source type, destination type pair. If you want to reuse configuration, use mapping inheritance.
Be sure to call `CreateMap` once for a source type, destination type pair. If you want to reuse configuration, use mapping inheritance.

## ProjectTo runtime polymorphic mapping with Include/IncludeBase

We consider this an off the beaten path feature and we don't expose it through `CreateProjection`. You can use [an extension method](https://github.com/AutoMapper/AutoMapper/search?l=C%23&q=Advanced) or `CreateMap`.

## `Context.State` similar to `Context.Items`

The same pattern the framework uses to pass state to delegates. Note that `State` and `Items` are mutually exclusive per `Map` call.

## Custom Equals/GetHashCode for source objects

To avoid broken implementations, we no longer call those when checking for identical source objects, we hard code to checking object references.
10 changes: 4 additions & 6 deletions docs/Before-and-after-map-actions.md
Expand Up @@ -42,10 +42,10 @@ var configuration = new MapperConfiguration(cfg => {
});
```

### Asp.Net Core and `AutoMapper.Extensions.Microsoft.DependencyInjection`
If you are using Asp.Net Core and the `AutoMapper.Extensions.Microsoft.DependencyInjection` package, this is also a good way of using Dependency Injection. You can't inject dependencies into `Profile` classes, but you can do it in `IMappingAction` implementations.
### Dependency Injection
You can't inject dependencies into `Profile` classes, but you can do it in `IMappingAction` implementations.

The following example shows how to connect an `IMappingAction` accessing the current `HttpContext` to a `Profile` after map action, leveraging Dependency Injection:
The following example shows how to connect an `IMappingAction` accessing the current `HttpContext` to a `Profile` after map action, leveraging dependency injection:

``` csharp
public class SetTraceIdentifierAction : IMappingAction<SomeModel, SomeOtherModel>
Expand Down Expand Up @@ -84,6 +84,4 @@ public class Startup
}
//..
}
```

*See `AutoMapper.Extensions.Microsoft.DependencyInjection` for more info.*
```
1 change: 1 addition & 0 deletions docs/Custom-value-resolvers.md
Expand Up @@ -127,6 +127,7 @@ This is how to setup the mapping for this custom resolver
cfg.CreateMap<Source, Dest>()
.ForMember(dest => dest.Foo, opt => opt.MapFrom((src, dest, destMember, context) => context.Items["Foo"]));
```
Starting with version 13.0, you can use `context.State` instead, in a similar way. Note that `State` and `Items` are mutually exclusive per `Map` call.

### ForPath

Expand Down
4 changes: 2 additions & 2 deletions docs/Queryable-Extensions.md
Expand Up @@ -227,6 +227,7 @@ Not all mapping options can be supported, as the expression generated must be in
* NullSubstitute
* Value transformers
* IncludeMembers
* Runtime polymorphic mapping with Include/IncludeBase

Not supported:
* Condition
Expand All @@ -237,6 +238,5 @@ Not supported:
* Custom resolvers
* Custom type converters
* ForPath
* Value converters
* Runtime polymorphic mapping with Include/IncludeBase
* Value converters
* **Any calculated property on your domain object**
1 change: 1 addition & 0 deletions docs/index.rst
Expand Up @@ -68,6 +68,7 @@ New to AutoMapper? Check out the :doc:`Getting-started` page first.
:caption: Upgrading

API-Changes
13.0-Upgrade-Guide
12.0-Upgrade-Guide
11.0-Upgrade-Guide
10.0-Upgrade-Guide
Expand Down
File renamed without changes.
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFramework>net8.0</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>AutoMapper.Extensions.Microsoft.DependencyInjection.Tests</AssemblyName>
<PackageId>AutoMapper.Extensions.Microsoft.DependencyInjection.Tests</PackageId>
Expand All @@ -16,11 +16,11 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="All" />
<PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4" PrivateAssets="All" />
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit" Version="2.6.2" />
</ItemGroup>

</Project>
File renamed without changes.
File renamed without changes.
File renamed without changes.
29 changes: 27 additions & 2 deletions src/AutoMapper/ApiCompatBaseline.txt
@@ -1,9 +1,29 @@
Compat issues with assembly AutoMapper:
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.AutoMapAttribute' changed from '[AttributeUsageAttribute(1036, AllowMultiple=true)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple=true)]' in the implementation.
InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMapper.IMappingOperationOptions.State' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Object AutoMapper.IMappingOperationOptions.State.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IMappingOperationOptions.State.set(System.Object)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection<AutoMapper.PropertyMapAction> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Collections.Generic.IReadOnlyCollection<System.Action<AutoMapper.PropertyMap, AutoMapper.IMemberConfigurationExpression>> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyCollection<System.Action<AutoMapper.PropertyMap, AutoMapper.IMemberConfigurationExpression>> AutoMapper.IProfileConfiguration.AllPropertyMapActions.get()' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.Configuration.ICtorParamConfigurationExpression.ExplicitExpansion()' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IProjectionMemberConfiguration<TSource, TDestination, TMember>.ExplicitExpansion()' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void AutoMapper.IProjectionMemberConfiguration<TSource, TDestination, TMember>.ExplicitExpansion()' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.IProjectionMemberConfiguration<TSource, TDestination, TMember>.ExplicitExpansion(System.Boolean)' is present in the implementation but not in the contract.
CannotSealType : Type 'AutoMapper.Mapper' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotSealType : Type 'AutoMapper.MapperConfiguration' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotSealType : Type 'AutoMapper.MapperConfigurationExpression' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotSealType : Type 'AutoMapper.MappingOperationOptions<TSource, TDestination>' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
MembersMustExist : Member 'protected void AutoMapper.MappingOperationOptions<TSource, TDestination>.AfterMapAction.set(System.Action<TSource, TDestination>)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void AutoMapper.MappingOperationOptions<TSource, TDestination>.BeforeMapAction.set(System.Action<TSource, TDestination>)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void AutoMapper.Configuration.ICtorParamConfigurationExpression.ExplicitExpansion(System.Boolean)' is present in the implementation but not in the contract.
CannotSealType : Type 'AutoMapper.Configuration.MappingExpression' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotMakeMemberNonVirtual : Member 'protected void AutoMapper.Configuration.MappingExpression.IgnoreDestinationMember(System.Reflection.MemberInfo, System.Boolean)' is non-virtual in the implementation but is virtual in the contract.
CannotSealType : Type 'AutoMapper.Configuration.MemberConfigurationExpression' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
MembersMustExist : Member 'public void AutoMapper.Configuration.MemberConfigurationExpression<TSource, TDestination, TMember>.ExplicitExpansion()' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'AutoMapper.Configuration.PathConfigurationExpression<TSource, TDestination, TMember>' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
MembersMustExist : Member 'protected System.Collections.Generic.List<System.Action<AutoMapper.PathMap>> AutoMapper.Configuration.PathConfigurationExpression<TSource, TDestination, TMember>.PathMapActions.get()' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'AutoMapper.Configuration.SourceMappingExpression' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotSealType : Type 'AutoMapper.Configuration.SourceMemberConfig' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.IgnoreAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MapAtRuntimeAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.MappingOrderAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
Expand All @@ -12,5 +32,10 @@ CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMappe
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.UseExistingValueAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueConverterAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'AutoMapper.Configuration.Annotations.ValueResolverAttribute' changed from '[AttributeUsageAttribute(384)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Field | AttributeTargets.Property)]' in the implementation.
CannotSealType : Type 'AutoMapper.Configuration.Conventions.PrePostfixName' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotSealType : Type 'AutoMapper.Configuration.Conventions.ReplaceName' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'AutoMapper.Execution.TypeMapPlanBuilder' is a 'ref struct' in the implementation but is a 'struct' in the contract.
Total Issues: 14
CannotSealType : Type 'AutoMapper.QueryableExtensions.MemberVisitor' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
CannotMakeMemberNonVirtual : Member 'protected System.Linq.Expressions.Expression AutoMapper.QueryableExtensions.MemberVisitor.VisitMember(System.Linq.Expressions.MemberExpression)' is non-virtual in the implementation but is virtual in the contract.
CannotSealType : Type 'AutoMapper.QueryableExtensions.Impl.MemberProjection' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
Total Issues: 39
6 changes: 3 additions & 3 deletions src/AutoMapper/AutoMapper.csproj
Expand Up @@ -14,7 +14,7 @@
<PackageProjectUrl>https://automapper.org</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>

<MinVerDefaultPreReleasePhase>preview</MinVerDefaultPreReleasePhase>
<MinVerDefaultPreReleaseIdentifiers>preview.0</MinVerDefaultPreReleaseIdentifiers>
<MinVerTagPrefix>v</MinVerTagPrefix>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
Expand All @@ -40,8 +40,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="MinVer" Version="2.5.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="MinVer" Version="4.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.DotNet.ApiCompat" Version="7.0.0-beta.22074.1" PrivateAssets="All" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/AutoMapper/Configuration/ConfigurationValidator.cs
Expand Up @@ -116,7 +116,7 @@ private void CheckPropertyMaps(IGlobalConfiguration config, HashSet<TypeMap> typ
// when we don't know what the source type is, bail
if (sourceType.IsGenericParameter || sourceType == typeof(object))
{
return;
continue;
}
DryRunTypeMap(config, typeMapsChecked, new(sourceType, memberMap.DestinationType), null, memberMap);
}
Expand Down
6 changes: 3 additions & 3 deletions src/AutoMapper/Configuration/Conventions.cs
Expand Up @@ -5,7 +5,7 @@ public interface ISourceToDestinationNameMapper
void Merge(ISourceToDestinationNameMapper other);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public class MemberConfiguration
public sealed class MemberConfiguration
{
NameSplitMember _nameSplitMember;
public INamingConvention SourceNamingConvention { get; set; } = PascalCaseNamingConvention.Instance;
Expand Down Expand Up @@ -66,7 +66,7 @@ public void Merge(MemberConfiguration other)
}
}
}
public class PrePostfixName : ISourceToDestinationNameMapper
public sealed class PrePostfixName : ISourceToDestinationNameMapper
{
public List<string> DestinationPrefixes { get; } = new();
public List<string> DestinationPostfixes { get; } = new();
Expand All @@ -89,7 +89,7 @@ public void Merge(ISourceToDestinationNameMapper other)
DestinationPostfixes.TryAdd(typedOther.DestinationPostfixes);
}
}
public class ReplaceName : ISourceToDestinationNameMapper
public sealed class ReplaceName : ISourceToDestinationNameMapper
{
public List<MemberNameReplacer> MemberNameReplacers { get; } = new();
public MemberInfo GetSourceMember(TypeDetails sourceTypeDetails, Type destType, Type destMemberType, string nameToSearch)
Expand Down
11 changes: 5 additions & 6 deletions src/AutoMapper/Configuration/CtorParamConfigurationExpression.cs
@@ -1,6 +1,4 @@
using System.Runtime.CompilerServices;
namespace AutoMapper.Configuration;

namespace AutoMapper.Configuration;
public interface ICtorParamConfigurationExpression
{
/// <summary>
Expand All @@ -11,7 +9,8 @@ public interface ICtorParamConfigurationExpression
/// <summary>
/// Ignore this member for LINQ projections unless explicitly expanded during projection
/// </summary>
void ExplicitExpansion();
/// <param name="value">Is explicitExpansion active</param>
void ExplicitExpansion(bool value = true);
}
public interface ICtorParamConfigurationExpression<TSource> : ICtorParamConfigurationExpression
{
Expand All @@ -35,7 +34,7 @@ public interface ICtorParameterConfiguration
void Configure(TypeMap typeMap);
}
[EditorBrowsable(EditorBrowsableState.Never)]
public class CtorParamConfigurationExpression<TSource, TDestination> : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
public sealed class CtorParamConfigurationExpression<TSource, TDestination> : ICtorParamConfigurationExpression<TSource>, ICtorParameterConfiguration
{
public string CtorParamName { get; }
public Type SourceType { get; }
Expand Down Expand Up @@ -63,7 +62,7 @@ public void MapFrom(string sourceMembersPath)
_ctorParamActions.Add(cpm => cpm.MapFrom(sourceMembersPath, sourceMembers));
}

public void ExplicitExpansion() => _ctorParamActions.Add(cpm => cpm.ExplicitExpansion = true);
public void ExplicitExpansion(bool value) => _ctorParamActions.Add(cpm => cpm.ExplicitExpansion = value);

public void Configure(TypeMap typeMap)
{
Expand Down

0 comments on commit f937b0f

Please sign in to comment.