├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ ├── ci.yml
│ ├── lock.yml
│ └── release.yml
├── .gitignore
├── .readthedocs.yml
├── AutoMapper.sln
├── AutoMapper.snk
├── Build.ps1
├── CONTRIBUTING.md
├── Directory.Build.props
├── ISSUE_TEMPLATE.md
├── LICENSE.txt
├── Push.ps1
├── README.md
├── Setup.ps1
├── docs
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── 10.0-Upgrade-Guide.md
│ ├── 11.0-Upgrade-Guide.md
│ ├── 12.0-Upgrade-Guide.md
│ ├── 13.0-Upgrade-Guide.md
│ ├── 5.0-Upgrade-Guide.md
│ ├── 8.0-Upgrade-Guide.md
│ ├── 8.1.1-Upgrade-Guide.md
│ ├── 9.0-Upgrade-Guide.md
│ ├── API-Changes.md
│ ├── Attribute-mapping.md
│ ├── Before-and-after-map-actions.md
│ ├── Conditional-mapping.md
│ ├── Configuration-validation.md
│ ├── Configuration.md
│ ├── Construction.md
│ ├── Custom-type-converters.md
│ ├── Custom-value-resolvers.md
│ ├── Dependency-injection.md
│ ├── Dynamic-and-ExpandoObject-Mapping.md
│ ├── Enum-Mapping.md
│ ├── Expression-Translation-(UseAsDataSource).md
│ ├── Flattening.md
│ ├── Getting-started.md
│ ├── Lists-and-arrays.md
│ ├── Mapping-inheritance.md
│ ├── Nested-mappings.md
│ ├── Null-substitution.md
│ ├── Open-Generics.md
│ ├── Projection.md
│ ├── Queryable-Extensions.md
│ ├── Reverse-Mapping-and-Unflattening.md
│ ├── Setup.md
│ ├── The-MyGet-build.md
│ ├── Understanding-your-mapping.md
│ ├── Value-converters.md
│ ├── Value-transformers.md
│ ├── conf.py
│ ├── img
│ └── logo.png
│ └── index.rst
├── icon.png
├── nuget.config
├── nuget.exe
└── src
├── AutoMapper.DI.Tests
├── AppDomainResolutionTests.cs
├── AssemblyResolutionTests.cs
├── AttributeTests.cs
├── AutoMapper.DI.Tests.csproj
├── DependencyTests.cs
├── Integrations
│ └── ServiceLifetimeTests.cs
├── MultipleRegistrationTests.cs
├── Profiles.cs
├── Properties
│ └── AssemblyInfo.cs
├── ScopeTests.cs
├── ServiceLifetimeTests.cs
└── TypeResolutionTests.cs
├── AutoMapper
├── ApiCompat
│ ├── PreBuild.ps1
│ └── PreBuild.sh
├── ApiCompatBaseline.txt
├── AssemblyInfo.cs
├── AutoMapper.csproj
├── AutoMapperMappingException.cs
├── Configuration
│ ├── Annotations
│ │ ├── AutoMapAttribute.cs
│ │ ├── IMemberConfigurationProvider.cs
│ │ ├── IgnoreAttribute.cs
│ │ ├── MapAtRuntimeAttribute.cs
│ │ ├── MappingOrderAttribute.cs
│ │ ├── NullSubstituteAttribute.cs
│ │ ├── SourceMemberAttribute.cs
│ │ ├── UseExistingValueAttribute.cs
│ │ ├── ValueConverterAttribute.cs
│ │ └── ValueResolverAttribute.cs
│ ├── ConfigurationValidator.cs
│ ├── Conventions.cs
│ ├── CtorParamConfigurationExpression.cs
│ ├── IMappingExpression.cs
│ ├── IMappingExpressionBase.cs
│ ├── IMappingOperationOptions.cs
│ ├── IMemberConfigurationExpression.cs
│ ├── INamingConvention.cs
│ ├── IProfileExpression.cs
│ ├── MapperConfiguration.cs
│ ├── MapperConfigurationExpression.cs
│ ├── MappingExpression.cs
│ ├── MemberConfigurationExpression.cs
│ ├── PathConfigurationExpression.cs
│ ├── Profile.cs
│ ├── SourceMappingExpression.cs
│ └── TypeMapConfiguration.cs
├── ConstructorMap.cs
├── Execution
│ ├── ExpressionBuilder.cs
│ ├── ObjectFactory.cs
│ ├── ProxyGenerator.cs
│ └── TypeMapPlanBuilder.cs
├── Features.cs
├── Internal
│ ├── InternalApi.cs
│ ├── LockingConcurrentDictionary.cs
│ ├── MemberPath.cs
│ ├── PrimitiveHelper.cs
│ ├── ReflectionHelper.cs
│ ├── TypeDetails.cs
│ ├── TypeExtensions.cs
│ └── TypePair.cs
├── Mapper.cs
├── Mappers
│ ├── AssignableMapper.cs
│ ├── CollectionMapper.cs
│ ├── ConstructorMapper.cs
│ ├── ConversionOperatorMapper.cs
│ ├── ConvertMapper.cs
│ ├── EnumToEnumMapper.cs
│ ├── FromDynamicMapper.cs
│ ├── FromStringDictionaryMapper.cs
│ ├── IObjectMapper.cs
│ ├── KeyValueMapper.cs
│ ├── MapperRegistry.cs
│ ├── NullableDestinationMapper.cs
│ ├── NullableSourceMapper.cs
│ ├── ParseStringMapper.cs
│ ├── StringToEnumMapper.cs
│ ├── ToDynamicMapper.cs
│ ├── ToStringDictionaryMapper.cs
│ ├── ToStringMapper.cs
│ └── UnderlyingEnumTypeMapper.cs
├── MemberMap.cs
├── PathMap.cs
├── ProfileMap.cs
├── PropertyMap.cs
├── QueryableExtensions
│ ├── Extensions.cs
│ ├── NullsafeQueryRewriter.cs
│ ├── ProjectionBuilder.cs
│ └── ProjectionMappers
│ │ ├── AssignableProjectionMapper.cs
│ │ ├── EnumProjectionMapper.cs
│ │ ├── EnumerableProjectionMapper.cs
│ │ ├── NullableSourceProjectionMapper.cs
│ │ └── StringProjectionMapper.cs
├── ResolutionContext.cs
├── ServiceCollectionExtensions.cs
└── TypeMap.cs
├── Benchmark
├── BenchEngine.cs
├── Benchmark.csproj
├── FlatteningMapper.cs
├── HiPerfTimer.cs
├── IObjectToObjectMapper.cs
└── Program.cs
├── IntegrationTests
├── AutoMapper.IntegrationTests.csproj
├── BuiltInTypes
│ ├── ByteArray.cs
│ ├── ConvertUsing.cs
│ ├── DateTimeToNullableDateTime.cs
│ ├── Enums.cs
│ ├── NullableToNonNullable.cs
│ ├── ProjectEnumerableOfIntToHashSet.cs
│ └── ProjectEnumerableOfIntToList.cs
├── ChildClassTests.cs
├── ConstructorDefaultValue.cs
├── CustomMapFrom
│ ├── CustomMapFromTest.cs
│ └── MapObjectPropertyFromSubQuery.cs
├── CustomProjection.cs
├── ExplicitExpansion
│ ├── ConstructorExplicitExpansionOverride.cs
│ ├── ExpandCollections.cs
│ ├── ExpandCollectionsOverride.cs
│ ├── ExpandCollectionsWithStrings.cs
│ ├── ExpandMembersPath.cs
│ ├── ExplicitlyExpandCollectionsAndChildReferences.cs
│ ├── MembersToExpandExpressions.cs
│ ├── NestedExplicitExpand.cs
│ ├── NestedExplicitExpandWithFields.cs
│ ├── ProjectAndAllowNullCollections.cs
│ └── ProjectionWithExplicitExpansion.cs
├── ICollectionAggregateProjections.cs
├── IEnumerableAggregateProjections.cs
├── IEnumerableMemberProjections.cs
├── IncludeMembers.cs
├── Inheritance
│ ├── DerivedComplexTypes.cs
│ ├── OverrideDestinationMappingsTest.cs
│ ├── PolymorphismTests.cs
│ ├── ProjectToAbstractType.cs
│ ├── ProjectToAbstractTypeWithInheritance.cs
│ ├── ProxyTests.cs
│ └── QueryableInterfaceInheritanceIssue.cs
├── IntegrationTest.cs
├── MaxDepth
│ ├── MaxDepthWithCollections.cs
│ ├── NavigationPropertySO.cs
│ └── NestedDtos.cs
├── NullCheckCollections.cs
├── NullSubstitute.cs
├── ParameterizedQueries.cs
├── ProjectionAdvanced.cs
├── ProjectionOrderTest.cs
└── ValueTransformerTests.cs
├── TestApp
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── TestApp.csproj
└── UnitTests
├── AddProfiles.cs
├── App.config
├── ArraysAndLists.cs
├── AssemblyScanning.cs
├── AssertionExtensions.cs
├── AttributeBasedMaps.cs
├── AutoMapper.UnitTests.csproj
├── AutoMapperSpecBase.cs
├── AutoMapperTester.cs
├── BasicFlattening.cs
├── BeforeAfterMapping.cs
├── BidirectionalRelationships.cs
├── BidirectionalRelationshipsWithoutPR.cs
├── Bug
├── AddingConfigurationForNonMatchingDestinationMemberBug.cs
├── AfterMapNestedObjects.cs
├── AllowNullCollectionsAssignableArray.cs
├── AllowNullDestinationValuesBugs.cs
├── AssertConfigurationIsValidNullables.cs
├── AssignableCollectionBug.cs
├── AutoMapperInheritanceProblemDemo.cs
├── BaseMapWithIncludesAndUnincludedMappings.cs
├── CannotConvertEnumToNullable.cs
├── CannotMapICollectionToAggregateSumDestination.cs
├── CannotProjectIEnumerableToAggregateDestinations.cs
├── CannotProjectStringToNullableEnum.cs
├── CaseSensitivityBug.cs
├── CollectionBaseClassGetConvention.cs
├── CollectionMapperMapsISetIncorrectly.cs
├── CollectionWhere.cs
├── CollectionsNullability.cs
├── ConditionBug.cs
├── ConstructUsingReturnsNull.cs
├── ConstructorParameterNamedType.cs
├── ContextValuesIncorrect.cs
├── ConvertMapperThreading.cs
├── CreateMapExpressionWithIgnoredPropertyBug.cs
├── CustomConverters.cs
├── CustomIEnumerableBug.cs
├── DeepCloningBug.cs
├── DeepInheritanceIssue.cs
├── DestinationCtorCalledTwice.cs
├── DestinationValueInitializedByCtorBug.cs
├── DuplicateExtensionMethods.cs
├── DuplicateValuesBug.cs
├── DuplicateValuesBugWithoutPR.cs
├── EFCollections.cs
├── EmptyNullSubstituteBug.cs
├── EnumCaseSensitivityBug.cs
├── EnumConditionsBug.cs
├── EnumMatchingOnValue.cs
├── ExistingArrays.cs
├── ForAllMembersAndDoNotUseDestinationValue.cs
├── GenericCreateMapWithCircularReferences.cs
├── GenericTypeConverter.cs
├── GuidTryExpression.cs
├── IgnoreAll.cs
├── IncludeBaseInheritance.cs
├── IncludeInheritance.cs
├── InitializeNRE.cs
├── IntToNullableDecimal.cs
├── InterfaceMultipleInheritance.cs
├── InterfaceSelfMappingBug.cs
├── InternalProperties.cs
├── JsonNet.cs
├── LazyCollectionMapping.cs
├── ListSourceMapperBug.cs
├── MapAtRuntime
│ ├── BaseEntity.cs
│ ├── BaseEntityDTO.cs
│ ├── Entity1.cs
│ ├── Entity10.cs
│ ├── Entity11.cs
│ ├── Entity12.cs
│ ├── Entity13.cs
│ ├── Entity14.cs
│ ├── Entity15.cs
│ ├── Entity16.cs
│ ├── Entity17.cs
│ ├── Entity18.cs
│ ├── Entity19.cs
│ ├── Entity2.cs
│ ├── Entity20.cs
│ ├── Entity21.cs
│ ├── Entity22.cs
│ ├── Entity23.cs
│ ├── Entity24.cs
│ ├── Entity25.cs
│ ├── Entity26.cs
│ ├── Entity3.cs
│ ├── Entity4.cs
│ ├── Entity5.cs
│ ├── Entity6.cs
│ ├── Entity7.cs
│ ├── Entity8.cs
│ ├── Entity9.cs
│ ├── EntityDTO1.cs
│ ├── EntityDTO10.cs
│ ├── EntityDTO11.cs
│ ├── EntityDTO12.cs
│ ├── EntityDTO13.cs
│ ├── EntityDTO14.cs
│ ├── EntityDTO15.cs
│ ├── EntityDTO16.cs
│ ├── EntityDTO17.cs
│ ├── EntityDTO18.cs
│ ├── EntityDTO19.cs
│ ├── EntityDTO2.cs
│ ├── EntityDTO20.cs
│ ├── EntityDTO21.cs
│ ├── EntityDTO22.cs
│ ├── EntityDTO23.cs
│ ├── EntityDTO24.cs
│ ├── EntityDTO25.cs
│ ├── EntityDTO26.cs
│ ├── EntityDTO3.cs
│ ├── EntityDTO4.cs
│ ├── EntityDTO5.cs
│ ├── EntityDTO6.cs
│ ├── EntityDTO7.cs
│ ├── EntityDTO8.cs
│ ├── EntityDTO9.cs
│ └── MapAtRuntime.cs
├── MapAtRuntimeWithCollections
│ ├── BaseEntity.cs
│ ├── BaseEntityDTO.cs
│ ├── Entity1.cs
│ ├── Entity10.cs
│ ├── Entity11.cs
│ ├── Entity12.cs
│ ├── Entity13.cs
│ ├── Entity14.cs
│ ├── Entity15.cs
│ ├── Entity16.cs
│ ├── Entity17.cs
│ ├── Entity18.cs
│ ├── Entity19.cs
│ ├── Entity2.cs
│ ├── Entity20.cs
│ ├── Entity21.cs
│ ├── Entity22.cs
│ ├── Entity23.cs
│ ├── Entity24.cs
│ ├── Entity25.cs
│ ├── Entity26.cs
│ ├── Entity3.cs
│ ├── Entity4.cs
│ ├── Entity5.cs
│ ├── Entity6.cs
│ ├── Entity7.cs
│ ├── Entity8.cs
│ ├── Entity9.cs
│ ├── EntityDTO1.cs
│ ├── EntityDTO10.cs
│ ├── EntityDTO11.cs
│ ├── EntityDTO12.cs
│ ├── EntityDTO13.cs
│ ├── EntityDTO14.cs
│ ├── EntityDTO15.cs
│ ├── EntityDTO16.cs
│ ├── EntityDTO17.cs
│ ├── EntityDTO18.cs
│ ├── EntityDTO19.cs
│ ├── EntityDTO2.cs
│ ├── EntityDTO20.cs
│ ├── EntityDTO21.cs
│ ├── EntityDTO22.cs
│ ├── EntityDTO23.cs
│ ├── EntityDTO24.cs
│ ├── EntityDTO25.cs
│ ├── EntityDTO26.cs
│ ├── EntityDTO3.cs
│ ├── EntityDTO4.cs
│ ├── EntityDTO5.cs
│ ├── EntityDTO6.cs
│ ├── EntityDTO7.cs
│ ├── EntityDTO8.cs
│ ├── EntityDTO9.cs
│ └── MapAtRuntimeWithCollections.cs
├── MapExpandoObjectProperty.cs
├── MapFromClosureBug.cs
├── MapOverloadsWithDynamic.cs
├── MappingInheritance.cs
├── MappingToAReadOnlyCollection.cs
├── MemberListSourceAndForPath.cs
├── MemberNamedTypeBug.cs
├── MissingMapping.cs
├── MultiThreadingIssues.cs
├── MultidimensionalArrays.cs
├── MultipleInterfaceInheritance.cs
├── MultipleTypeConverterInterfaces.cs
├── NamingConventions.cs
├── NestedMappingProjectionsExplicitExpanding.cs
├── NonExistingProperty.cs
├── NullArrayBug.cs
├── NullConstructorParameterName.cs
├── NullSubstituteInnerClass.cs
├── NullSubstituteType.cs
├── NullToString.cs
├── NullableBytesAndEnums.cs
├── NullableConverterBug.cs
├── NullableDateTime.cs
├── NullableEnumToNullableValueType.cs
├── NullableEnums.cs
├── NullableIntToNullableDecimal.cs
├── NullableIntToNullableEnum.cs
├── NullablePropertiesBug.cs
├── NullableResolveUsing.cs
├── NullableToInvalid.cs
├── NullableUntypedMapFrom.cs
├── ObjectEnumToObjectEnum.cs
├── ObjectTypeMapFailure.cs
├── OneSourceWithMultipleDestinationsAndPreserveReferences.cs
├── OneSourceWithMultipleDestinationsWithoutPR.cs
├── ParentChildResolversBug.cs
├── PreserveReferencesSameDestination.cs
├── ProjectCollectionsBug.cs
├── ProjectConstructorParameters.cs
├── ProjectUsingTheQueriedEntity.cs
├── PropertyNamedType.cs
├── ReadOnlyCollectionMappingBug.cs
├── ReadOnlyFieldMappingBug.cs
├── RecognizeDestinationPostfixes.cs
├── RecognizeIxesBug.cs
├── RemovePrefixes.cs
├── RepeatedMappingConfigurationTest.cs
├── ReportMissingInclude.cs
├── ReverseMapReplaceMemberName.cs
├── SelectiveConfigurationValidation.cs
├── SequenceContainsNoElementsTest.cs
├── SetterOnlyBug.cs
├── StructMapping.cs
├── SubclassMappings.cs
├── TargetISet.cs
├── TypeMapIncludeBaseTypes.cs
├── UseDestinationValue.cs
└── WithoutPreserveReferencesSameDestination.cs
├── BuildExecutionPlan.cs
├── ClassDiagram1.cd
├── CollectionMapping.cs
├── ConditionalMapping.cs
├── ConfigCompilation.cs
├── ConfigurationFeatureTest.cs
├── ConfigurationRules.cs
├── ConfigurationValidation.cs
├── Constructors.cs
├── ContextItems.cs
├── CustomCollectionTester.cs
├── CustomMapping.cs
├── CustomValidations.cs
├── Dictionaries.cs
├── EnumToNullableEnum.cs
├── Enumerations.cs
├── ExplicitMapperCreation.cs
├── ExpressionBridge.cs
├── ExtensionMethods.cs
├── FillingExistingDestination.cs
├── ForAllMaps.cs
├── ForAllMembers.cs
├── ForPath.cs
├── General.cs
├── IMappingExpression
├── ForCtorParam.cs
├── IncludeMembers.cs
├── NonGenericConstructorTests.cs
├── NonGenericProjectEnumTest.cs
├── NonGenericResolveUsing.cs
└── NonGenericReverseMapping.cs
├── IgnoreAllPropertiesWithAnInaccessibleSetterTests.cs
├── IgnoreAllTests.cs
├── Indexers.cs
├── InterfaceMapping.cs
├── Internal
├── CreateProxyThreading.cs
├── GenerateSimilarType.cs
├── MapperTests.cs
├── ObjectFactoryTests.cs
├── PrimitiveExtensionsTester.cs
└── TypeMapFactorySpecs.cs
├── Internationalization.cs
├── MapToAttributeTest.cs
├── Mappers
├── ConstructorMapperTests.cs
├── ConversionOperators.cs
├── ConvertMapperTests.cs
├── CustomMapperTests.cs
├── DynamicMapperTests.cs
├── NameValueCollectionMapperTests.cs
├── ReadOnlyCollectionMapperTests.cs
├── ReadOnlyDictionaryMapperTests.cs
├── StringDictionaryMapperTests.cs
└── TypeHelperTests.cs
├── MappingExceptions.cs
├── MappingExpressionFeatureWithReverseTest.cs
├── MappingExpressionFeatureWithoutReverseTest.cs
├── MappingInheritance
├── ApplyIncludeBaseRecursively.cs
├── ConventionMappedCollectionShouldMapBaseTypes.cs
├── IgnoreShouldBeInherited.cs
├── IgnoreShouldBeInheritedIfConventionCannotMap.cs
├── IncludeAllDerived.cs
├── IncludeBaseBug.cs
├── IncludeBaseShouldNotCreateMaps.cs
├── IncludeBaseShouldValidateTypes.cs
├── IncludeBaseWithNonGenericUsage.cs
├── IncludedBaseMappingShouldInheritBaseMappings.cs
├── IncludedMappingShouldInheritBaseMappings.cs
├── InheritanceWithoutIncludeShouldWork.cs
├── MapToBaseClass.cs
├── MultipleInheritedBaseMappingsOfSameTypeFails.cs
├── OpenGenericsWithInheritance.cs
├── OverrideIgnore.cs
├── PreserveReferencesWithInheritance.cs
├── PropertyOnMappingShouldResolveMostSpecificType.cs
├── ReverseMapWithInclude.cs
├── ShouldInheritBeforeAndAfterMap.cs
├── ShouldSupportOnlyDestinationTypeBeingDerived.cs
└── SourceValidationWithInheritance.cs
├── MappingOrder.cs
├── MaxDepthTests.cs
├── MaxExecutionPlanDepth.cs
├── MemberNameReplacers.cs
├── MemberResolution.cs
├── NestedContainers.cs
├── NullBehavior.cs
├── OpenGenerics.cs
├── Profiles.cs
├── Projection
├── ConstructorTests.cs
├── ExplicitExpansion.cs
├── ExplicitExpansionWithInheritance.cs
├── ExplicitValues.cs
├── GenericsTests.cs
├── InheritedMaps.cs
├── MapFromTest.cs
├── MoreExplanatoryExceptionTests.cs
├── NestedAndArraysTests.cs
├── NestedExpressionsMapFromTests.cs
├── NonGenericQueryableTests.cs
├── NullSubstitutes.cs
├── ParameterizedQueriesTests.cs
├── PrimitiveArraysTest.cs
├── ProjectCollectionEnumerableTest.cs
├── ProjectCollectionListTest.cs
├── ProjectEnumTest.cs
├── ProjectEnumerableToArrayTest.cs
├── ProjectIReadOnlyCollection.cs
├── ProjectTest.cs
├── ProjectionMappers.cs
├── ProjectionTests.cs
├── RecursiveQuery.cs
└── ToStringTests.cs
├── Regression.cs
├── ReverseMapWithPreserveReferences.cs
├── ReverseMapWithoutPreserveReferences.cs
├── ReverseMapping.cs
├── SeparateConfiguration.cs
├── ShouldMapMethod.cs
├── ShouldUseConstructor.cs
├── TesterExtensions.cs
├── TypeConverters.cs
├── TypeExtensionsTests.cs
├── UsingEngineInsideMap.cs
├── ValueConverters.cs
├── ValueTransformers.cs
└── ValueTypes.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.doc diff=astextplain
2 | *.DOC diff=astextplain
3 | *.docx diff=astextplain
4 | *.DOCX diff=astextplain
5 | *.dot diff=astextplain
6 | *.DOT diff=astextplain
7 | *.pdf diff=astextplain
8 | *.PDF diff=astextplain
9 | *.rtf diff=astextplain
10 | *.RTF diff=astextplain
11 |
12 | *.jpg binary
13 | *.png binary
14 | *.gif binary
15 |
16 | core.eol crlf
17 |
18 | *.cs diff=csharp
19 |
20 | *.csproj merge=union
21 | *.vbproj merge=union
22 | *.fsproj merge=union
23 | *.dbproj merge=union
24 | *.sln merge=union
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [jbogard, lbargaoanu]
4 |
5 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 | permissions:
11 | contents: read
12 | concurrency:
13 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
14 | cancel-in-progress: true
15 | jobs:
16 | build:
17 | strategy:
18 | fail-fast: false
19 | runs-on: windows-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v2
23 | with:
24 | fetch-depth: 0
25 | - name: Install SQL Local DB
26 | run: ./Setup.ps1
27 | shell: pwsh
28 | - name: Build and Test
29 | run: ./Build.ps1
30 | shell: pwsh
31 | - name: Push to MyGet
32 | env:
33 | NUGET_URL: https://www.myget.org/F/automapperdev/api/v3/index.json
34 | NUGET_API_KEY: ${{ secrets.MYGET_CI_API_KEY }}
35 | run: ./Push.ps1
36 | shell: pwsh
37 | - name: Artifacts
38 | uses: actions/upload-artifact@v2
39 | with:
40 | name: artifacts
41 | path: artifacts/**/*
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | name: 'Lock threads'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * 0'
6 |
7 | permissions:
8 | contents: read
9 |
10 | jobs:
11 | lock:
12 | permissions:
13 | issues: write # for dessant/lock-threads to lock issues
14 | pull-requests: write # for dessant/lock-threads to lock PRs
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: dessant/lock-threads@v2
18 | with:
19 | github-token: ${{ github.token }}
20 | issue-lock-inactive-days: 31
21 | pr-lock-inactive-days: 31
22 | issue-lock-comment: >
23 | This issue has been automatically locked since there
24 | has not been any recent activity after it was closed.
25 | Please open a new issue for related bugs.
26 | pr-lock-comment: >
27 | This pull request has been automatically locked since there
28 | has not been any recent activity after it was closed.
29 | Please open a new issue for related bugs.
30 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*.*.*'
7 | permissions:
8 | contents: read
9 |
10 | jobs:
11 | build:
12 | strategy:
13 | fail-fast: false
14 | runs-on: windows-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 | - name: Install SQL Local DB
21 | run: ./Setup.ps1
22 | shell: pwsh
23 | - name: Build and Test
24 | run: ./Build.ps1
25 | shell: pwsh
26 | - name: Push to MyGet
27 | env:
28 | NUGET_URL: https://www.myget.org/F/automapperdev/api/v3/index.json
29 | NUGET_API_KEY: ${{ secrets.MYGET_CI_API_KEY }}
30 | run: ./Push.ps1
31 | shell: pwsh
32 | - name: Push to NuGet
33 | env:
34 | NUGET_URL: https://api.nuget.org/v3/index.json
35 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
36 | run: ./Push.ps1
37 | shell: pwsh
38 | - name: Artifacts
39 | uses: actions/upload-artifact@v2
40 | with:
41 | name: artifacts
42 | path: artifacts/**/*
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | release/
3 | scripts/
4 | artifacts/
5 | .dotnet
6 | TestResults/
7 | *.suo
8 | *.user
9 | bin
10 | Bin
11 | obj
12 | _ReSharper*
13 | *.csproj.user
14 | *.resharper.user
15 | *.suo
16 | *.cache
17 | TestResult.xml
18 | AppPackages/
19 | *.bak
20 | packages
21 | *.orig
22 | *.DotSettings
23 | *.ide/
24 | .nuget
25 | project.lock.json
26 | .vs
27 |
28 | # JetBrains Rider
29 | .idea/
30 | *.sln.iml
31 |
32 | # Read the Docs
33 | docs/_build
34 | /src/LastMajorVersionBinary
35 | *.diagsession
36 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 |
3 | build:
4 | os: "ubuntu-22.04"
5 | tools:
6 | python: "3.10"
7 |
8 | python:
9 | install:
10 | - requirements: docs/requirements.txt
11 |
12 | sphinx:
13 | configuration: docs/source/conf.py
--------------------------------------------------------------------------------
/AutoMapper.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sulhan12/AutoMapper/2314ed6c64b1538f542859dfc1a50a91e3504452/AutoMapper.snk
--------------------------------------------------------------------------------
/Build.ps1:
--------------------------------------------------------------------------------
1 | # Taken from psake https://github.com/psake/psake
2 |
3 | <#
4 | .SYNOPSIS
5 | This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
6 | to see if an error occcured. If an error is detected then an exception is thrown.
7 | This function allows you to run command-line programs without having to
8 | explicitly check the $lastexitcode variable.
9 | .EXAMPLE
10 | exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
11 | #>
12 | function Exec
13 | {
14 | [CmdletBinding()]
15 | param(
16 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
17 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
18 | )
19 | & $cmd
20 | if ($lastexitcode -ne 0) {
21 | throw ("Exec: " + $errorMessage)
22 | }
23 | }
24 |
25 | $artifacts = ".\artifacts"
26 |
27 | if(Test-Path $artifacts) { Remove-Item $artifacts -Force -Recurse }
28 |
29 | exec { & dotnet test -c Release --results-directory $artifacts -l trx }
30 |
31 | exec { & dotnet pack .\src\AutoMapper\AutoMapper.csproj -c Release -o $artifacts --no-build }
32 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Jimmy Bogard
5 | latest
6 | $(NoWarn);CS1701;CS1702;CS1591
7 | true
8 | strict
9 | enable
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2010 Jimmy Bogard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/Push.ps1:
--------------------------------------------------------------------------------
1 | $scriptName = $MyInvocation.MyCommand.Name
2 | $artifacts = "./artifacts"
3 |
4 | if ([string]::IsNullOrEmpty($Env:NUGET_API_KEY)) {
5 | Write-Host "${scriptName}: NUGET_API_KEY is empty or not set. Skipped pushing package(s)."
6 | } else {
7 | Get-ChildItem $artifacts -Filter "*.nupkg" | ForEach-Object {
8 | Write-Host "$($scriptName): Pushing $($_.Name)"
9 | dotnet nuget push $_ --source $Env:NUGET_URL --api-key $Env:NUGET_API_KEY
10 | if ($lastexitcode -ne 0) {
11 | throw ("Exec: " + $errorMessage)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Setup.ps1:
--------------------------------------------------------------------------------
1 | # Taken from psake https://github.com/psake/psake
2 |
3 | <#
4 | .SYNOPSIS
5 | This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
6 | to see if an error occcured. If an error is detected then an exception is thrown.
7 | This function allows you to run command-line programs without having to
8 | explicitly check the $lastexitcode variable.
9 | .EXAMPLE
10 | exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
11 | #>
12 | function Exec
13 | {
14 | [CmdletBinding()]
15 | param(
16 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
17 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
18 | )
19 | & $cmd
20 | if ($lastexitcode -ne 0) {
21 | throw ("Exec: " + $errorMessage)
22 | }
23 | }
24 |
25 | Write-Host "Downloading"
26 | Import-Module BitsTransfer
27 | Start-BitsTransfer -Source https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi -Destination SqlLocalDB.msi
28 | Write-Host "Installing"
29 | Start-Process -FilePath "SqlLocalDB.msi" -Wait -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES";
30 | <#
31 | Write-Host "Checking"
32 | sqlcmd -l 60 -S "(localdb)\MSSQLLocalDB" -Q "SELECT @@VERSION;"
33 | #>
34 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx==7.1.2
2 | sphinx-rtd-theme==1.3.0rc1
3 | myst_parser==2.0.0
--------------------------------------------------------------------------------
/docs/source/13.0-Upgrade-Guide.md:
--------------------------------------------------------------------------------
1 | # 13.0 Upgrade Guide
2 |
3 | [Release notes](https://github.com/AutoMapper/AutoMapper/releases/tag/v13.0.0).
4 |
5 | ## AutoMapper now targets .Net 6
6 |
7 | ## `AddAutoMapper` is part of the core package and the DI package is discontinued
8 |
9 | ## `AllowAdditiveTypeMapCreation` was removed
10 |
11 | Be sure to call `CreateMap` once for a source type, destination type pair. If you want to reuse configuration, use mapping inheritance.
12 |
13 | ## ProjectTo runtime polymorphic mapping with Include/IncludeBase
14 |
15 | 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`.
16 |
17 | ## `Context.State` similar to `Context.Items`
18 |
19 | The same pattern the framework uses to pass state to delegates. Note that `State` and `Items` are mutually exclusive per `Map` call.
20 |
21 | ## Custom Equals/GetHashCode for source objects
22 |
23 | To avoid broken implementations, we no longer call those when checking for identical source objects, we hard code to checking object references.
24 |
--------------------------------------------------------------------------------
/docs/source/8.1.1-Upgrade-Guide.md:
--------------------------------------------------------------------------------
1 | # 8.1.1 Upgrade Guide
2 |
3 | The purpose of this release is to allow you to upgrade to 9.0 gradually.
4 |
5 | ## AutoMapper no longer creates maps automatically by default
6 |
7 | `CreateMissingTypeMaps` was deprecated and its default value changed to `false`. If you were relying on this, your app will no longer work by default.
8 |
9 | If you're not interested in upgrading to 9.0, where dynamic mapping was removed, you should stick with 8.1.
10 |
11 | Otherwise you can port your app gradually to 9.0 by creating the needed maps. Setting `CreateMissingTypeMaps` to `false` will get you the 9.0 behavior and setting it to `true` will revert to the 8.1 behavior.
12 |
--------------------------------------------------------------------------------
/docs/source/9.0-Upgrade-Guide.md:
--------------------------------------------------------------------------------
1 | # 9.0 Upgrade Guide
2 |
3 | ## The static API was removed
4 |
5 | Switch to the instance based API, preferably using dependency injection.
6 | See [here](Setup.html) and [here](Dependency-injection.html).
7 |
8 | ## AutoMapper no longer creates maps automatically (CreateMissingTypeMaps and conventions)
9 |
10 | You will need to explicitly configure maps, manually or using reflection. Also consider [attribute mapping](Attribute-mapping.html).
--------------------------------------------------------------------------------
/docs/source/API-Changes.md:
--------------------------------------------------------------------------------
1 | # API Changes
2 |
3 | Starting with version 9.0, you can find out [what changed](https://raw.githubusercontent.com/AutoMapper/AutoMapper/master/src/AutoMapper/ApiCompatBaseline.txt) in the public API from the last major version release.
4 | From the [releases page](https://github.com/AutoMapper/AutoMapper/releases) you can reach the source code for that release and the version of ApiCompatBaseline.txt in that tree will tell you what changed.
5 | A major version release is compared with the previous major version release (so 9.0.0 with 8.0.0) and a minor version release with the current major version release (so 9.1.1 with 9.0.0).
--------------------------------------------------------------------------------
/docs/source/Dynamic-and-ExpandoObject-Mapping.md:
--------------------------------------------------------------------------------
1 | # Dynamic and ExpandoObject Mapping
2 |
3 | AutoMapper can map to/from dynamic objects without any explicit configuration:
4 |
5 | ```c#
6 | public class Foo {
7 | public int Bar { get; set; }
8 | public int Baz { get; set; }
9 | public Foo InnerFoo { get; set; }
10 | }
11 | dynamic foo = new MyDynamicObject();
12 | foo.Bar = 5;
13 | foo.Baz = 6;
14 |
15 | var configuration = new MapperConfiguration(cfg => {});
16 |
17 | var result = mapper.Map(foo);
18 | result.Bar.ShouldEqual(5);
19 | result.Baz.ShouldEqual(6);
20 |
21 | dynamic foo2 = mapper.Map(result);
22 | foo2.Bar.ShouldEqual(5);
23 | foo2.Baz.ShouldEqual(6);
24 | ```
25 |
26 | Similarly you can map straight from `Dictionary` to objects, AutoMapper will line up the keys with property names.
27 | For mapping to destination child objects, you can use the dot notation.
28 | ```c#
29 | var result = mapper.Map(new Dictionary { ["InnerFoo.Bar"] = 42 });
30 | result.InnerFoo.Bar.ShouldEqual(42);
31 | ```
--------------------------------------------------------------------------------
/docs/source/Null-substitution.md:
--------------------------------------------------------------------------------
1 | # Null Substitution
2 |
3 | Null substitution allows you to supply an alternate value for a destination member if the source value is null anywhere along the member chain. This means that instead of mapping from null, it will map from the value you supply.
4 |
5 | ```c#
6 | var config = new MapperConfiguration(cfg => cfg.CreateMap()
7 | .ForMember(destination => destination.Value, opt => opt.NullSubstitute("Other Value")));
8 |
9 | var source = new Source { Value = null };
10 | var mapper = config.CreateMapper();
11 | var dest = mapper.Map(source);
12 |
13 | dest.Value.ShouldEqual("Other Value");
14 |
15 | source.Value = "Not null";
16 |
17 | dest = mapper.Map(source);
18 |
19 | dest.Value.ShouldEqual("Not null");
20 | ```
21 |
22 | The substitute is assumed to be of the source member type, and will go through any mapping/conversion after to the destination type.
23 |
--------------------------------------------------------------------------------
/docs/source/The-MyGet-build.md:
--------------------------------------------------------------------------------
1 | # The MyGet Build
2 |
3 | AutoMapper uses MyGet to publish development builds based on the master branch. This means that the MyGet build sometimes contains fixes that are not available in the current NuGet package. Please try the latest MyGet build before reporting issues, in case your issue has already been fixed but not released.
4 |
5 | The AutoMapper MyGet gallery is available [here](https://myget.org/feed/automapperdev/package/nuget/AutoMapper). Be sure to include prereleases.
6 |
7 | ## Installing the Package
8 |
9 | If you want to install the latest MyGet package into a project, you can use the following command:
10 |
11 | ```
12 | Install-Package AutoMapper -Source https://www.myget.org/F/automapperdev/api/v3/index.json -IncludePrerelease
13 | ```
14 |
--------------------------------------------------------------------------------
/docs/source/Understanding-your-mapping.md:
--------------------------------------------------------------------------------
1 | # Understanding Your Mappings
2 |
3 | AutoMapper creates an execution plan for your mapping. That execution plan can be viewed as [an expression tree](https://msdn.microsoft.com/en-us/library/mt654263.aspx?f=255&MSPPError=-2147217396) during debugging. You can get a better view of the resulting code by installing [the ReadableExpressions VS extension](https://marketplace.visualstudio.com/items?itemName=vs-publisher-1232914.ReadableExpressionsVisualizers). If you need to see the code outside VS, you can use [the ReadableExpressions package directly](https://www.nuget.org/packages/AgileObjects.ReadableExpressions). [This DotNetFiddle](https://dotnetfiddle.net/aJYTGZ) has a live demo using the NuGet package, and [this article](https://agileobjects.co.uk/view-automapper-execution-plan-readableexpressions) describes using the VS extension.
4 |
5 | ```c#
6 | var configuration = new MapperConfiguration(cfg => cfg.CreateMap());
7 | var executionPlan = configuration.BuildExecutionPlan(typeof(Foo), typeof(Bar));
8 | ```
9 |
10 | Be sure to remove all such code before release.
11 |
12 | For ProjectTo, you need to inspect `IQueryable.Expression`.
13 |
14 | ```c#
15 | var expression = context.Entities.ProjectTo().Expression;
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/source/Value-transformers.md:
--------------------------------------------------------------------------------
1 | # Value Transformers
2 |
3 | Value transformers apply an additional transformation to a single type. Before assigning the value, AutoMapper will check to see if the value to be set has any value transformations associated, and will apply them before setting.
4 |
5 | You can create value transformers at several different levels:
6 |
7 | - Globally
8 | - Profile
9 | - Map
10 | - Member
11 |
12 | ```c#
13 | var configuration = new MapperConfiguration(cfg => {
14 | cfg.ValueTransformers.Add(val => val + "!!!");
15 | });
16 |
17 | var source = new Source { Value = "Hello" };
18 | var dest = mapper.Map(source);
19 |
20 | dest.Value.ShouldBe("Hello!!!");
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 |
3 | # -- Project information
4 |
5 | project = 'AutoMapper'
6 | copyright = '2024, Jimmy Bogard'
7 | author = 'Jimmy Bogard'
8 |
9 | # -- General configuration
10 |
11 | extensions = [
12 | 'sphinx.ext.duration',
13 | 'sphinx.ext.doctest',
14 | 'sphinx.ext.autodoc',
15 | 'sphinx.ext.autosummary',
16 | 'sphinx.ext.intersphinx',
17 | 'myst_parser'
18 | ]
19 |
20 | intersphinx_mapping = {
21 | 'python': ('https://docs.python.org/3/', None),
22 | 'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
23 | }
24 | intersphinx_disabled_domains = ['std']
25 |
26 | templates_path = ['_templates']
27 |
28 | # -- Options for HTML output
29 |
30 | html_theme = 'sphinx_rtd_theme'
31 | html_theme_options = {
32 | 'logo_only': True,
33 | 'display_version': False
34 | }
35 | html_logo = 'img/logo.png'
36 |
37 |
38 | # -- Options for EPUB output
39 | epub_show_urls = 'footnote'
--------------------------------------------------------------------------------
/docs/source/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sulhan12/AutoMapper/2314ed6c64b1538f542859dfc1a50a91e3504452/docs/source/img/logo.png
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sulhan12/AutoMapper/2314ed6c64b1538f542859dfc1a50a91e3504452/icon.png
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sulhan12/AutoMapper/2314ed6c64b1538f542859dfc1a50a91e3504452/nuget.exe
--------------------------------------------------------------------------------
/src/AutoMapper.DI.Tests/AppDomainResolutionTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
4 | {
5 | using System;
6 | using AutoMapper.Internal;
7 | using Shouldly;
8 | using Xunit;
9 |
10 | public class AppDomainResolutionTests
11 | {
12 | private readonly IServiceProvider _provider;
13 |
14 | public AppDomainResolutionTests()
15 | {
16 | IServiceCollection services = new ServiceCollection();
17 | services.AddAutoMapper(typeof(AppDomainResolutionTests));
18 | _provider = services.BuildServiceProvider();
19 | }
20 |
21 | [Fact]
22 | public void ShouldResolveConfiguration()
23 | {
24 | _provider.GetService().ShouldNotBeNull();
25 | }
26 |
27 | [Fact]
28 | public void ShouldConfigureProfiles()
29 | {
30 | _provider.GetService().Internal().GetAllTypeMaps().Count.ShouldBe(4);
31 | }
32 |
33 | [Fact]
34 | public void ShouldResolveMapper()
35 | {
36 | _provider.GetService().ShouldNotBeNull();
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/AutoMapper.DI.Tests/AttributeTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Shouldly;
4 | using Xunit;
5 |
6 | namespace AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
7 | {
8 | public class AttributeTests
9 | {
10 | [Fact]
11 | public void Should_not_register_static_instance_when_configured()
12 | {
13 | IServiceCollection services = new ServiceCollection();
14 | services.AddAutoMapper(typeof(Source3));
15 |
16 | var serviceProvider = services.BuildServiceProvider();
17 |
18 | var mapper = serviceProvider.GetService();
19 |
20 | var source = new Source3 {Value = 3};
21 |
22 | var dest = mapper.Map(source);
23 |
24 | dest.Value.ShouldBe(source.Value);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/AutoMapper.DI.Tests/AutoMapper.DI.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
7 | AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
8 | true
9 | false
10 | false
11 | false
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/AutoMapper.DI.Tests/DependencyTests.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Extensions.Microsoft.DependencyInjection.Tests
2 | {
3 | using System;
4 | using global::Microsoft.Extensions.DependencyInjection;
5 | using Shouldly;
6 | using Xunit;
7 |
8 | public class DependencyTests
9 | {
10 | private readonly IServiceProvider _provider;
11 |
12 | public DependencyTests()
13 | {
14 | IServiceCollection services = new ServiceCollection();
15 | services.AddTransient(sp => new FooService(5));
16 | services.AddAutoMapper(typeof(Source), typeof(Profile));
17 | _provider = services.BuildServiceProvider();
18 |
19 | _provider.GetService().AssertConfigurationIsValid();
20 | }
21 |
22 | [Fact]
23 | public void ShouldResolveWithDependency()
24 | {
25 | var mapper = _provider.GetService();
26 | var dest = mapper.Map(new Source2());
27 |
28 | dest.ResolvedValue.ShouldBe(5);
29 | }
30 |
31 | [Fact]
32 | public void ShouldConvertWithDependency()
33 | {
34 | var mapper = _provider.GetService();
35 | var dest = mapper.Map(new Source2 { ConvertedValue = 5});
36 |
37 | dest.ConvertedValue.ShouldBe(10);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/AutoMapper.DI.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("AutoMapper.Extensions.Microsoft.DependencyInjection.Tests")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("a93a7f85-292a-4130-891d-4307d3f60c30")]
20 |
21 | [assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)]
22 |
--------------------------------------------------------------------------------
/src/AutoMapper/ApiCompat/PreBuild.ps1:
--------------------------------------------------------------------------------
1 | param([string]$version)
2 | echo $version
3 | $versionNumbers = $version.Split(".")
4 | if($versionNumbers[1] -eq "0" -AND $versionNumbers[2] -eq "0")
5 | {
6 | $oldVersion = $versionNumbers[0] - 1
7 | }else{
8 | $oldVersion = $versionNumbers[0]
9 | }
10 | $oldVersion = $oldVersion.ToString() +".0.0"
11 | echo $oldVersion
12 | & ..\..\nuget install AutoMapper -Version $oldVersion -OutputDirectory ..\LastMajorVersionBinary
13 | & copy ..\LastMajorVersionBinary\AutoMapper.$oldVersion\lib\net6.0\AutoMapper.dll ..\LastMajorVersionBinary
14 |
--------------------------------------------------------------------------------
/src/AutoMapper/ApiCompat/PreBuild.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | version=$1
3 | echo $version
4 | readarray -d . -t versionNumbers <<< $version
5 | if [[ ${versionNumbers[1]} -eq "0" && ${versionNumbers[2]} -eq "0" ]]
6 | then
7 | oldVersion=$(({versionNumbers[0]} - 1))
8 | else
9 | oldVersion=${versionNumbers[0]}
10 | fi
11 | oldVersion="$oldVersion.0.0"
12 | echo $oldVersion
13 | rm -rf ../LastMajorVersionBinary
14 | curl https://globalcdn.nuget.org/packages/automapper.$oldVersion.nupkg --create-dirs -o ../LastMajorVersionBinary/automapper.$oldVersion.nupkg
15 | unzip -j ../LastMajorVersionBinary/automapper.$oldVersion.nupkg lib/netstandard2.1/AutoMapper.dll -d ../LastMajorVersionBinary
16 |
--------------------------------------------------------------------------------
/src/AutoMapper/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Resources;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: CLSCompliant(true)]
5 | [assembly: ComVisible(false)]
6 | [assembly: NeutralResourcesLanguage("en")]
7 |
8 | namespace System.Runtime.CompilerServices;
9 |
10 | static class IsExternalInit { }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/IMemberConfigurationProvider.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration;
2 |
3 | public interface IMemberConfigurationProvider
4 | {
5 | void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression);
6 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/IgnoreAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Ignore this member for configuration validation and skip during mapping.
5 | ///
6 | ///
7 | /// Must be used in combination with
8 | ///
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public sealed class IgnoreAttribute : Attribute, IMemberConfigurationProvider
11 | {
12 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
13 | {
14 | memberConfigurationExpression.Ignore();
15 | }
16 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/MapAtRuntimeAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Do not precompute the execution plan for this member, just map it at runtime.
5 | /// Simplifies the execution plan by not inlining.
6 | ///
7 | ///
8 | /// Must be used in combination with
9 | ///
10 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
11 | public sealed class MapAtRuntimeAttribute : Attribute, IMemberConfigurationProvider
12 | {
13 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
14 | {
15 | memberConfigurationExpression.MapAtRuntime();
16 | }
17 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/MappingOrderAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Supply a custom mapping order instead of what the .NET runtime returns
5 | ///
6 | ///
7 | /// Must be used in combination with
8 | ///
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public sealed class MappingOrderAttribute(int value) : Attribute, IMemberConfigurationProvider
11 | {
12 | public int Value { get; } = value;
13 |
14 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
15 | {
16 | memberConfigurationExpression.SetMappingOrder(Value);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/NullSubstituteAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Substitute a custom value when the source member resolves as null
5 | ///
6 | ///
7 | /// Must be used in combination with
8 | ///
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public sealed class NullSubstituteAttribute(object value) : Attribute, IMemberConfigurationProvider
11 | {
12 | ///
13 | /// Value to use if source value is null
14 | ///
15 | public object Value { get; } = value;
16 |
17 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
18 | {
19 | memberConfigurationExpression.NullSubstitute(Value);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/SourceMemberAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Specify the source member to map from. Can only reference a member on the type
5 | ///
6 | ///
7 | /// Must be used in combination with
8 | ///
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public sealed class SourceMemberAttribute(string name) : Attribute, IMemberConfigurationProvider
11 | {
12 | public string Name { get; } = name;
13 |
14 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
15 | {
16 | var destinationMember = memberConfigurationExpression.DestinationMember;
17 | if (destinationMember.Has() || destinationMember.Has())
18 | {
19 | return;
20 | }
21 | memberConfigurationExpression.MapFrom(Name);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/UseExistingValueAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Use the destination value instead of mapping from the source value or creating a new instance
5 | ///
6 | ///
7 | /// Must be used in combination with
8 | ///
9 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
10 | public sealed class UseExistingValueAttribute : Attribute, IMemberConfigurationProvider
11 | {
12 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
13 | {
14 | memberConfigurationExpression.UseDestinationValue();
15 | }
16 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Configuration/Annotations/ValueConverterAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Configuration.Annotations;
2 |
3 | ///
4 | /// Specify a value converter type to convert from the matching source member to the destination member
5 | /// Use with to specify a separate source member to supply to the value converter
6 | ///
7 | ///
8 | /// Must be used in combination with
9 | ///
10 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
11 | public sealed class ValueConverterAttribute(Type type) : Attribute, IMemberConfigurationProvider
12 | {
13 | ///
14 | /// type
15 | ///
16 | public Type Type { get; } = type;
17 |
18 | public void ApplyConfiguration(IMemberConfigurationExpression memberConfigurationExpression)
19 | {
20 | var sourceMemberAttribute = memberConfigurationExpression.DestinationMember.GetCustomAttribute();
21 |
22 | if (sourceMemberAttribute != null)
23 | {
24 | memberConfigurationExpression.ConvertUsing(Type, sourceMemberAttribute.Name);
25 | }
26 | else
27 | {
28 | memberConfigurationExpression.ConvertUsing(Type);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Internal/LockingConcurrentDictionary.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | namespace AutoMapper.Internal;
3 | public readonly struct LockingConcurrentDictionary(Func valueFactory, int capacity = 31)
4 | {
5 | private readonly Func> _valueFactory = key => new(() => valueFactory(key));
6 | private readonly ConcurrentDictionary> _dictionary = new(Environment.ProcessorCount, capacity);
7 | public TValue GetOrAdd(in TKey key) => _dictionary.GetOrAdd(key, _valueFactory).Value;
8 | public bool IsDefault => _dictionary == null;
9 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Internal/MemberPath.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal;
2 | [EditorBrowsable(EditorBrowsableState.Never)]
3 | public readonly record struct MemberPath(MemberInfo[] Members)
4 | {
5 | public static readonly MemberPath Empty = new(Members: []);
6 | public MemberPath(Stack members) : this(members.ToMemberInfos()){}
7 | public MemberInfo Last => Members[^1];
8 | public MemberInfo First => Members[0];
9 | public int Length => Members.Length;
10 | public bool Equals(MemberPath other) => Members.SequenceEqual(other.Members);
11 | public override int GetHashCode()
12 | {
13 | HashCode hashCode = new();
14 | foreach(var member in Members)
15 | {
16 | hashCode.Add(member);
17 | }
18 | return hashCode.ToHashCode();
19 | }
20 | public override string ToString() => string.Join(".", Members.Select(mi => mi.Name));
21 | public bool StartsWith(MemberPath path)
22 | {
23 | if (path.Length > Length)
24 | {
25 | return false;
26 | }
27 | for (int index = 0; index < path.Length; index++)
28 | {
29 | if (Members[index] != path.Members[index])
30 | {
31 | return false;
32 | }
33 | }
34 | return true;
35 | }
36 | public MemberPath Concat(IEnumerable memberInfos) => new([..Members.Concat(memberInfos)]);
37 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/AssignableMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 |
3 | public class AssignableMapper : IObjectMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.DestinationType.IsAssignableFrom(context.SourceType);
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap,
7 | MemberMap memberMap, Expression sourceExpression, Expression destExpression) => sourceExpression;
8 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ConstructorMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class ConstructorMapper : IObjectMapper
3 | {
4 | public bool IsMatch(TypePair context) => GetConstructor(context.SourceType, context.DestinationType) != null;
5 | private static ConstructorInfo GetConstructor(Type sourceType, Type destinationType) =>
6 | destinationType.GetConstructor(TypeExtensions.InstanceFlags, null, [sourceType], null);
7 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
8 | {
9 | var constructor = GetConstructor(sourceExpression.Type, destExpression.Type);
10 | return New(constructor, ToType(sourceExpression, constructor.FirstParameterType()));
11 | }
12 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ConversionOperatorMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class ConversionOperatorMapper : IObjectMapper
3 | {
4 | private readonly string _operatorName;
5 | public ConversionOperatorMapper(string operatorName) => _operatorName = operatorName;
6 | public bool IsMatch(TypePair context) => GetConversionOperator(context.SourceType, context.DestinationType) != null;
7 | private MethodInfo GetConversionOperator(Type sourceType, Type destinationType)
8 | {
9 | foreach (MethodInfo sourceMethod in sourceType.GetMember(_operatorName, MemberTypes.Method, BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy))
10 | {
11 | if (destinationType.IsAssignableFrom(sourceMethod.ReturnType))
12 | {
13 | return sourceMethod;
14 | }
15 | }
16 | return destinationType.GetMethod(_operatorName, TypeExtensions.StaticFlags, null, [sourceType], null);
17 | }
18 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
19 | {
20 | var conversionOperator = GetConversionOperator(sourceExpression.Type, destExpression.Type);
21 | return Call(conversionOperator, ToType(sourceExpression, conversionOperator.FirstParameterType()));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ConvertMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class ConvertMapper : IObjectMapper
3 | {
4 | public static bool IsPrimitive(Type type) => type.IsPrimitive || type == typeof(string) || type == typeof(decimal);
5 | public bool IsMatch(TypePair types) => (types.SourceType == typeof(string) && types.DestinationType == typeof(DateTime)) ||
6 | (IsPrimitive(types.SourceType) && IsPrimitive(types.DestinationType));
7 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap,
8 | MemberMap memberMap, Expression sourceExpression, Expression destExpression)
9 | {
10 | var convertMethod = typeof(Convert).GetMethod("To" + destExpression.Type.Name, [sourceExpression.Type]);
11 | return Call(convertMethod, sourceExpression);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/EnumToEnumMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class EnumToEnumMapper : IObjectMapper
3 | {
4 | private static readonly MethodInfo TryParseMethod = typeof(Enum).StaticGenericMethod("TryParse", parametersCount: 3);
5 | public bool IsMatch(TypePair context) => context.IsEnumToEnum();
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap,
7 | MemberMap memberMap, Expression sourceExpression, Expression destExpression)
8 | {
9 | var destinationType = destExpression.Type;
10 | var sourceToString = Call(sourceExpression, ObjectToString);
11 | var result = Variable(destinationType, "destinationEnumValue");
12 | var ignoreCase = True;
13 | var tryParse = Call(TryParseMethod.MakeGenericMethod(destinationType), sourceToString, ignoreCase, result);
14 | var (variables, statements) = configuration.Scratchpad();
15 | variables.Add(result);
16 | statements.Add(Condition(tryParse, result, Convert(sourceExpression, destinationType)));
17 | return Block(variables, statements);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/KeyValueMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class KeyValueMapper : IObjectMapper
3 | {
4 | public bool IsMatch(TypePair context) => IsKeyValue(context.SourceType) && IsKeyValue(context.DestinationType);
5 | public static bool IsKeyValue(Type type) => type.IsGenericType(typeof(KeyValuePair<,>));
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
7 | {
8 | var sourceArguments = sourceExpression.Type.GenericTypeArguments;
9 | var destinationType = destExpression.Type;
10 | var destinationArguments = destinationType.GenericTypeArguments;
11 | TypePair keys = new(sourceArguments[0], destinationArguments[0]);
12 | TypePair values = new(sourceArguments[1], destinationArguments[1]);
13 | var mapKeys = configuration.MapExpression(profileMap, keys, ExpressionBuilder.Property(sourceExpression, "Key"));
14 | var mapValues = configuration.MapExpression(profileMap, values, ExpressionBuilder.Property(sourceExpression, "Value"));
15 | return New(destinationType.GetConstructor(destinationArguments), mapKeys, mapValues);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/NullableDestinationMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 |
3 | public class NullableDestinationMapper : IObjectMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.DestinationType.IsNullableType();
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) =>
7 | configuration.MapExpression(profileMap, GetAssociatedTypes(sourceExpression.Type, destExpression.Type), sourceExpression, memberMap);
8 | public TypePair? GetAssociatedTypes(TypePair initialTypes) => GetAssociatedTypes(initialTypes.SourceType, initialTypes.DestinationType);
9 | TypePair GetAssociatedTypes(Type sourceType, Type destinationType) => new(sourceType, Nullable.GetUnderlyingType(destinationType));
10 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/NullableSourceMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 |
3 | public class NullableSourceMapper : IObjectMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.SourceType.IsNullableType();
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) =>
7 | configuration.MapExpression(profileMap, GetAssociatedTypes(sourceExpression.Type, destExpression.Type),
8 | ExpressionBuilder.Property(sourceExpression, "Value"), memberMap, destExpression);
9 | public TypePair? GetAssociatedTypes(TypePair initialTypes) => GetAssociatedTypes(initialTypes.SourceType, initialTypes.DestinationType);
10 | TypePair GetAssociatedTypes(Type sourceType, Type destinationType) => new(Nullable.GetUnderlyingType(sourceType), destinationType);
11 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ParseStringMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 |
3 | public class ParseStringMapper : IObjectMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.SourceType == typeof(string) && HasParse(context.DestinationType);
6 | static bool HasParse(Type type) => type == typeof(Guid) || type == typeof(TimeSpan) || type == typeof(DateTimeOffset);
7 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) =>
8 | Call(destExpression.Type.GetMethod("Parse", [typeof(string)]), sourceExpression);
9 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ToStringDictionaryMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class ToStringDictionaryMapper : IObjectMapper
3 | {
4 | private static readonly MethodInfo MembersDictionaryMethodInfo = typeof(ToStringDictionaryMapper).GetStaticMethod(nameof(MembersDictionary));
5 | public bool IsMatch(TypePair context) => typeof(IDictionary).IsAssignableFrom(context.DestinationType);
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression) =>
7 | Call(MembersDictionaryMethodInfo, sourceExpression.ToObject(), Constant(profileMap));
8 | private static Dictionary MembersDictionary(object source, ProfileMap profileMap) =>
9 | profileMap.CreateTypeDetails(source.GetType()).ReadAccessors.ToDictionary(p => p.Name, p => p.GetMemberValue(source));
10 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/ToStringMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 | public class ToStringMapper : IObjectMapper
3 | {
4 | public bool IsMatch(TypePair context) => context.DestinationType == typeof(string);
5 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
6 | {
7 | var sourceType = sourceExpression.Type;
8 | var toStringCall = Call(sourceExpression, ObjectToString);
9 | return sourceType.IsEnum ? StringToEnumMapper.CheckEnumMember(sourceExpression, sourceType, toStringCall) : toStringCall;
10 | }
11 | }
--------------------------------------------------------------------------------
/src/AutoMapper/Mappers/UnderlyingEnumTypeMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.Internal.Mappers;
2 |
3 | public class UnderlyingTypeEnumMapper : IObjectMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.IsEnumToUnderlyingType() || context.IsUnderlyingTypeToEnum();
6 | public Expression MapExpression(IGlobalConfiguration configuration, ProfileMap profileMap,
7 | MemberMap memberMap, Expression sourceExpression, Expression destExpression) => sourceExpression;
8 | }
--------------------------------------------------------------------------------
/src/AutoMapper/PathMap.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper;
2 | [DebuggerDisplay("{DestinationExpression}")]
3 | [EditorBrowsable(EditorBrowsableState.Never)]
4 | public sealed class PathMap(LambdaExpression destinationExpression, MemberPath memberPath, TypeMap typeMap) : MemberMap(typeMap)
5 | {
6 | public PathMap(PathMap pathMap, TypeMap typeMap, IncludedMember includedMember) : this(pathMap.DestinationExpression, pathMap.MemberPath, typeMap)
7 | {
8 | IncludedMember = includedMember.Chain(pathMap.IncludedMember);
9 | Resolver = pathMap.Resolver;
10 | Condition = pathMap.Condition;
11 | Ignored = pathMap.Ignored;
12 | }
13 | public override Type SourceType => Resolver.ResolvedType;
14 | public LambdaExpression DestinationExpression { get; } = destinationExpression;
15 | public MemberPath MemberPath { get; } = memberPath;
16 | public override Type DestinationType => MemberPath.Last.GetMemberType();
17 | public override string DestinationName => MemberPath.ToString();
18 | public override bool CanBeSet => ReflectionHelper.CanBeSet(MemberPath.Last);
19 | public override bool Ignored { get; set; }
20 | public override IncludedMember IncludedMember { get; protected set; }
21 | public override LambdaExpression Condition { get; set; }
22 | }
--------------------------------------------------------------------------------
/src/AutoMapper/QueryableExtensions/ProjectionMappers/AssignableProjectionMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.QueryableExtensions.Impl;
2 | [EditorBrowsable(EditorBrowsableState.Never)]
3 | public class AssignableProjectionMapper : IProjectionMapper
4 | {
5 | public bool IsMatch(TypePair context) => context.DestinationType.IsAssignableFrom(context.SourceType);
6 | public Expression Project(IGlobalConfiguration configuration, in ProjectionRequest request, Expression resolvedSource, LetPropertyMaps letPropertyMaps)
7 | => ToType(resolvedSource, request.DestinationType);
8 | }
--------------------------------------------------------------------------------
/src/AutoMapper/QueryableExtensions/ProjectionMappers/EnumProjectionMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.QueryableExtensions.Impl;
2 | [EditorBrowsable(EditorBrowsableState.Never)]
3 | public class EnumProjectionMapper : IProjectionMapper
4 | {
5 | public Expression Project(IGlobalConfiguration configuration, in ProjectionRequest request, Expression resolvedSource, LetPropertyMaps letPropertyMaps)
6 | => Convert(resolvedSource, request.DestinationType);
7 | public bool IsMatch(TypePair context) => context.IsEnumToEnum() || context.IsUnderlyingTypeToEnum() || context.IsEnumToUnderlyingType();
8 | }
--------------------------------------------------------------------------------
/src/AutoMapper/QueryableExtensions/ProjectionMappers/NullableSourceProjectionMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.QueryableExtensions.Impl;
2 | internal class NullableSourceProjectionMapper : IProjectionMapper
3 | {
4 | public Expression Project(IGlobalConfiguration configuration, in ProjectionRequest request, Expression resolvedSource, LetPropertyMaps letPropertyMaps) =>
5 | Coalesce(resolvedSource, New(request.DestinationType));
6 | public bool IsMatch(TypePair context) =>
7 | context.DestinationType.IsValueType && !context.DestinationType.IsNullableType() && context.SourceType.IsNullableType();
8 | }
--------------------------------------------------------------------------------
/src/AutoMapper/QueryableExtensions/ProjectionMappers/StringProjectionMapper.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.QueryableExtensions.Impl;
2 |
3 | [EditorBrowsable(EditorBrowsableState.Never)]
4 | public class StringProjectionMapper : IProjectionMapper
5 | {
6 | public bool IsMatch(TypePair context) => context.DestinationType == typeof(string);
7 | public Expression Project(IGlobalConfiguration configuration, in ProjectionRequest request, Expression resolvedSource, LetPropertyMaps letPropertyMaps)
8 | => Call(resolvedSource, ObjectToString);
9 | }
--------------------------------------------------------------------------------
/src/Benchmark/BenchEngine.cs:
--------------------------------------------------------------------------------
1 | namespace Benchmark;
2 |
3 | public class BenchEngine
4 | {
5 | private readonly IObjectToObjectMapper _mapper;
6 | private readonly string _mode;
7 |
8 | public BenchEngine(IObjectToObjectMapper mapper, string mode)
9 | {
10 | _mapper = mapper;
11 | _mode = mode;
12 | }
13 |
14 | public void Start()
15 | {
16 | _mapper.Initialize();
17 | _mapper.Map();
18 |
19 | var timer = Stopwatch.StartNew();
20 |
21 | for(int i = 0; i < 1_000_000; i++)
22 | {
23 | _mapper.Map();
24 | }
25 |
26 | timer.Stop();
27 |
28 | Console.WriteLine("{2:D3} ms {0}: - {1}", _mapper.Name, _mode, (int)timer.Elapsed.TotalMilliseconds);
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Benchmark/Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | Exe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Benchmark/HiPerfTimer.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace Benchmark;
4 |
5 | public class HiPerfTimer
6 | {
7 | [DllImport("Kernel32.dll")]
8 | private static extern bool QueryPerformanceCounter(
9 | out long lpPerformanceCount);
10 |
11 | [DllImport("Kernel32.dll")]
12 | private static extern bool QueryPerformanceFrequency(
13 | out long lpFrequency);
14 |
15 | private long _startTime, _stopTime;
16 | private long _freq;
17 |
18 | // Constructor
19 | public HiPerfTimer()
20 | {
21 | _startTime = 0;
22 | _stopTime = 0;
23 |
24 | if (QueryPerformanceFrequency(out _freq) == false)
25 | {
26 | // high-performance counter not supported
27 | throw new Win32Exception();
28 | }
29 | }
30 |
31 | // Start the timer
32 | public void Start()
33 | {
34 | // lets do the waiting threads there work
35 | Thread.Sleep(0);
36 |
37 | QueryPerformanceCounter(out _startTime);
38 | }
39 |
40 | // Stop the timer
41 | public void Stop()
42 | {
43 | QueryPerformanceCounter(out _stopTime);
44 | }
45 |
46 | // Returns the duration of the timer (in seconds)
47 | public double Duration
48 | {
49 | get
50 | {
51 | double d = (_stopTime - _startTime);
52 | return d / _freq;
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Benchmark/IObjectToObjectMapper.cs:
--------------------------------------------------------------------------------
1 | namespace Benchmark;
2 |
3 | public interface IObjectToObjectMapper
4 | {
5 | string Name { get; }
6 | void Initialize();
7 | object Map();
8 | }
--------------------------------------------------------------------------------
/src/Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | using Benchmark.Flattening;
2 |
3 | namespace Benchmark;
4 |
5 | public class Program
6 | {
7 | public static void Main(string[] args)
8 | {
9 | var mappers = new Dictionary
10 | {
11 | { "Flattening", new IObjectToObjectMapper[] { new FlatteningMapper() , new ManualMapper(), } },
12 | { "Ctors", new IObjectToObjectMapper[] { new CtorMapper(), new ManualCtorMapper(), } },
13 | { "Complex", new IObjectToObjectMapper[] { new ComplexTypeMapper(), new ManualComplexTypeMapper() } },
14 | { "Deep", new IObjectToObjectMapper[] { new DeepTypeMapper(), new ManualDeepTypeMapper() } }
15 | };
16 | while (true)
17 | {
18 | foreach (var pair in mappers)
19 | {
20 | foreach (var mapper in pair.Value)
21 | {
22 | new BenchEngine(mapper, pair.Key).Start();
23 | }
24 | }
25 | Console.ReadLine();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/IntegrationTests/AutoMapper.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | $(NoWarn);618
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/IntegrationTests/ProjectionAdvanced.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.IntegrationTests;
2 | public class ProjectionAdvanced : IntegrationTest
3 | {
4 | protected override MapperConfiguration CreateConfiguration() => new(c => c.CreateProjection().Advanced().ForAllMembers(o=>o.Ignore()));
5 | [Fact]
6 | public void Should_work()
7 | {
8 | using var context = new Context();
9 | var dto = ProjectTo(context.Entities).Single();
10 | dto.Id.ShouldBe(0);
11 | dto.Name.ShouldBeNull();
12 | dto.Value.ShouldBeNull();
13 | }
14 | public class Initializer : DropCreateDatabaseAlways
15 | {
16 | protected override void Seed(Context context) => context.Add(new Entity { Name = "name", Value = "value" });
17 | }
18 | public class Context : LocalDbContext
19 | {
20 | public DbSet Entities { get; set; }
21 | }
22 | public class Entity
23 | {
24 | public int Id { get; set; }
25 | public string Name { get; set; }
26 | public string Value { get; set; }
27 | }
28 | public class Dto
29 | {
30 | public int Id { get; set; }
31 | public string Name { get; set; }
32 | public string Value { get; set; }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/TestApp/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyConfiguration("")]
9 | [assembly: AssemblyCompany("")]
10 | [assembly: AssemblyProduct("TestApp")]
11 | [assembly: AssemblyTrademark("")]
12 |
13 | // Setting ComVisible to false makes the types in this assembly not visible
14 | // to COM components. If you need to access a type in this assembly from
15 | // COM, set the ComVisible attribute to true on that type.
16 | [assembly: ComVisible(false)]
17 |
18 | // The following GUID is for the ID of the typelib if this project is exposed to COM
19 | [assembly: Guid("de95f633-80b5-4248-a594-7fb357c8dac9")]
20 |
--------------------------------------------------------------------------------
/src/TestApp/TestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | TestApp
6 | Exe
7 | enable
8 | enable
9 | TestApp
10 | false
11 | false
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/UnitTests/AddProfiles.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.UnitTests;
2 |
3 | public class AddProfiles : AutoMapperSpecBase
4 | {
5 | public class Source { }
6 | public class Dest { }
7 | public class ForwardProfile : Profile
8 | {
9 | public ForwardProfile() => CreateMap();
10 | }
11 | public class ReverseProfile : Profile
12 | {
13 | public ReverseProfile() => CreateMap();
14 | }
15 | protected override MapperConfiguration CreateConfiguration() => new(c => c.AddProfiles(new Profile[] { new ForwardProfile(), new ReverseProfile() }));
16 | [Fact]
17 | public void Should_not_throw_when_loading_multiple_profiles() => GetProfiles().Count().ShouldBe(3); // default plus two specifically added
18 | }
--------------------------------------------------------------------------------
/src/UnitTests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/UnitTests/AssertionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace AutoMapper.UnitTests;
2 |
3 | public static class AssertionExtensions
4 | {
5 | public static void ShouldContain(this IEnumerable items, object item)
6 | => ShouldBeEnumerableTestExtensions.ShouldContain(items.Cast