From e227ac69dab1e6ed2ca5ce129b96af37e64914cb Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 13:50:10 +0100 Subject: [PATCH 01/17] Renames --- .../CustomDataSourceTargetMemberSpecifier.cs | 2 +- ...ICustomDataSourceTargetMemberSpecifier.cs} | 2 +- .../Configuration/IRootMappingConfigurator.cs | 18 ++++++++-------- .../Api/Configuration/MappingConfigurator.cs | 10 ++++----- ...DataSource.cs => ComplexTypeDataSource.cs} | 6 +++--- ...gDataSource.cs => EnumerableDataSource.cs} | 4 ++-- .../ConfiguredDataSourceFactory.cs} | 6 +++--- .../DataSourceFindContext.cs | 6 +++--- .../DataSourceSetFactory.cs} | 21 ++++++++++--------- .../Factories/IDataSourceFactory.cs | 9 ++++++++ .../MaptimeDataSourceFactory.cs} | 7 +++---- .../MetaMemberDataSourceFactory.cs} | 9 ++++---- .../SourceMemberDataSourceFactory.cs} | 8 +++---- .../DataSources/Finders/IDataSourceFinder.cs | 9 -------- .../Population/MemberPopulatorFactory.cs | 4 ++-- .../ComplexTypeConstructionFactory.cs | 4 ++-- 16 files changed, 62 insertions(+), 63 deletions(-) rename AgileMapper/Api/Configuration/{ICustomMappingDataSourceTargetMemberSpecifier.cs => ICustomDataSourceTargetMemberSpecifier.cs} (98%) rename AgileMapper/DataSources/{ComplexTypeMappingDataSource.cs => ComplexTypeDataSource.cs} (88%) rename AgileMapper/DataSources/{EnumerableMappingDataSource.cs => EnumerableDataSource.cs} (98%) rename AgileMapper/DataSources/{Finders/ConfiguredDataSourceFinder.cs => Factories/ConfiguredDataSourceFactory.cs} (73%) rename AgileMapper/DataSources/{Finders => Factories}/DataSourceFindContext.cs (91%) rename AgileMapper/DataSources/{Finders/DataSourceFinder.cs => Factories/DataSourceSetFactory.cs} (60%) create mode 100644 AgileMapper/DataSources/Factories/IDataSourceFactory.cs rename AgileMapper/DataSources/{Finders/MaptimeDataSourceFinder.cs => Factories/MaptimeDataSourceFactory.cs} (87%) rename AgileMapper/DataSources/{Finders/MetaMemberDataSourceFinder.cs => Factories/MetaMemberDataSourceFactory.cs} (98%) rename AgileMapper/DataSources/{Finders/SourceMemberDataSourceFinder.cs => Factories/SourceMemberDataSourceFactory.cs} (91%) delete mode 100644 AgileMapper/DataSources/Finders/IDataSourceFinder.cs diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index 4eb2ed3b5..bc4425731 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -24,7 +24,7 @@ #endif internal class CustomDataSourceTargetMemberSpecifier : - ICustomMappingDataSourceTargetMemberSpecifier, + ICustomDataSourceTargetMemberSpecifier, ICustomProjectionDataSourceTargetMemberSpecifier { private readonly MappingConfigInfo _configInfo; diff --git a/AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/ICustomDataSourceTargetMemberSpecifier.cs similarity index 98% rename from AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs rename to AgileMapper/Api/Configuration/ICustomDataSourceTargetMemberSpecifier.cs index b4fe2f6ad..5c7a0b03f 100644 --- a/AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/ICustomDataSourceTargetMemberSpecifier.cs @@ -8,7 +8,7 @@ /// /// The source type to which the configuration should apply. /// The target type to which the configuration should apply. - public interface ICustomMappingDataSourceTargetMemberSpecifier + public interface ICustomDataSourceTargetMemberSpecifier { /// /// Apply the configuration to the given . diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index 240926c36..e7ac14641 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -127,7 +127,7 @@ ICustomDataSourceMappingConfigContinuation Map - ICustomMappingDataSourceTargetMemberSpecifier Map( + ICustomDataSourceTargetMemberSpecifier Map( Expression, TSourceValue>> valueFactoryExpression); /// @@ -138,10 +138,10 @@ ICustomMappingDataSourceTargetMemberSpecifier MapThe type of the custom value being configured. /// The expression to map to the configured target member. /// - /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// An ICustomDataSourceTargetMemberSpecifier with which to specify the target member to which the /// custom value should be applied. /// - ICustomMappingDataSourceTargetMemberSpecifier Map( + ICustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression); /// @@ -152,10 +152,10 @@ ICustomMappingDataSourceTargetMemberSpecifier MapThe type of the custom value being configured. /// The expression to map to the configured target member. /// - /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// An ICustomDataSourceTargetMemberSpecifier with which to specify the target member to which the /// custom value should be applied. /// - ICustomMappingDataSourceTargetMemberSpecifier Map( + ICustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression); /// @@ -164,10 +164,10 @@ ICustomMappingDataSourceTargetMemberSpecifier MapThe type of value returned by the given Func. /// The Func object to map to the configured target member. /// - /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// An ICustomDataSourceTargetMemberSpecifier with which to specify the target member to which the /// custom value should be applied. /// - ICustomMappingDataSourceTargetMemberSpecifier MapFunc( + ICustomDataSourceTargetMemberSpecifier MapFunc( Func valueFunc); /// @@ -177,10 +177,10 @@ ICustomMappingDataSourceTargetMemberSpecifier MapFuncThe type of the custom constant value being configured. /// The constant value to map to the configured target member. /// - /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// An ICustomDataSourceTargetMemberSpecifier with which to specify the target member to which the /// custom constant value should be applied. /// - ICustomMappingDataSourceTargetMemberSpecifier Map(TSourceValue value); + ICustomDataSourceTargetMemberSpecifier Map(TSourceValue value); /// /// Configure a constant value for the given when mapping from and to the diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 757096467..4f1ab57cc 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -349,7 +349,7 @@ public ICustomDataSourceMappingConfigContinuation Map(valueFactoryExpression).To(targetMember); } - public ICustomMappingDataSourceTargetMemberSpecifier Map( + public ICustomDataSourceTargetMemberSpecifier Map( Expression, TSourceValue>> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); @@ -361,23 +361,23 @@ public ICustomProjectionDataSourceTargetMemberSpecifier Map(valueFactoryExpression); } - public ICustomMappingDataSourceTargetMemberSpecifier Map( + public ICustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public ICustomMappingDataSourceTargetMemberSpecifier Map( + public ICustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public ICustomMappingDataSourceTargetMemberSpecifier MapFunc( + public ICustomDataSourceTargetMemberSpecifier MapFunc( Func valueFunc) => GetConstantValueTargetMemberSpecifier(valueFunc); - public ICustomMappingDataSourceTargetMemberSpecifier Map(TSourceValue value) + public ICustomDataSourceTargetMemberSpecifier Map(TSourceValue value) => GetConstantValueTargetMemberSpecifier(value); public IMappingConfigContinuation Map( diff --git a/AgileMapper/DataSources/ComplexTypeMappingDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs similarity index 88% rename from AgileMapper/DataSources/ComplexTypeMappingDataSource.cs rename to AgileMapper/DataSources/ComplexTypeDataSource.cs index 3b9e1f717..55af1c64c 100644 --- a/AgileMapper/DataSources/ComplexTypeMappingDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -8,9 +8,9 @@ using System.Linq.Expressions; #endif - internal class ComplexTypeMappingDataSource : DataSourceBase + internal class ComplexTypeDataSource : DataSourceBase { - public ComplexTypeMappingDataSource( + public ComplexTypeDataSource( IDataSource complexTypeDataSource, int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) @@ -34,7 +34,7 @@ private static Expression GetMapping( return mapping; } - public ComplexTypeMappingDataSource(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) + public ComplexTypeDataSource(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) : base(complexTypeMappingData.MapperData.SourceMember, GetMapping(dataSourceIndex, complexTypeMappingData)) { } diff --git a/AgileMapper/DataSources/EnumerableMappingDataSource.cs b/AgileMapper/DataSources/EnumerableDataSource.cs similarity index 98% rename from AgileMapper/DataSources/EnumerableMappingDataSource.cs rename to AgileMapper/DataSources/EnumerableDataSource.cs index 939a6b48f..95ece2a2b 100644 --- a/AgileMapper/DataSources/EnumerableMappingDataSource.cs +++ b/AgileMapper/DataSources/EnumerableDataSource.cs @@ -13,9 +13,9 @@ using ObjectPopulation; using ObjectPopulation.Enumerables; - internal class EnumerableMappingDataSource : DataSourceBase + internal class EnumerableDataSource : DataSourceBase { - public EnumerableMappingDataSource( + public EnumerableDataSource( IDataSource sourceEnumerableDataSource, int dataSourceIndex, IChildMemberMappingData enumerableMappingData) diff --git a/AgileMapper/DataSources/Finders/ConfiguredDataSourceFinder.cs b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs similarity index 73% rename from AgileMapper/DataSources/Finders/ConfiguredDataSourceFinder.cs rename to AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs index 7a4f916f6..e63d4d489 100644 --- a/AgileMapper/DataSources/Finders/ConfiguredDataSourceFinder.cs +++ b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs @@ -1,11 +1,11 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using Extensions.Internal; - internal struct ConfiguredDataSourceFinder : IDataSourceFinder + internal struct ConfiguredDataSourceFactory : IDataSourceFactory { - public IEnumerable FindFor(DataSourceFindContext context) + public IEnumerable CreateFor(DataSourceFindContext context) { if (context.ConfiguredDataSources.None()) { diff --git a/AgileMapper/DataSources/Finders/DataSourceFindContext.cs b/AgileMapper/DataSources/Factories/DataSourceFindContext.cs similarity index 91% rename from AgileMapper/DataSources/Finders/DataSourceFindContext.cs rename to AgileMapper/DataSources/Factories/DataSourceFindContext.cs index 96f485294..76d79af8f 100644 --- a/AgileMapper/DataSources/Finders/DataSourceFindContext.cs +++ b/AgileMapper/DataSources/Factories/DataSourceFindContext.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using Extensions; @@ -53,12 +53,12 @@ public IDataSource GetFinalDataSource(IDataSource foundDataSource, IChildMemberM if (UseComplexTypeDataSource(foundDataSource, childTargetMember)) { - return new ComplexTypeMappingDataSource(foundDataSource, DataSourceIndex, mappingData); + return new ComplexTypeDataSource(foundDataSource, DataSourceIndex, mappingData); } if (childTargetMember.IsEnumerable && foundDataSource.SourceMember.IsEnumerable) { - return new EnumerableMappingDataSource(foundDataSource, DataSourceIndex, mappingData); + return new EnumerableDataSource(foundDataSource, DataSourceIndex, mappingData); } return foundDataSource; diff --git a/AgileMapper/DataSources/Finders/DataSourceFinder.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs similarity index 60% rename from AgileMapper/DataSources/Finders/DataSourceFinder.cs rename to AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index e0b08824d..301b13710 100644 --- a/AgileMapper/DataSources/Finders/DataSourceFinder.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -1,20 +1,21 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using System.Linq; using Members; + using ObjectPopulation; - internal struct DataSourceFinder + internal static class DataSourceSetFactory { - private static readonly IDataSourceFinder[] _finders = + private static readonly IDataSourceFactory[] _childDataSourceFactories = { - default(ConfiguredDataSourceFinder), - default(MaptimeDataSourceFinder), - default(SourceMemberDataSourceFinder), - default(MetaMemberDataSourceFinder) + default(ConfiguredDataSourceFactory), + default(MaptimeDataSourceFactory), + default(SourceMemberDataSourceFactory), + default(MetaMemberDataSourceFactory) }; - public static DataSourceSet FindFor(IChildMemberMappingData childMappingData) + public static DataSourceSet CreateFor(IChildMemberMappingData childMappingData) { var findContext = new DataSourceFindContext(childMappingData); var validDataSources = EnumerateDataSources(findContext).ToArray(); @@ -24,9 +25,9 @@ public static DataSourceSet FindFor(IChildMemberMappingData childMappingData) private static IEnumerable EnumerateDataSources(DataSourceFindContext context) { - foreach (var finder in _finders) + foreach (var finder in _childDataSourceFactories) { - foreach (var dataSource in finder.FindFor(context)) + foreach (var dataSource in finder.CreateFor(context)) { if (!dataSource.IsValid) { diff --git a/AgileMapper/DataSources/Factories/IDataSourceFactory.cs b/AgileMapper/DataSources/Factories/IDataSourceFactory.cs new file mode 100644 index 000000000..ff7bcdcd3 --- /dev/null +++ b/AgileMapper/DataSources/Factories/IDataSourceFactory.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories +{ + using System.Collections.Generic; + + internal interface IDataSourceFactory + { + IEnumerable CreateFor(DataSourceFindContext context); + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Finders/MaptimeDataSourceFinder.cs b/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs similarity index 87% rename from AgileMapper/DataSources/Finders/MaptimeDataSourceFinder.cs rename to AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs index fb0a0c944..29609173d 100644 --- a/AgileMapper/DataSources/Finders/MaptimeDataSourceFinder.cs +++ b/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs @@ -1,17 +1,16 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using Extensions.Internal; - - internal struct MaptimeDataSourceFinder : IDataSourceFinder + internal struct MaptimeDataSourceFactory : IDataSourceFactory { private static readonly IMaptimeDataSourceFactory[] _mapTimeDataSourceFactories = { default(DictionaryDataSourceFactory) }; - public IEnumerable FindFor(DataSourceFindContext context) + public IEnumerable CreateFor(DataSourceFindContext context) { if (!UseMaptimeDataSources(context, out var maptimeDataSources)) { diff --git a/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs b/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs similarity index 98% rename from AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs rename to AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs index 6974810d2..162e37790 100644 --- a/AgileMapper/DataSources/Finders/MetaMemberDataSourceFinder.cs +++ b/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System; using System.Collections.Generic; @@ -16,11 +16,10 @@ using ObjectPopulation.Enumerables; using ReadableExpressions.Extensions; using TypeConversion; - using static System.StringComparison; - internal struct MetaMemberDataSourceFinder : IDataSourceFinder + internal struct MetaMemberDataSourceFactory : IDataSourceFactory { - public IEnumerable FindFor(DataSourceFindContext context) + public IEnumerable CreateFor(DataSourceFindContext context) { if (TryGetMetaMemberNameParts(context, out var memberNameParts) && TryGetMetaMember(memberNameParts, context, out var metaMember)) @@ -85,7 +84,7 @@ private static bool TryGetMetaMemberNameParts( default: currentMemberName = memberNamePart + currentMemberName; - if (currentMemberName.StartsWith(NumberOfMetaMemberPart.Name, Ordinal)) + if (currentMemberName.StartsWith(NumberOfMetaMemberPart.Name, StringComparison.Ordinal)) { currentMemberName = currentMemberName.Substring(NumberOfMetaMemberPart.Name.Length); memberNameParts.Add(currentMemberName); diff --git a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs similarity index 91% rename from AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs rename to AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs index 988260d43..346fd4acd 100644 --- a/AgileMapper/DataSources/Finders/SourceMemberDataSourceFinder.cs +++ b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs @@ -1,12 +1,12 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using Extensions.Internal; using Members; - internal struct SourceMemberDataSourceFinder : IDataSourceFinder + internal struct SourceMemberDataSourceFactory : IDataSourceFactory { - public IEnumerable FindFor(DataSourceFindContext context) + public IEnumerable CreateFor(DataSourceFindContext context) { if (context.MapperData.TargetMember.IsCustom) { @@ -24,7 +24,7 @@ public IEnumerable FindFor(DataSourceFindContext context) { if (UseFallbackComplexTypeMappingDataSource(targetMember)) { - yield return new ComplexTypeMappingDataSource(context.DataSourceIndex, context.ChildMappingData); + yield return new ComplexTypeDataSource(context.DataSourceIndex, context.ChildMappingData); } } else if (configuredDataSources.Any() && configuredDataSources.Last().IsConditional) diff --git a/AgileMapper/DataSources/Finders/IDataSourceFinder.cs b/AgileMapper/DataSources/Finders/IDataSourceFinder.cs deleted file mode 100644 index 658e11e98..000000000 --- a/AgileMapper/DataSources/Finders/IDataSourceFinder.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.DataSources.Finders -{ - using System.Collections.Generic; - - internal interface IDataSourceFinder - { - IEnumerable FindFor(DataSourceFindContext context); - } -} \ No newline at end of file diff --git a/AgileMapper/Members/Population/MemberPopulatorFactory.cs b/AgileMapper/Members/Population/MemberPopulatorFactory.cs index 74e2ef23b..03a788356 100644 --- a/AgileMapper/Members/Population/MemberPopulatorFactory.cs +++ b/AgileMapper/Members/Population/MemberPopulatorFactory.cs @@ -8,7 +8,7 @@ namespace AgileObjects.AgileMapper.Members.Population using System.Linq.Expressions; #endif using Configuration; - using DataSources.Finders; + using DataSources.Factories; using Extensions; using Extensions.Internal; using Members; @@ -66,7 +66,7 @@ private static IMemberPopulator Create(QualifiedMember targetMember, IObjectMapp } var childMappingData = mappingData.GetChildMappingData(childMapperData); - var dataSources = DataSourceFinder.FindFor(childMappingData); + var dataSources = DataSourceSetFactory.CreateFor(childMappingData); if (dataSources.None) { diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs index 77c60c13f..a27e5ccae 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs @@ -12,7 +12,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using Caching; using Configuration; using DataSources; - using DataSources.Finders; + using DataSources.Factories; using Extensions; using Extensions.Internal; using MapperKeys; @@ -359,7 +359,7 @@ private static DataSourceSet[] GetArgumentDataSources(TInvokable invokable, Cons key.MappingData.MapperData); var memberMappingData = key.MappingData.GetChildMappingData(parameterMapperData); - var dataSources = DataSourceFinder.FindFor(memberMappingData); + var dataSources = DataSourceSetFactory.CreateFor(memberMappingData); return dataSources; }); From ed1704d91929c51dde2747ce0554f22e90d45ae4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 14:21:58 +0100 Subject: [PATCH 02/17] Creating DataSourceSets to generate root mapping Expressions --- .../Factories/DataSourceSetFactory.cs | 20 ++++++++++ ...ComplexTypeMappingRootDataSourceFactory.cs | 15 ++++++++ .../DictionaryMappingRootDataSourceFactory.cs | 38 +++++++++++++++++++ .../EnumMappingRootDataSourceFactory.cs | 17 +++++++++ .../EnumerableMappingRootDataSourceFactory.cs | 16 ++++++++ .../IMappingRootDataSourceFactory.cs | 11 ++++++ .../MappingRootDataSourceFactoryBase.cs | 23 +++++++++++ .../QueryProjectionRootDataSourceFactory.cs | 24 ++++++++++++ .../ObjectPopulation/MappingFactory.cs | 2 - .../ObjectPopulation/ObjectMapperFactory.cs | 7 ++-- 10 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/IMappingRootDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs create mode 100644 AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs diff --git a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index 301b13710..bfb772455 100644 --- a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -2,11 +2,21 @@ { using System.Collections.Generic; using System.Linq; + using MappingRoot; using Members; using ObjectPopulation; internal static class DataSourceSetFactory { + private static readonly IMappingRootDataSourceFactory[] _mappingRootDataSourceFactories = + { + new QueryProjectionRootDataSourceFactory(), + new EnumMappingRootDataSourceFactory(), + new DictionaryMappingRootDataSourceFactory(), + new EnumerableMappingRootDataSourceFactory(), + new ComplexTypeMappingRootDataSourceFactory() + }; + private static readonly IDataSourceFactory[] _childDataSourceFactories = { default(ConfiguredDataSourceFactory), @@ -15,6 +25,16 @@ internal static class DataSourceSetFactory default(MetaMemberDataSourceFactory) }; + public static DataSourceSet CreateFor(IObjectMappingData rootMappingData) + { + var rootDataSourceFactory = _mappingRootDataSourceFactories + .First(mef => mef.IsFor(rootMappingData)); + + var rootDataSource = rootDataSourceFactory.CreateFor(rootMappingData); + + return new DataSourceSet(rootMappingData.MapperData, rootDataSource); + } + public static DataSourceSet CreateFor(IChildMemberMappingData childMappingData) { var findContext = new DataSourceFindContext(childMappingData); diff --git a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs new file mode 100644 index 000000000..436f75cbb --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using ObjectPopulation; + using ObjectPopulation.ComplexTypes; + + internal class ComplexTypeMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + { + public ComplexTypeMappingRootDataSourceFactory() + : base(ComplexTypeMappingExpressionFactory.Instance) + { + } + + public bool IsFor(IObjectMappingData mappingData) => true; + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs new file mode 100644 index 000000000..9fd31428c --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using Members.Dictionaries; + using ObjectPopulation; + + internal class DictionaryMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + { + public DictionaryMappingRootDataSourceFactory() + : base(DictionaryMappingExpressionFactory.Instance) + { + } + + public bool IsFor(IObjectMappingData mappingData) + { + if (mappingData.MapperData.TargetMember.IsDictionary) + { + return true; + } + + if (mappingData.IsRoot) + { + return false; + } + + if (!(mappingData.MapperData.TargetMember is DictionaryTargetMember dictionaryMember)) + { + return false; + } + + if (dictionaryMember.HasSimpleEntries) + { + return true; + } + + return dictionaryMember.HasObjectEntries && !mappingData.IsStandalone(); + } + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs new file mode 100644 index 000000000..77445a4db --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs @@ -0,0 +1,17 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using NetStandardPolyfills; + using ObjectPopulation; + using ReadableExpressions.Extensions; + + internal class EnumMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + { + public EnumMappingRootDataSourceFactory() + : base(EnumMappingExpressionFactory.Instance) + { + } + + public bool IsFor(IObjectMappingData mappingData) + => mappingData.MapperData.TargetType.GetNonNullableType().IsEnum(); + } +} diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs new file mode 100644 index 000000000..a598292d4 --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using ObjectPopulation; + using ObjectPopulation.Enumerables; + + internal class EnumerableMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + { + public EnumerableMappingRootDataSourceFactory() + : base(EnumerableMappingExpressionFactory.Instance) + { + } + + public bool IsFor(IObjectMappingData mappingData) + => mappingData.MapperData.TargetMember.IsEnumerable; + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/IMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/IMappingRootDataSourceFactory.cs new file mode 100644 index 000000000..7da3cf766 --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/IMappingRootDataSourceFactory.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using ObjectPopulation; + + internal interface IMappingRootDataSourceFactory + { + bool IsFor(IObjectMappingData mappingData); + + IDataSource CreateFor(IObjectMappingData mappingData); + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs b/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs new file mode 100644 index 000000000..99f42b150 --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using ObjectPopulation; + + internal abstract class MappingRootDataSourceFactoryBase + { + private readonly MappingExpressionFactoryBase _mappingExpressionFactory; + + protected MappingRootDataSourceFactoryBase(MappingExpressionFactoryBase mappingExpressionFactory) + { + _mappingExpressionFactory = mappingExpressionFactory; + } + + public IDataSource CreateFor(IObjectMappingData mappingData) + { + var mappingExpression = _mappingExpressionFactory.Create(mappingData); + + return new AdHocDataSource( + mappingData.MapperData.SourceMember, + mappingExpression); + } + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs new file mode 100644 index 000000000..767944378 --- /dev/null +++ b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot +{ + using Extensions.Internal; + using ObjectPopulation; + using Queryables; + + internal class QueryProjectionRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + { + public QueryProjectionRootDataSourceFactory() + : base(QueryProjectionExpressionFactory.Instance) + { + } + + public bool IsFor(IObjectMappingData mappingData) + { + var mapperData = mappingData.MapperData; + + return mapperData.IsRoot && + mapperData.TargetMember.IsEnumerable && + (mappingData.MappingContext.RuleSet.Name == Constants.Project) && + mapperData.SourceType.IsQueryable(); + } + } +} diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 7ce36d73a..9e6ec279e 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -249,8 +249,6 @@ private static bool ShouldUseLocalSourceValueVariable( SourceAccessCounter.MultipleAccessesExist(sourceValue, mapping); } - - public static Expression UseLocalSourceValueVariableIfAppropriate( Expression mappingExpression, ObjectMapperData mapperData) diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index be8a20a6a..805d5648e 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -4,8 +4,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Collections.Generic; using Caching; using ComplexTypes; + using DataSources.Factories; using Enumerables; - using Extensions.Internal; using MapperKeys; using Queryables; #if NET35 @@ -65,8 +65,9 @@ public ObjectMapper GetOrCreateRoot(ObjectMa public ObjectMapper Create(ObjectMappingData mappingData) { - var mappingExpressionFactory = _mappingExpressionFactories.First(mef => mef.IsFor(mappingData)); - var mappingExpression = mappingExpressionFactory.Create(mappingData); + var mappingExpression = DataSourceSetFactory + .CreateFor(mappingData) + .ValueExpression; if (mappingExpression.NodeType == ExpressionType.Default) { From bb89d2efa7570bf7b9c73553d370d81e623a3c0b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 14:25:20 +0100 Subject: [PATCH 03/17] Removing unused MappingExpressionFactory code --- .../ComplexTypeMappingExpressionFactory.cs | 2 -- .../DictionaryMappingExpressionFactory.cs | 27 +------------------ .../EnumMappingExpressionFactory.cs | 6 +---- .../EnumerableMappingExpressionFactory.cs | 7 ++--- .../MappingExpressionFactoryBase.cs | 2 -- .../ObjectPopulation/ObjectMapperFactory.cs | 23 +++------------- .../QueryProjectionExpressionFactory.cs | 10 ------- 7 files changed, 8 insertions(+), 69 deletions(-) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index a6cd8fbc2..443496f2b 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -30,8 +30,6 @@ private ComplexTypeMappingExpressionFactory() }; } - public override bool IsFor(IObjectMappingData mappingData) => true; - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { if (mappingData.MapperData.TargetCouldBePopulated()) diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index f84553f4c..7ca6277c7 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -228,31 +228,6 @@ private static QualifiedMember[] GetConfiguredTargetMembers( #endregion - public override bool IsFor(IObjectMappingData mappingData) - { - if (mappingData.MapperData.TargetMember.IsDictionary) - { - return true; - } - - if (mappingData.IsRoot) - { - return false; - } - - if (!(mappingData.MapperData.TargetMember is DictionaryTargetMember dictionaryMember)) - { - return false; - } - - if (dictionaryMember.HasSimpleEntries) - { - return true; - } - - return dictionaryMember.HasObjectEntries && !mappingData.IsStandalone(); - } - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { if (mappingData.MappingTypes.SourceType.IsDictionary()) @@ -300,7 +275,7 @@ protected override IEnumerable GetObjectPopulation(MappingCreationCo yield return assignment; } - ReturnPopulation: + ReturnPopulation: if (population != null) { yield return population; diff --git a/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs index 85a3497cf..a35a88024 100644 --- a/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs @@ -2,23 +2,19 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System.Collections.Generic; using System.Globalization; - using Extensions.Internal; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; using Members; - using NetStandardPolyfills; using ReadableExpressions.Extensions; internal class EnumMappingExpressionFactory : MappingExpressionFactoryBase { public static readonly EnumMappingExpressionFactory Instance = new EnumMappingExpressionFactory(); - public override bool IsFor(IObjectMappingData mappingData) - => mappingData.MapperData.TargetType.GetNonNullableType().IsEnum(); - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs index e368156ca..fb5ca95c0 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs @@ -1,21 +1,18 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables { using System.Collections.Generic; - using Extensions.Internal; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using Members; internal class EnumerableMappingExpressionFactory : MappingExpressionFactoryBase { public static readonly MappingExpressionFactoryBase Instance = new EnumerableMappingExpressionFactory(); - public override bool IsFor(IObjectMappingData mappingData) - => mappingData.MapperData.TargetMember.IsEnumerable; - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index a73872320..7db65db6d 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -23,8 +23,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal abstract class MappingExpressionFactoryBase { - public abstract bool IsFor(IObjectMappingData mappingData); - public Expression Create(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index 805d5648e..2374f3673 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -2,35 +2,22 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System; using System.Collections.Generic; - using Caching; - using ComplexTypes; - using DataSources.Factories; - using Enumerables; - using MapperKeys; - using Queryables; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Caching; + using DataSources.Factories; + using MapperKeys; internal class ObjectMapperFactory { - private readonly IList _mappingExpressionFactories; private readonly ICache _rootMappersCache; private Dictionary> _creationCallbacksByKey; public ObjectMapperFactory(CacheSet mapperScopedCacheSet) { - _mappingExpressionFactories = new[] - { - QueryProjectionExpressionFactory.Instance, - EnumMappingExpressionFactory.Instance, - DictionaryMappingExpressionFactory.Instance, - EnumerableMappingExpressionFactory.Instance, - ComplexTypeMappingExpressionFactory.Instance - }; - _rootMappersCache = mapperScopedCacheSet.CreateScoped(default(RootMapperKeyComparer)); } @@ -65,9 +52,7 @@ public ObjectMapper GetOrCreateRoot(ObjectMa public ObjectMapper Create(ObjectMappingData mappingData) { - var mappingExpression = DataSourceSetFactory - .CreateFor(mappingData) - .ValueExpression; + var mappingExpression = DataSourceSetFactory.CreateFor(mappingData).ValueExpression; if (mappingExpression.NodeType == ExpressionType.Default) { diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index eca899539..9feea84b8 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -13,16 +13,6 @@ internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase { public static readonly MappingExpressionFactoryBase Instance = new QueryProjectionExpressionFactory(); - public override bool IsFor(IObjectMappingData mappingData) - { - var mapperData = mappingData.MapperData; - - return mapperData.IsRoot && - mapperData.TargetMember.IsEnumerable && - (mappingData.MappingContext.RuleSet.Name == Constants.Project) && - mapperData.SourceType.IsQueryable(); - } - protected override IEnumerable GetObjectPopulation(MappingCreationContext context) { var mapperData = context.MapperData; From dae6c86f6edbabf89279ee55201cdeedaeb995f9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 15:21:19 +0100 Subject: [PATCH 04/17] Removing ExpressionFactory singleton instances --- .../MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs | 2 +- .../MappingRoot/DictionaryMappingRootDataSourceFactory.cs | 2 +- .../Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs | 2 +- .../MappingRoot/EnumerableMappingRootDataSourceFactory.cs | 2 +- .../MappingRoot/QueryProjectionRootDataSourceFactory.cs | 2 +- .../ComplexTypes/ComplexTypeMappingExpressionFactory.cs | 4 +--- .../ObjectPopulation/DictionaryMappingExpressionFactory.cs | 4 +--- AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs | 2 -- .../Enumerables/EnumerableMappingExpressionFactory.cs | 2 -- AgileMapper/Queryables/QueryProjectionExpressionFactory.cs | 2 -- 10 files changed, 7 insertions(+), 17 deletions(-) diff --git a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs index 436f75cbb..ccbc9d9c5 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs @@ -6,7 +6,7 @@ internal class ComplexTypeMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory { public ComplexTypeMappingRootDataSourceFactory() - : base(ComplexTypeMappingExpressionFactory.Instance) + : base(new ComplexTypeMappingExpressionFactory()) { } diff --git a/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs index 9fd31428c..c32fdfd74 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs @@ -6,7 +6,7 @@ internal class DictionaryMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory { public DictionaryMappingRootDataSourceFactory() - : base(DictionaryMappingExpressionFactory.Instance) + : base(new DictionaryMappingExpressionFactory()) { } diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs index 77445a4db..73e0ac33f 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs @@ -7,7 +7,7 @@ internal class EnumMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory { public EnumMappingRootDataSourceFactory() - : base(EnumMappingExpressionFactory.Instance) + : base(new EnumMappingExpressionFactory()) { } diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs index a598292d4..3664b5307 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs @@ -6,7 +6,7 @@ internal class EnumerableMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory { public EnumerableMappingRootDataSourceFactory() - : base(EnumerableMappingExpressionFactory.Instance) + : base(new EnumerableMappingExpressionFactory()) { } diff --git a/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs index 767944378..d233403a2 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs @@ -7,7 +7,7 @@ internal class QueryProjectionRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory { public QueryProjectionRootDataSourceFactory() - : base(QueryProjectionExpressionFactory.Instance) + : base(new QueryProjectionExpressionFactory()) { } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 443496f2b..a5da806b4 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -13,13 +13,11 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes internal class ComplexTypeMappingExpressionFactory : MappingExpressionFactoryBase { - public static readonly MappingExpressionFactoryBase Instance = new ComplexTypeMappingExpressionFactory(); - private readonly PopulationExpressionFactoryBase _memberInitPopulationFactory; private readonly PopulationExpressionFactoryBase _multiStatementPopulationFactory; private readonly IList _shortCircuitFactories; - private ComplexTypeMappingExpressionFactory() + public ComplexTypeMappingExpressionFactory() { _memberInitPopulationFactory = new MemberInitPopulationExpressionFactory(); _multiStatementPopulationFactory = new MultiStatementPopulationExpressionFactory(); diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index 7ca6277c7..eb926317e 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -22,11 +22,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal class DictionaryMappingExpressionFactory : MappingExpressionFactoryBase { - public static readonly MappingExpressionFactoryBase Instance = new DictionaryMappingExpressionFactory(); - private readonly MemberPopulatorFactory _memberPopulatorFactory; - private DictionaryMappingExpressionFactory() + public DictionaryMappingExpressionFactory() { _memberPopulatorFactory = new MemberPopulatorFactory(GetAllTargetMembers); } diff --git a/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs index a35a88024..90e680005 100644 --- a/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/EnumMappingExpressionFactory.cs @@ -13,8 +13,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal class EnumMappingExpressionFactory : MappingExpressionFactoryBase { - public static readonly EnumMappingExpressionFactory Instance = new EnumMappingExpressionFactory(); - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs index fb5ca95c0..b4d324b86 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs @@ -11,8 +11,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables internal class EnumerableMappingExpressionFactory : MappingExpressionFactoryBase { - public static readonly MappingExpressionFactoryBase Instance = new EnumerableMappingExpressionFactory(); - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out string reason) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 9feea84b8..168d6213e 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -11,8 +11,6 @@ internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase { - public static readonly MappingExpressionFactoryBase Instance = new QueryProjectionExpressionFactory(); - protected override IEnumerable GetObjectPopulation(MappingCreationContext context) { var mapperData = context.MapperData; From 9ddcb9ca0b8f769cd2bdcd88e095a303c7e7d2d5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 16:03:52 +0100 Subject: [PATCH 05/17] Tidying --- .../MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs | 4 ++-- .../MappingRoot/DictionaryMappingRootDataSourceFactory.cs | 4 ++-- .../MappingRoot/EnumMappingRootDataSourceFactory.cs | 4 ++-- .../MappingRoot/EnumerableMappingRootDataSourceFactory.cs | 4 ++-- .../MappingRoot/MappingRootDataSourceFactoryBase.cs | 4 +++- .../MappingRoot/QueryProjectionRootDataSourceFactory.cs | 6 +++--- AgileMapper/ObjectPopulation/ObjectMapperFactory.cs | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs index ccbc9d9c5..c1e4aa6c6 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs @@ -3,13 +3,13 @@ using ObjectPopulation; using ObjectPopulation.ComplexTypes; - internal class ComplexTypeMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + internal class ComplexTypeMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase { public ComplexTypeMappingRootDataSourceFactory() : base(new ComplexTypeMappingExpressionFactory()) { } - public bool IsFor(IObjectMappingData mappingData) => true; + public override bool IsFor(IObjectMappingData mappingData) => true; } } \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs index c32fdfd74..72bff05a0 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/DictionaryMappingRootDataSourceFactory.cs @@ -3,14 +3,14 @@ using Members.Dictionaries; using ObjectPopulation; - internal class DictionaryMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + internal class DictionaryMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase { public DictionaryMappingRootDataSourceFactory() : base(new DictionaryMappingExpressionFactory()) { } - public bool IsFor(IObjectMappingData mappingData) + public override bool IsFor(IObjectMappingData mappingData) { if (mappingData.MapperData.TargetMember.IsDictionary) { diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs index 73e0ac33f..a48f25231 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumMappingRootDataSourceFactory.cs @@ -4,14 +4,14 @@ using ObjectPopulation; using ReadableExpressions.Extensions; - internal class EnumMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + internal class EnumMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase { public EnumMappingRootDataSourceFactory() : base(new EnumMappingExpressionFactory()) { } - public bool IsFor(IObjectMappingData mappingData) + public override bool IsFor(IObjectMappingData mappingData) => mappingData.MapperData.TargetType.GetNonNullableType().IsEnum(); } } diff --git a/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs index 3664b5307..d775bcbbb 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/EnumerableMappingRootDataSourceFactory.cs @@ -3,14 +3,14 @@ using ObjectPopulation; using ObjectPopulation.Enumerables; - internal class EnumerableMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + internal class EnumerableMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase { public EnumerableMappingRootDataSourceFactory() : base(new EnumerableMappingExpressionFactory()) { } - public bool IsFor(IObjectMappingData mappingData) + public override bool IsFor(IObjectMappingData mappingData) => mappingData.MapperData.TargetMember.IsEnumerable; } } \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs b/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs index 99f42b150..d4ab60814 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/MappingRootDataSourceFactoryBase.cs @@ -2,7 +2,7 @@ { using ObjectPopulation; - internal abstract class MappingRootDataSourceFactoryBase + internal abstract class MappingRootDataSourceFactoryBase : IMappingRootDataSourceFactory { private readonly MappingExpressionFactoryBase _mappingExpressionFactory; @@ -11,6 +11,8 @@ protected MappingRootDataSourceFactoryBase(MappingExpressionFactoryBase mappingE _mappingExpressionFactory = mappingExpressionFactory; } + public abstract bool IsFor(IObjectMappingData mappingData); + public IDataSource CreateFor(IObjectMappingData mappingData) { var mappingExpression = _mappingExpressionFactory.Create(mappingData); diff --git a/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs index d233403a2..ee41f5e55 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/QueryProjectionRootDataSourceFactory.cs @@ -4,20 +4,20 @@ using ObjectPopulation; using Queryables; - internal class QueryProjectionRootDataSourceFactory : MappingRootDataSourceFactoryBase, IMappingRootDataSourceFactory + internal class QueryProjectionRootDataSourceFactory : MappingRootDataSourceFactoryBase { public QueryProjectionRootDataSourceFactory() : base(new QueryProjectionExpressionFactory()) { } - public bool IsFor(IObjectMappingData mappingData) + public override bool IsFor(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; return mapperData.IsRoot && mapperData.TargetMember.IsEnumerable && - (mappingData.MappingContext.RuleSet.Name == Constants.Project) && + (mappingData.MappingContext.RuleSet.Name == Constants.Project) && mapperData.SourceType.IsQueryable(); } } diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index 2374f3673..11eda902c 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -54,7 +54,7 @@ public ObjectMapper Create(ObjectMappingData { var mappingExpression = DataSourceSetFactory.CreateFor(mappingData).ValueExpression; - if (mappingExpression.NodeType == ExpressionType.Default) + if (mappingExpression == Constants.EmptyExpression) { return null; } From 109a2a6781735f67ef47366d88b5099cccd4526d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Aug 2019 16:12:55 +0100 Subject: [PATCH 06/17] Switching DataSourceFactory to a delegate --- .../Factories/ConfiguredDataSourceFactory.cs | 4 ++-- .../DataSources/Factories/DataSourceFactory.cs | 6 ++++++ .../DataSources/Factories/DataSourceSetFactory.cs | 12 ++++++------ .../DataSources/Factories/IDataSourceFactory.cs | 9 --------- .../Factories/MaptimeDataSourceFactory.cs | 4 ++-- .../Factories/MetaMemberDataSourceFactory.cs | 4 ++-- .../Factories/SourceMemberDataSourceFactory.cs | 4 ++-- 7 files changed, 20 insertions(+), 23 deletions(-) create mode 100644 AgileMapper/DataSources/Factories/DataSourceFactory.cs delete mode 100644 AgileMapper/DataSources/Factories/IDataSourceFactory.cs diff --git a/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs index e63d4d489..bac98be68 100644 --- a/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using Extensions.Internal; - internal struct ConfiguredDataSourceFactory : IDataSourceFactory + internal static class ConfiguredDataSourceFactory { - public IEnumerable CreateFor(DataSourceFindContext context) + public static IEnumerable Create(DataSourceFindContext context) { if (context.ConfiguredDataSources.None()) { diff --git a/AgileMapper/DataSources/Factories/DataSourceFactory.cs b/AgileMapper/DataSources/Factories/DataSourceFactory.cs new file mode 100644 index 000000000..ca0ec81a3 --- /dev/null +++ b/AgileMapper/DataSources/Factories/DataSourceFactory.cs @@ -0,0 +1,6 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories +{ + using System.Collections.Generic; + + internal delegate IEnumerable DataSourceFactory(DataSourceFindContext context); +} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index bfb772455..a44da4351 100644 --- a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -17,12 +17,12 @@ internal static class DataSourceSetFactory new ComplexTypeMappingRootDataSourceFactory() }; - private static readonly IDataSourceFactory[] _childDataSourceFactories = + private static readonly DataSourceFactory[] _childDataSourceFactories = { - default(ConfiguredDataSourceFactory), - default(MaptimeDataSourceFactory), - default(SourceMemberDataSourceFactory), - default(MetaMemberDataSourceFactory) + ConfiguredDataSourceFactory.Create, + MaptimeDataSourceFactory.Create, + SourceMemberDataSourceFactory.Create, + MetaMemberDataSourceFactory.Create }; public static DataSourceSet CreateFor(IObjectMappingData rootMappingData) @@ -47,7 +47,7 @@ private static IEnumerable EnumerateDataSources(DataSourceFindConte { foreach (var finder in _childDataSourceFactories) { - foreach (var dataSource in finder.CreateFor(context)) + foreach (var dataSource in finder.Invoke(context)) { if (!dataSource.IsValid) { diff --git a/AgileMapper/DataSources/Factories/IDataSourceFactory.cs b/AgileMapper/DataSources/Factories/IDataSourceFactory.cs deleted file mode 100644 index ff7bcdcd3..000000000 --- a/AgileMapper/DataSources/Factories/IDataSourceFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.DataSources.Factories -{ - using System.Collections.Generic; - - internal interface IDataSourceFactory - { - IEnumerable CreateFor(DataSourceFindContext context); - } -} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs index 29609173d..85527b3fe 100644 --- a/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs @@ -3,14 +3,14 @@ using System.Collections.Generic; using Extensions.Internal; - internal struct MaptimeDataSourceFactory : IDataSourceFactory + internal static class MaptimeDataSourceFactory { private static readonly IMaptimeDataSourceFactory[] _mapTimeDataSourceFactories = { default(DictionaryDataSourceFactory) }; - public IEnumerable CreateFor(DataSourceFindContext context) + public static IEnumerable Create(DataSourceFindContext context) { if (!UseMaptimeDataSources(context, out var maptimeDataSources)) { diff --git a/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs index 162e37790..20322ef6b 100644 --- a/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs @@ -17,9 +17,9 @@ using ReadableExpressions.Extensions; using TypeConversion; - internal struct MetaMemberDataSourceFactory : IDataSourceFactory + internal static class MetaMemberDataSourceFactory { - public IEnumerable CreateFor(DataSourceFindContext context) + public static IEnumerable Create(DataSourceFindContext context) { if (TryGetMetaMemberNameParts(context, out var memberNameParts) && TryGetMetaMember(memberNameParts, context, out var metaMember)) diff --git a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs index 346fd4acd..ad8da270e 100644 --- a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs @@ -4,9 +4,9 @@ using Extensions.Internal; using Members; - internal struct SourceMemberDataSourceFactory : IDataSourceFactory + internal static class SourceMemberDataSourceFactory { - public IEnumerable CreateFor(DataSourceFindContext context) + public static IEnumerable Create(DataSourceFindContext context) { if (context.MapperData.TargetMember.IsCustom) { From 03f915d6b9ca0adf1314c165276fff9c3fb0e788 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 6 Aug 2019 20:11:50 +0100 Subject: [PATCH 07/17] Adding ComplexTypeDataSource factory methods --- .../DataSources/ComplexTypeDataSource.cs | 59 ++++++++++++------- .../Factories/DataSourceFindContext.cs | 2 +- ...ComplexTypeMappingRootDataSourceFactory.cs | 11 ++-- .../SourceMemberDataSourceFactory.cs | 6 +- .../ComplexTypeMappingExpressionFactory.cs | 2 + 5 files changed, 47 insertions(+), 33 deletions(-) diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index 55af1c64c..9c28ec1b4 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -2,6 +2,7 @@ { using Members; using ObjectPopulation; + using ObjectPopulation.ComplexTypes; #if NET35 using Microsoft.Scripting.Ast; #else @@ -10,46 +11,60 @@ internal class ComplexTypeDataSource : DataSourceBase { - public ComplexTypeDataSource( - IDataSource complexTypeDataSource, - int dataSourceIndex, - IChildMemberMappingData complexTypeMappingData) - : base( - complexTypeDataSource, - GetMapping(complexTypeDataSource, dataSourceIndex, complexTypeMappingData)) + private ComplexTypeDataSource(IDataSource wrappedDataSource, Expression mapping) + : base(wrappedDataSource, mapping) { } - private static Expression GetMapping( - IDataSource complexTypeDataSource, - int dataSourceIndex, - IChildMemberMappingData complexTypeMappingData) + private ComplexTypeDataSource(IQualifiedMember sourceMember, Expression mapping) + : base(sourceMember, mapping) { - var mapping = MappingFactory.GetChildMapping( - complexTypeDataSource.SourceMember, - complexTypeDataSource.Value, - dataSourceIndex, - complexTypeMappingData); - - return mapping; } - public ComplexTypeDataSource(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) - : base(complexTypeMappingData.MapperData.SourceMember, GetMapping(dataSourceIndex, complexTypeMappingData)) + #region Factory Methods + + public static IDataSource Create(IObjectMappingData mappingData) { + var mapping = ComplexTypeMappingExpressionFactory.Instance.Create(mappingData); + + return new ComplexTypeDataSource( + mappingData.MapperData.SourceMember, + mapping); } - private static Expression GetMapping(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) + public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) { var complexTypeMapperData = complexTypeMappingData.MapperData; var relativeMember = complexTypeMapperData.SourceMember.RelativeTo(complexTypeMapperData.SourceMember); var sourceMemberAccess = relativeMember.GetQualifiedAccess(complexTypeMapperData); - return MappingFactory.GetChildMapping( + var mapping = MappingFactory.GetChildMapping( relativeMember, sourceMemberAccess, dataSourceIndex, complexTypeMappingData); + + return new ComplexTypeDataSource( + complexTypeMappingData.MapperData.SourceMember, + mapping); } + + public static IDataSource Create( + IDataSource wrappedDataSource, + int dataSourceIndex, + IChildMemberMappingData complexTypeMappingData) + { + var mapping = MappingFactory.GetChildMapping( + wrappedDataSource.SourceMember, + wrappedDataSource.Value, + dataSourceIndex, + complexTypeMappingData); + + return new ComplexTypeDataSource( + wrappedDataSource, + mapping); + } + + #endregion } } \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/DataSourceFindContext.cs b/AgileMapper/DataSources/Factories/DataSourceFindContext.cs index 76d79af8f..a91b1fe49 100644 --- a/AgileMapper/DataSources/Factories/DataSourceFindContext.cs +++ b/AgileMapper/DataSources/Factories/DataSourceFindContext.cs @@ -53,7 +53,7 @@ public IDataSource GetFinalDataSource(IDataSource foundDataSource, IChildMemberM if (UseComplexTypeDataSource(foundDataSource, childTargetMember)) { - return new ComplexTypeDataSource(foundDataSource, DataSourceIndex, mappingData); + return ComplexTypeDataSource.Create(foundDataSource, DataSourceIndex, mappingData); } if (childTargetMember.IsEnumerable && foundDataSource.SourceMember.IsEnumerable) diff --git a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs index c1e4aa6c6..83536d01b 100644 --- a/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MappingRoot/ComplexTypeMappingRootDataSourceFactory.cs @@ -1,15 +1,12 @@ namespace AgileObjects.AgileMapper.DataSources.Factories.MappingRoot { using ObjectPopulation; - using ObjectPopulation.ComplexTypes; - internal class ComplexTypeMappingRootDataSourceFactory : MappingRootDataSourceFactoryBase + internal class ComplexTypeMappingRootDataSourceFactory : IMappingRootDataSourceFactory { - public ComplexTypeMappingRootDataSourceFactory() - : base(new ComplexTypeMappingExpressionFactory()) - { - } + public bool IsFor(IObjectMappingData mappingData) => true; - public override bool IsFor(IObjectMappingData mappingData) => true; + public IDataSource CreateFor(IObjectMappingData mappingData) + => ComplexTypeDataSource.Create(mappingData); } } \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs index ad8da270e..e9cd96a2c 100644 --- a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs @@ -22,9 +22,9 @@ public static IEnumerable Create(DataSourceFindContext context) { if (context.DataSourceIndex == 0) { - if (UseFallbackComplexTypeMappingDataSource(targetMember)) + if (UseFallbackComplexTypeDataSource(targetMember)) { - yield return new ComplexTypeDataSource(context.DataSourceIndex, context.ChildMappingData); + yield return ComplexTypeDataSource.Create(context.DataSourceIndex, context.ChildMappingData); } } else if (configuredDataSources.Any() && configuredDataSources.Last().IsConditional) @@ -88,7 +88,7 @@ private static IDataSource GetSourceMemberDataSource( Constants.EmptyExpression); } - private static bool UseFallbackComplexTypeMappingDataSource(QualifiedMember targetMember) + private static bool UseFallbackComplexTypeDataSource(QualifiedMember targetMember) => targetMember.IsComplex && !targetMember.IsDictionary && (targetMember.Type != typeof(object)); } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index a5da806b4..2274560a7 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -13,6 +13,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes internal class ComplexTypeMappingExpressionFactory : MappingExpressionFactoryBase { + public static readonly MappingExpressionFactoryBase Instance = new ComplexTypeMappingExpressionFactory(); + private readonly PopulationExpressionFactoryBase _memberInitPopulationFactory; private readonly PopulationExpressionFactoryBase _multiStatementPopulationFactory; private readonly IList _shortCircuitFactories; From b2c2d9f6c666b5cb951343ccd4ca90c1b1aac225 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 6 Aug 2019 20:37:26 +0100 Subject: [PATCH 08/17] Adding child data sources --- .../DataSources/ComplexTypeDataSource.cs | 30 +++++++++---------- AgileMapper/DataSources/DataSourceBase.cs | 2 ++ .../Factories/DataSourceSetFactory.cs | 28 +++++++++++++---- AgileMapper/DataSources/IDataSource.cs | 2 ++ 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index 9c28ec1b4..8f794a3ba 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -32,6 +32,20 @@ public static IDataSource Create(IObjectMappingData mappingData) mapping); } + public static IDataSource Create( + IDataSource wrappedDataSource, + int dataSourceIndex, + IChildMemberMappingData complexTypeMappingData) + { + var mapping = MappingFactory.GetChildMapping( + wrappedDataSource.SourceMember, + wrappedDataSource.Value, + dataSourceIndex, + complexTypeMappingData); + + return new ComplexTypeDataSource(wrappedDataSource, mapping); + } + public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) { var complexTypeMapperData = complexTypeMappingData.MapperData; @@ -49,22 +63,6 @@ public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData co mapping); } - public static IDataSource Create( - IDataSource wrappedDataSource, - int dataSourceIndex, - IChildMemberMappingData complexTypeMappingData) - { - var mapping = MappingFactory.GetChildMapping( - wrappedDataSource.SourceMember, - wrappedDataSource.Value, - dataSourceIndex, - complexTypeMappingData); - - return new ComplexTypeDataSource( - wrappedDataSource, - mapping); - } - #endregion } } \ No newline at end of file diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index 06d27f183..2fa7ab6bb 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -191,6 +191,8 @@ private static bool IsNotOptionalEntityMemberId(IMemberMapperData mapperData) public ICollection Variables { get; } + public virtual IList ChildDataSources => Enumerable.EmptyArray; + public Expression Value { get; } public virtual Expression AddPreCondition(Expression population) => population; diff --git a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index a44da4351..9962725bf 100644 --- a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -47,13 +47,8 @@ private static IEnumerable EnumerateDataSources(DataSourceFindConte { foreach (var finder in _childDataSourceFactories) { - foreach (var dataSource in finder.Invoke(context)) + foreach (var dataSource in EnumerateDataSources(finder.Invoke(context))) { - if (!dataSource.IsValid) - { - continue; - } - yield return dataSource; if (!dataSource.IsConditional) @@ -68,5 +63,26 @@ private static IEnumerable EnumerateDataSources(DataSourceFindConte } } } + + private static IEnumerable EnumerateDataSources(IEnumerable dataSources) + { + foreach (var dataSource in dataSources) + { + if (dataSource.ChildDataSources.Any()) + { + foreach (var childDataSource in EnumerateDataSources(dataSource.ChildDataSources)) + { + yield return childDataSource; + } + } + + if (!dataSource.IsValid) + { + continue; + } + + yield return dataSource; + } + } } } \ No newline at end of file diff --git a/AgileMapper/DataSources/IDataSource.cs b/AgileMapper/DataSources/IDataSource.cs index 98c72d870..34981be98 100644 --- a/AgileMapper/DataSources/IDataSource.cs +++ b/AgileMapper/DataSources/IDataSource.cs @@ -23,6 +23,8 @@ internal interface IDataSource : IConditionallyChainable ICollection Variables { get; } + IList ChildDataSources { get; } + Expression AddPreCondition(Expression population); Expression AddCondition(Expression value, Expression alternateBranch = null); From d6a16a36f0ad45e43eb3f65454d185b4b40474f6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Aug 2019 09:10:41 +0100 Subject: [PATCH 09/17] Start of generating derived type mappings via data sources --- .../DataSources/ComplexTypeDataSource.cs | 42 +++++++++++++++---- .../ObjectPopulation/MappingFactory.cs | 13 +++--- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index 8f794a3ba..8bc14c413 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -1,24 +1,33 @@ namespace AgileObjects.AgileMapper.DataSources { - using Members; - using ObjectPopulation; - using ObjectPopulation.ComplexTypes; + using System.Collections.Generic; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Members; + using ObjectPopulation; + using ObjectPopulation.ComplexTypes; internal class ComplexTypeDataSource : DataSourceBase { - private ComplexTypeDataSource(IDataSource wrappedDataSource, Expression mapping) + private ComplexTypeDataSource( + IDataSource wrappedDataSource, + Expression mapping, + IList childDataSources) : base(wrappedDataSource, mapping) { + ChildDataSources = childDataSources; } - private ComplexTypeDataSource(IQualifiedMember sourceMember, Expression mapping) + private ComplexTypeDataSource( + IQualifiedMember sourceMember, + Expression mapping, + IList childDataSources) : base(sourceMember, mapping) { + ChildDataSources = childDataSources; } #region Factory Methods @@ -29,7 +38,8 @@ public static IDataSource Create(IObjectMappingData mappingData) return new ComplexTypeDataSource( mappingData.MapperData.SourceMember, - mapping); + mapping, + DerivedComplexTypeDataSourcesFactory.CreateFor(mappingData)); } public static IDataSource Create( @@ -43,7 +53,10 @@ public static IDataSource Create( dataSourceIndex, complexTypeMappingData); - return new ComplexTypeDataSource(wrappedDataSource, mapping); + return new ComplexTypeDataSource( + wrappedDataSource, + mapping, + Enumerable.EmptyArray); } public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) @@ -59,10 +72,21 @@ public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData co complexTypeMappingData); return new ComplexTypeDataSource( - complexTypeMappingData.MapperData.SourceMember, - mapping); + complexTypeMappingData.MapperData.SourceMember, + mapping, + Enumerable.EmptyArray); } #endregion + + public override IList ChildDataSources { get; } + } + + internal static class DerivedComplexTypeDataSourcesFactory + { + public static IList CreateFor(IObjectMappingData declaredTypeMappingData) + { + return Enumerable.EmptyArray; + } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 9e6ec279e..05c9215d0 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -18,6 +18,13 @@ public static Expression GetChildMapping( IChildMemberMappingData childMappingData) { var childMapperData = childMappingData.MapperData; + + var childObjectMappingData = ObjectMappingDataFactory.ForChild( + sourceMember, + childMapperData.TargetMember, + dataSourceIndex, + childMappingData.Parent); + var targetMemberAccess = childMapperData.GetTargetMemberAccess(); childMapperData.TargetMember.MapCreating(sourceMember.Type); @@ -27,12 +34,6 @@ public static Expression GetChildMapping( targetMemberAccess, childMapperData.EnumerableIndex); - var childObjectMappingData = ObjectMappingDataFactory.ForChild( - sourceMember, - childMapperData.TargetMember, - dataSourceIndex, - childMappingData.Parent); - if (childObjectMappingData.MappingTypes.RuntimeTypesNeeded) { return childMapperData.Parent.GetRuntimeTypedMapping( From 187c832dce873fabeb6c8e53b6f94b20723254a1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 16 Aug 2019 19:58:36 +0100 Subject: [PATCH 10/17] WIP --- AgileMapper/DataSources/AdHocDataSource.cs | 9 +- .../DataSources/ComplexTypeDataSource.cs | 40 +- AgileMapper/DataSources/DataSourceBase.cs | 8 +- AgileMapper/DataSources/DataSourceSet.cs | 82 +-- .../DerivedComplexTypeDataSourcesFactory.cs | 535 ++++++++++++++++++ .../DataSources/DictionaryEntryDataSource.cs | 2 +- .../DictionaryEntryVariablePair.cs | 2 +- .../Factories/DataSourceSetFactory.cs | 35 +- AgileMapper/DataSources/IDataSource.cs | 6 +- .../DataSources/ValueExpressionBuilders.cs | 75 +++ .../Internal/EnumerableExtensions.cs | 6 + .../Members/Population/MemberPopulator.cs | 26 +- .../ComplexTypeConstructionFactory.cs | 2 +- .../DerivedComplexTypeMappingsFactory.cs | 20 +- .../ObjectPopulation/DerivedMappingFactory.cs | 17 +- .../DerivedSourceTypeCheck.cs | 12 +- .../DictionaryPopulationBuilder.cs | 2 +- .../ObjectPopulation/ObjectMapperFactory.cs | 2 +- 18 files changed, 732 insertions(+), 149 deletions(-) create mode 100644 AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs create mode 100644 AgileMapper/DataSources/ValueExpressionBuilders.cs diff --git a/AgileMapper/DataSources/AdHocDataSource.cs b/AgileMapper/DataSources/AdHocDataSource.cs index 65089290f..a84b3ebab 100644 --- a/AgileMapper/DataSources/AdHocDataSource.cs +++ b/AgileMapper/DataSources/AdHocDataSource.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.DataSources { - using System.Collections.Generic; #if NET35 using Microsoft.Scripting.Ast; #else @@ -27,8 +26,12 @@ public AdHocDataSource( IQualifiedMember sourceMember, Expression value, Expression condition, - ICollection variables = null) - : base(sourceMember, variables ?? Enumerable.EmptyArray, value, condition) + params ParameterExpression[] variables) + : base( + sourceMember, + variables, + value, + condition) { } } diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index 8bc14c413..ea73ebd5c 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.DataSources { - using System.Collections.Generic; #if NET35 using Microsoft.Scripting.Ast; #else @@ -12,34 +11,25 @@ internal class ComplexTypeDataSource : DataSourceBase { - private ComplexTypeDataSource( - IDataSource wrappedDataSource, - Expression mapping, - IList childDataSources) + private ComplexTypeDataSource(IDataSource wrappedDataSource, Expression mapping) : base(wrappedDataSource, mapping) { - ChildDataSources = childDataSources; } - private ComplexTypeDataSource( - IQualifiedMember sourceMember, - Expression mapping, - IList childDataSources) + private ComplexTypeDataSource(IQualifiedMember sourceMember, Expression mapping) : base(sourceMember, mapping) { - ChildDataSources = childDataSources; } #region Factory Methods public static IDataSource Create(IObjectMappingData mappingData) { + var derivedTypeDataSources = DerivedComplexTypeDataSourcesFactory.CreateFor(mappingData); + var mapping = ComplexTypeMappingExpressionFactory.Instance.Create(mappingData); - return new ComplexTypeDataSource( - mappingData.MapperData.SourceMember, - mapping, - DerivedComplexTypeDataSourcesFactory.CreateFor(mappingData)); + return new ComplexTypeDataSource(mappingData.MapperData.SourceMember, mapping); } public static IDataSource Create( @@ -53,10 +43,7 @@ public static IDataSource Create( dataSourceIndex, complexTypeMappingData); - return new ComplexTypeDataSource( - wrappedDataSource, - mapping, - Enumerable.EmptyArray); + return new ComplexTypeDataSource(wrappedDataSource, mapping); } public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData complexTypeMappingData) @@ -71,22 +58,9 @@ public static IDataSource Create(int dataSourceIndex, IChildMemberMappingData co dataSourceIndex, complexTypeMappingData); - return new ComplexTypeDataSource( - complexTypeMappingData.MapperData.SourceMember, - mapping, - Enumerable.EmptyArray); + return new ComplexTypeDataSource(complexTypeMapperData.SourceMember, mapping); } #endregion - - public override IList ChildDataSources { get; } - } - - internal static class DerivedComplexTypeDataSourcesFactory - { - public static IList CreateFor(IObjectMappingData declaredTypeMappingData) - { - return Enumerable.EmptyArray; - } } } \ No newline at end of file diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index 2fa7ab6bb..1eb871da3 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -29,7 +29,7 @@ protected DataSourceBase(IDataSource wrappedDataSource, Expression value) protected DataSourceBase( IQualifiedMember sourceMember, - ICollection variables, + IList variables, Expression value, Expression condition = null) { @@ -189,13 +189,11 @@ private static bool IsNotOptionalEntityMemberId(IMemberMapperData mapperData) public virtual Expression Condition { get; } - public ICollection Variables { get; } - - public virtual IList ChildDataSources => Enumerable.EmptyArray; + public IList Variables { get; } public Expression Value { get; } - public virtual Expression AddPreCondition(Expression population) => population; + public virtual Expression Finalise(Expression population) => population; public Expression AddCondition(Expression value, Expression alternateBranch = null) { diff --git a/AgileMapper/DataSources/DataSourceSet.cs b/AgileMapper/DataSources/DataSourceSet.cs index e52c97457..8fd413f44 100644 --- a/AgileMapper/DataSources/DataSourceSet.cs +++ b/AgileMapper/DataSources/DataSourceSet.cs @@ -1,25 +1,44 @@ namespace AgileObjects.AgileMapper.DataSources { + using System; using System.Collections; using System.Collections.Generic; - using Extensions.Internal; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using Members; internal class DataSourceSet : IEnumerable { private readonly IList _dataSources; + private readonly Func, IMemberMapperData, Expression> _valueBuilder; private Expression _value; - public DataSourceSet(IMemberMapperData mapperData, params IDataSource[] dataSources) + private DataSourceSet( + IDataSource dataSource, + IMemberMapperData mapperData, + Func, IMemberMapperData, Expression> valueBuilder) { + _dataSources = new[] { dataSource }; MapperData = mapperData; + _valueBuilder = valueBuilder ?? ValueExpressionBuilders.SingleDataSource; + HasValue = dataSource.IsValid; + IsConditional = dataSource.IsConditional; + Variables = dataSource.Variables; + SourceMemberTypeTest = dataSource.SourceMemberTypeTest; + } + + private DataSourceSet( + IList dataSources, + IMemberMapperData mapperData, + Func, IMemberMapperData, Expression> valueBuilder) + { _dataSources = dataSources; - None = dataSources.Length == 0; + MapperData = mapperData; + None = dataSources.Count == 0; if (None) { @@ -27,9 +46,11 @@ public DataSourceSet(IMemberMapperData mapperData, params IDataSource[] dataSour return; } + _valueBuilder = valueBuilder ?? ValueExpressionBuilders.ConditionTree; + var variables = default(List); - for (var i = 0; i < dataSources.Length;) + for (var i = 0; i < dataSources.Count;) { var dataSource = dataSources[i++]; @@ -64,6 +85,28 @@ public DataSourceSet(IMemberMapperData mapperData, params IDataSource[] dataSour : Enumerable.EmptyArray; } + #region Factory Methods + + public static DataSourceSet For( + IDataSource dataSource, + IMemberMapperData mapperData, + Func, IMemberMapperData, Expression> valueBuilder = null) + { + return new DataSourceSet(dataSource, mapperData, valueBuilder); + } + + public static DataSourceSet For( + IList dataSources, + IMemberMapperData mapperData, + Func, IMemberMapperData, Expression> valueBuilder = null) + { + return dataSources.HasOne() + ? For(dataSources.First(), mapperData, valueBuilder) + : new DataSourceSet(dataSources, mapperData, valueBuilder); + } + + #endregion + public IMemberMapperData MapperData { get; } public bool None { get; } @@ -80,33 +123,8 @@ public DataSourceSet(IMemberMapperData mapperData, params IDataSource[] dataSour public int Count => _dataSources.Count; - public Expression ValueExpression => _value ?? (_value = BuildValueExpression()); - - private Expression BuildValueExpression() - { - var value = default(Expression); - - for (var i = _dataSources.Count - 1; i >= 0;) - { - var isFirstDataSource = value == default(Expression); - var dataSource = _dataSources[i--]; - - var dataSourceValue = dataSource.IsConditional - ? Expression.Condition( - dataSource.Condition, - isFirstDataSource - ? dataSource.Value - : dataSource.Value.GetConversionTo(value.Type), - isFirstDataSource - ? dataSource.Value.Type.ToDefaultExpression() - : value) - : dataSource.Value; - - value = dataSource.AddPreConditionIfNecessary(dataSourceValue); - } - - return value; - } + public Expression BuildValue() + => _value ?? (_value = _valueBuilder.Invoke(_dataSources, MapperData)); public Expression GetFinalValueOrNull() { diff --git a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs new file mode 100644 index 000000000..eb1fcfe08 --- /dev/null +++ b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs @@ -0,0 +1,535 @@ +namespace AgileObjects.AgileMapper.DataSources +{ + using System; + using System.Collections.Generic; + using System.Linq; +#if NET35 + using Microsoft.Scripting.Ast; + using static Microsoft.Scripting.Ast.Expression; +#else + using System.Linq.Expressions; + using static System.Linq.Expressions.Expression; +#endif + using Configuration; + using Extensions; + using Extensions.Internal; + using Members; + using NetStandardPolyfills; + using ObjectPopulation; + using static Constants; + using static TypeComparer; + + internal static class DerivedComplexTypeDataSourcesFactory + { + public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) + { + var declaredTypeMapperData = declaredTypeMappingData.MapperData; + + if (DoNotMapDerivedTypes(declaredTypeMapperData)) + { + return EmptyExpression; + } + + var derivedSourceTypes = GetDerivedSourceTypesIfNecessary(declaredTypeMapperData); + var hasDerivedSourceTypes = derivedSourceTypes.Any(); + var hasNoDerivedSourceTypes = !hasDerivedSourceTypes; + + var derivedTargetTypes = GetDerivedTargetTypesIfNecessary(declaredTypeMapperData); + var hasDerivedTargetTypes = derivedTargetTypes.Any(); + + var derivedTypePairs = GetTypePairsFor(declaredTypeMapperData, declaredTypeMapperData); + var hasDerivedTypePairs = derivedTypePairs.Any(); + + if (hasNoDerivedSourceTypes && !hasDerivedTargetTypes && !hasDerivedTypePairs) + { + return EmptyExpression; + } + + var derivedTypeDataSources = new List(); + + if (hasDerivedTypePairs) + { + AddDeclaredSourceTypeDataSources( + derivedTypePairs, + declaredTypeMappingData, + derivedTypeDataSources); + + if (hasNoDerivedSourceTypes && !derivedTypeDataSources.Last().IsConditional) + { + goto ReturnDerivedTypeMappings; + } + } + + if (hasDerivedSourceTypes) + { + AddDerivedSourceTypeDataSources( + derivedSourceTypes, + declaredTypeMappingData, + derivedTypeDataSources); + } + + if (hasDerivedTargetTypes) + { + AddDerivedTargetTypeDataSources( + declaredTypeMappingData, + derivedTargetTypes, + derivedTypeDataSources); + } + + ReturnDerivedTypeMappings: + + var derivedTypeDataSourceSet = DataSourceSet.For( + derivedTypeDataSources, + declaredTypeMapperData, + ValueExpressionBuilders.SequentialValues); + + return derivedTypeDataSourceSet.BuildValue(); + } + + private static bool DoNotMapDerivedTypes(IMemberMapperData mapperData) + { + if (mapperData.Context.IsForDerivedType) + { + return !mapperData.TargetType.IsInterface(); + } + + return mapperData.HasSameSourceAsParent(); + } + + private static ICollection GetDerivedSourceTypesIfNecessary(IMemberMapperData mapperData) + { + return mapperData.RuleSet.Settings.CheckDerivedSourceTypes + ? mapperData.GetDerivedSourceTypes() + : EmptyTypeArray; + } + + private static ICollection GetDerivedTargetTypesIfNecessary(IMemberMapperData mapperData) + { + return mapperData.TargetCouldBePopulated() + ? mapperData.GetDerivedTargetTypes() + : EmptyTypeArray; + } + + private static ICollection GetTypePairsFor(IBasicMapperData pairTestMapperData, IMemberMapperData mapperData) + { + var derivedTypePairs = mapperData.MapperContext.UserConfigurations + .DerivedTypes + .GetDerivedTypePairsFor(pairTestMapperData, mapperData.MapperContext); + + return derivedTypePairs; + } + + private static void AddDeclaredSourceTypeDataSources( + IEnumerable derivedTypePairs, + IObjectMappingData declaredTypeMappingData, + ICollection derivedTypeDataSources) + { + var declaredTypeMapperData = declaredTypeMappingData.MapperData; + + derivedTypePairs = derivedTypePairs + .OrderBy(tp => tp.DerivedSourceType, MostToLeastDerived); + + foreach (var derivedTypePair in derivedTypePairs) + { + var condition = GetTypePairCondition(derivedTypePair, declaredTypeMapperData); + + var sourceValue = GetDerivedTypeSourceValue( + derivedTypePair, + declaredTypeMappingData, + out var sourceValueCondition); + + var derivedTypeMapping = DerivedMappingFactory.GetDerivedTypeMapping( + declaredTypeMappingData, + sourceValue, + derivedTypePair.DerivedTargetType, + out var derivedTypeMappingData); + + if (sourceValueCondition != null) + { + derivedTypeMapping = Condition( + sourceValueCondition, + derivedTypeMapping, + derivedTypeMapping.Type.ToDefaultExpression()); + } + + var returnMappingResult = Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); + + var derivedTypeMappingDataSource = new AdHocDataSource( + derivedTypeMappingData.MapperData.SourceMember, + returnMappingResult, + condition); + + derivedTypeDataSources.Add(derivedTypeMappingDataSource); + + if (!derivedTypeMappingDataSource.IsConditional) + { + return; + } + } + } + + private static Expression GetTypePairCondition(DerivedTypePair derivedTypePair, IMemberMapperData declaredTypeMapperData) + { + var condition = declaredTypeMapperData.GetTargetValidCheckOrNull(derivedTypePair.DerivedTargetType); + + if (!derivedTypePair.HasConfiguredCondition) + { + return condition; + } + + var pairCondition = derivedTypePair.GetConditionOrNull(declaredTypeMapperData); + + return (condition != null) ? AndAlso(pairCondition, condition) : pairCondition; + } + + private static Expression GetDerivedTypeSourceValue( + DerivedTypePair derivedTypePair, + IObjectMappingData declaredTypeMappingData, + out Expression sourceValueCondition) + { + if (!derivedTypePair.IsImplementationPairing) + { + sourceValueCondition = null; + return declaredTypeMappingData.MapperData.SourceObject; + } + + var implementationMappingData = declaredTypeMappingData + .WithTypes(derivedTypePair.DerivedSourceType, derivedTypePair.DerivedTargetType); + + if (implementationMappingData.IsTargetConstructable()) + { + sourceValueCondition = null; + return declaredTypeMappingData.MapperData.SourceObject; + } + + // Derived Type is an implementation Type for an unconstructable target Type, + // and is itself unconstructable; only way we get here is if a ToTarget data + // source has been configured: + var toTargetDataSource = implementationMappingData + .GetToTargetDataSourceOrNullForTargetType(); + + sourceValueCondition = toTargetDataSource.IsConditional + ? toTargetDataSource.Condition.Replace( + implementationMappingData.MapperData.SourceObject, + declaredTypeMappingData.MapperData.SourceObject, + ExpressionEvaluation.Equivalator) + : null; + + return toTargetDataSource.Value.Replace( + implementationMappingData.MapperData.SourceObject, + declaredTypeMappingData.MapperData.SourceObject, + ExpressionEvaluation.Equivalator); + } + + private static void AddDerivedSourceTypeDataSources( + IEnumerable derivedSourceTypes, + IObjectMappingData declaredTypeMappingData, + IList derivedTypeDataSources) + { + var declaredTypeMapperData = declaredTypeMappingData.MapperData; + var insertionOffset = derivedTypeDataSources.Count; + + var orderedDerivedSourceTypes = derivedSourceTypes + .OrderBy(t => t, MostToLeastDerived); + + foreach (var derivedSourceType in orderedDerivedSourceTypes) + { + var derivedSourceCheck = new DerivedSourceTypeCheck(derivedSourceType); + var targetType = declaredTypeMapperData.TargetType.GetRuntimeTargetType(derivedSourceType); + + var outerCondition = derivedSourceCheck.TypeCheck; + outerCondition = AppendTargetValidCheckIfAppropriate(outerCondition, targetType, declaredTypeMapperData); + + var derivedTypePairs = GetTypePairsFor(derivedSourceType, targetType, declaredTypeMapperData); + + IDataSource sourceVariableIsDerivedTypeDataSource; + + if (derivedTypePairs.None()) + { + sourceVariableIsDerivedTypeDataSource = GetReturnMappingResultDataSource( + declaredTypeMappingData, + outerCondition, + derivedSourceCheck, + targetType); + + derivedTypeDataSources.Insert(sourceVariableIsDerivedTypeDataSource, insertionOffset); + continue; + } + + var hasUnconditionalDerivedTargetTypeMapping = HasUnconditionalDerivedTargetTypeMapping( + derivedTypePairs, + declaredTypeMapperData, + out var unconditionalDerivedTargetType, + out var groupedTypePairs); + + if (hasUnconditionalDerivedTargetTypeMapping) + { + sourceVariableIsDerivedTypeDataSource = GetReturnMappingResultDataSource( + declaredTypeMappingData, + outerCondition, + derivedSourceCheck, + unconditionalDerivedTargetType); + + derivedTypeDataSources.Insert(sourceVariableIsDerivedTypeDataSource, insertionOffset); + continue; + } + + sourceVariableIsDerivedTypeDataSource = GetMapFromConditionOrDefaultDataSource( + declaredTypeMappingData, + outerCondition, + derivedSourceCheck, + groupedTypePairs, + targetType); + + derivedTypeDataSources.Insert(sourceVariableIsDerivedTypeDataSource, insertionOffset); + } + } + + private static IDataSource GetMapFromConditionOrDefaultDataSource( + IObjectMappingData declaredTypeMappingData, + Expression condition, + DerivedSourceTypeCheck derivedSourceCheck, + IEnumerable typePairGroups, + Type targetType) + { + var declaredTypeMapperData = declaredTypeMappingData.MapperData; + var typePairDataSources = new List(); + + Expression derivedTypeMapping; + IObjectMappingData derivedTypeMappingData; + + foreach (var typePairGroup in typePairGroups) + { + var typePairsCondition = + declaredTypeMapperData.GetTypePairsCondition(typePairGroup.TypePairs) ?? + declaredTypeMapperData.GetTargetValidCheckOrNull(typePairGroup.DerivedTargetType); + + derivedTypeMapping = GetReturnMappingResultExpression( + declaredTypeMappingData, + derivedSourceCheck.TypedVariable, + typePairGroup.DerivedTargetType, + out derivedTypeMappingData); + + var typePairDataSource = new AdHocDataSource( + derivedTypeMappingData.MapperData.SourceMember, + derivedTypeMapping, + typePairsCondition); + + typePairDataSources.Add(typePairDataSource); + } + + var derivedTargetTypeDataSources = DataSourceSet.For( + typePairDataSources, + declaredTypeMapperData, + ValueExpressionBuilders.SequentialValues); + + derivedTypeMapping = GetReturnMappingResultExpression( + declaredTypeMappingData, + derivedSourceCheck.TypedVariable, + targetType, + out derivedTypeMappingData); + + var derivedTypeMappings = Block( + derivedSourceCheck.GetTypedVariableAssignment(declaredTypeMapperData), + derivedTargetTypeDataSources.BuildValue(), + derivedTypeMapping); + + return new AdHocDataSource( + derivedTypeMappingData.MapperData.SourceMember, + derivedTypeMappings, + condition, + derivedSourceCheck.TypedVariable); + } + + private static Expression AppendTargetValidCheckIfAppropriate( + Expression condition, + Type targetType, + IMemberMapperData mapperData) + { + if (targetType == mapperData.TargetType) + { + return condition; + } + + var targetIsValid = mapperData.GetTargetValidCheckOrNull(targetType); + + if (targetIsValid == null) + { + return condition; + } + + condition = AndAlso(condition, targetIsValid); + + return condition; + } + + private static bool HasUnconditionalDerivedTargetTypeMapping( + IEnumerable derivedTypePairs, + IMemberMapperData declaredTypeMapperData, + out Type unconditionalDerivedTargetType, + out TypePairGroup[] groupedTypePairs) + { + groupedTypePairs = derivedTypePairs + .GroupBy(tp => tp.DerivedTargetType) + .Project(group => new TypePairGroup(group)) + .OrderBy(tp => tp.DerivedTargetType, MostToLeastDerived) + .ToArray(); + + var unconditionalTypePairs = groupedTypePairs + .Filter(tpg => tpg.TypePairs.None(tp => tp.HasConfiguredCondition)); + + foreach (var unconditionalTypePair in unconditionalTypePairs) + { + var typePairsCondition = declaredTypeMapperData + .GetTargetValidCheckOrNull(unconditionalTypePair.DerivedTargetType); + + if (typePairsCondition == null) + { + unconditionalDerivedTargetType = unconditionalTypePair.DerivedTargetType; + return true; + } + } + + unconditionalDerivedTargetType = null; + return false; + } + + private static ICollection GetTypePairsFor( + Type derivedSourceType, + Type targetType, + IMemberMapperData mapperData) + { + var pairTestMapperData = new BasicMapperData( + mapperData.RuleSet, + derivedSourceType, + targetType, + mapperData.TargetMember.WithType(targetType), + mapperData.Parent); + + return GetTypePairsFor(pairTestMapperData, mapperData); + } + + private static void AddDerivedTargetTypeDataSources( + IObjectMappingData declaredTypeMappingData, + IEnumerable derivedTargetTypes, + ICollection derivedTypeDataSources) + { + var declaredTypeMapperData = declaredTypeMappingData.MapperData; + + derivedTargetTypes = derivedTargetTypes.OrderBy(t => t, MostToLeastDerived); + + foreach (var derivedTargetType in derivedTargetTypes) + { + var targetTypeCondition = declaredTypeMapperData.GetTargetIsDerivedTypeCheck(derivedTargetType); + + var derivedTypeMapping = GetReturnMappingResultExpression( + declaredTypeMappingData, + declaredTypeMapperData.SourceObject, + derivedTargetType, + out var derivedTypeMappingData); + + var derivedTargetTypeDataSouce = new AdHocDataSource( + derivedTypeMappingData.MapperData.SourceMember, + derivedTypeMapping, + targetTypeCondition); + + derivedTypeDataSources.Add(derivedTargetTypeDataSouce); + } + } + + private static IDataSource GetReturnMappingResultDataSource( + IObjectMappingData declaredTypeMappingData, + Expression condition, + DerivedSourceTypeCheck derivedSourceCheck, + Type targetType) + { + var typedVariableAssignment = derivedSourceCheck + .GetTypedVariableAssignment(declaredTypeMappingData.MapperData); + + var derivedTypeMapping = GetReturnMappingResultExpression( + declaredTypeMappingData, + derivedSourceCheck.TypedVariable, + targetType, + out var derivedTypeMappingData); + + return new AdHocDataSource( + derivedTypeMappingData.MapperData.SourceMember, + Block(typedVariableAssignment, derivedTypeMapping), + condition, + derivedSourceCheck.TypedVariable); + } + + private static Expression GetReturnMappingResultExpression( + IObjectMappingData declaredTypeMappingData, + Expression sourceValue, + Type targetType, + out IObjectMappingData derivedTypeMappingData) + { + var mapping = DerivedMappingFactory.GetDerivedTypeMapping( + declaredTypeMappingData, + sourceValue, + targetType, + out derivedTypeMappingData); + + return (mapping != EmptyExpression) + ? Return(declaredTypeMappingData.MapperData.ReturnLabelTarget, mapping) + : mapping; + } + } + + internal class TypePairGroup + { + public TypePairGroup(IGrouping typePairGroup) + { + DerivedTargetType = typePairGroup.Key; + TypePairs = typePairGroup.ToArray(); + } + + public Type DerivedTargetType { get; } + + public IList TypePairs { get; } + } + + internal static class DerivedTypeMappingExtensions + { + public static Expression GetTypePairsCondition( + this IMemberMapperData mapperData, + IEnumerable derivedTypePairs) + { + var conditionalPairs = derivedTypePairs + .Filter(pair => pair.HasConfiguredCondition) + .ToArray(); + + var pairConditions = conditionalPairs.Chain( + firstPair => firstPair.GetConditionOrNull(mapperData), + (conditionSoFar, pair) => OrElse( + conditionSoFar, + pair.GetConditionOrNull(mapperData))); + + return pairConditions; + } + + public static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapperData, Type targetType) + { + if (!mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated()) + { + return null; + } + + var targetIsOfDerivedType = mapperData.GetTargetIsDerivedTypeCheck(targetType); + + if (mapperData.TargetIsDefinitelyPopulated()) + { + return targetIsOfDerivedType; + } + + var targetIsNull = mapperData.TargetObject.GetIsDefaultComparison(); + var targetIsValid = OrElse(targetIsNull, targetIsOfDerivedType); + + return targetIsValid; + } + + public static Expression GetTargetIsDerivedTypeCheck(this IMemberMapperData mapperData, Type targetType) + => TypeIs(mapperData.TargetObject, targetType); + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/DictionaryEntryDataSource.cs b/AgileMapper/DataSources/DictionaryEntryDataSource.cs index 9783ea227..8a780a23e 100644 --- a/AgileMapper/DataSources/DictionaryEntryDataSource.cs +++ b/AgileMapper/DataSources/DictionaryEntryDataSource.cs @@ -66,7 +66,7 @@ private Expression CreatePreCondition() return Expression.Block(keyAssignment, matchingKeyExists); } - public override Expression AddPreCondition(Expression population) + public override Expression Finalise(Expression population) { var matchingKeyExists = GetMatchingKeyExistsTest(); var ifKeyExistsPopulate = Expression.IfThen(matchingKeyExists, population); diff --git a/AgileMapper/DataSources/DictionaryEntryVariablePair.cs b/AgileMapper/DataSources/DictionaryEntryVariablePair.cs index 41ebe079b..a822a42f2 100644 --- a/AgileMapper/DataSources/DictionaryEntryVariablePair.cs +++ b/AgileMapper/DataSources/DictionaryEntryVariablePair.cs @@ -59,7 +59,7 @@ private static string GetTargetMemberName(IBasicMapperData mapperData) public IMemberMapperData MapperData { get; } - public ICollection Variables { get; } + public IList Variables { get; } public ParameterExpression Key => _key ?? (_key = Expression.Variable(SourceMember.KeyType, _targetMemberName + "Key")); diff --git a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index 9962725bf..4e376e924 100644 --- a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -2,6 +2,7 @@ { using System.Collections.Generic; using System.Linq; + using Extensions.Internal; using MappingRoot; using Members; using ObjectPopulation; @@ -32,7 +33,7 @@ public static DataSourceSet CreateFor(IObjectMappingData rootMappingData) var rootDataSource = rootDataSourceFactory.CreateFor(rootMappingData); - return new DataSourceSet(rootMappingData.MapperData, rootDataSource); + return DataSourceSet.For(rootDataSource, rootMappingData.MapperData); } public static DataSourceSet CreateFor(IChildMemberMappingData childMappingData) @@ -40,15 +41,20 @@ public static DataSourceSet CreateFor(IChildMemberMappingData childMappingData) var findContext = new DataSourceFindContext(childMappingData); var validDataSources = EnumerateDataSources(findContext).ToArray(); - return new DataSourceSet(findContext.MapperData, validDataSources); + return DataSourceSet.For(validDataSources, findContext.MapperData); } private static IEnumerable EnumerateDataSources(DataSourceFindContext context) { - foreach (var finder in _childDataSourceFactories) + foreach (var factory in _childDataSourceFactories) { - foreach (var dataSource in EnumerateDataSources(finder.Invoke(context))) + foreach (var dataSource in factory.Invoke(context)) { + if (!dataSource.IsValid) + { + continue; + } + yield return dataSource; if (!dataSource.IsConditional) @@ -63,26 +69,5 @@ private static IEnumerable EnumerateDataSources(DataSourceFindConte } } } - - private static IEnumerable EnumerateDataSources(IEnumerable dataSources) - { - foreach (var dataSource in dataSources) - { - if (dataSource.ChildDataSources.Any()) - { - foreach (var childDataSource in EnumerateDataSources(dataSource.ChildDataSources)) - { - yield return childDataSource; - } - } - - if (!dataSource.IsValid) - { - continue; - } - - yield return dataSource; - } - } } } \ No newline at end of file diff --git a/AgileMapper/DataSources/IDataSource.cs b/AgileMapper/DataSources/IDataSource.cs index 34981be98..2b42b596c 100644 --- a/AgileMapper/DataSources/IDataSource.cs +++ b/AgileMapper/DataSources/IDataSource.cs @@ -21,11 +21,9 @@ internal interface IDataSource : IConditionallyChainable bool IsFallback { get; } - ICollection Variables { get; } + IList Variables { get; } - IList ChildDataSources { get; } - - Expression AddPreCondition(Expression population); + Expression Finalise(Expression population); Expression AddCondition(Expression value, Expression alternateBranch = null); } diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs new file mode 100644 index 000000000..0df077d1d --- /dev/null +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -0,0 +1,75 @@ +namespace AgileObjects.AgileMapper.DataSources +{ + using System.Collections.Generic; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif + using Extensions.Internal; + using Members; + + internal static class ValueExpressionBuilders + { + public static Expression SingleDataSource(IList dataSources, IMemberMapperData mapperData) + { + var dataSource = dataSources[0]; + + var value = dataSource.IsConditional + ? Expression.Condition( + dataSource.Condition, + dataSource.Value, + dataSource.Value.Type.ToDefaultExpression()) + : dataSource.Value; + + return dataSource.AddPreConditionIfNecessary(value); + } + + public static Expression ConditionTree(IList dataSources, IMemberMapperData mapperData) + { + var value = default(Expression); + + for (var i = dataSources.Count - 1; i >= 0;) + { + var isFirstDataSource = value == default(Expression); + var dataSource = dataSources[i--]; + + var dataSourceValue = dataSource.IsConditional + ? Expression.Condition( + dataSource.Condition, + isFirstDataSource + ? dataSource.Value + : dataSource.Value.GetConversionTo(value.Type), + isFirstDataSource + ? dataSource.Value.Type.ToDefaultExpression() + : value) + : dataSource.Value; + + value = dataSource.AddPreConditionIfNecessary(dataSourceValue); + } + + return value; + } + + public static Expression SequentialValues(IList dataSources, IMemberMapperData mapperData) + { + var mappingExpressions = new List(); + + foreach (var dataSource in dataSources) + { + var mapping = dataSource.Finalise(dataSource.Value); + + if (dataSource.IsConditional) + { + mapping = Expression.IfThen(dataSource.Condition, mapping); + } + + mapping = dataSource.AddPreConditionIfNecessary(mapping); + + mappingExpressions.Add(mapping); + } + + return Expression.Block(mappingExpressions); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs index 0d5d4fa60..caea7f2e9 100644 --- a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs +++ b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs @@ -245,6 +245,12 @@ public static T[] Prepend(this IList items, T initialItem) return newArray; } + public static void Insert(this IList items, T item, int insertionOffset) + { + var insertionIndex = items.Count - insertionOffset; + items.Insert(insertionIndex, item); + } + public static T[] Append(this IList array, T extraItem) { switch (array.Count) diff --git a/AgileMapper/Members/Population/MemberPopulator.cs b/AgileMapper/Members/Population/MemberPopulator.cs index 3776cee57..461194d88 100644 --- a/AgileMapper/Members/Population/MemberPopulator.cs +++ b/AgileMapper/Members/Population/MemberPopulator.cs @@ -69,10 +69,10 @@ private static DataSourceSet CreateNullDataSourceSet( IMemberMapperData mapperData, Func commentFactory) { - return new DataSourceSet( - mapperData, + return DataSourceSet.For( new NullDataSource( - ReadableExpression.Comment(commentFactory.Invoke(mapperData.TargetMember)))); + ReadableExpression.Comment(commentFactory.Invoke(mapperData.TargetMember))), + mapperData); } #endregion @@ -87,7 +87,7 @@ public Expression GetPopulation() { if (!CanPopulate) { - return _dataSources.ValueExpression; + return _dataSources.BuildValue(); } var populationGuard = MapperData @@ -120,7 +120,7 @@ public Expression GetPopulation() private Expression GetBinding(Expression populationGuard) { - var bindingValue = _dataSources.ValueExpression; + var bindingValue = _dataSources.BuildValue(); if (MapperData.RuleSet.Settings.AllowGuardedBindings && (populationGuard != null)) { @@ -138,7 +138,7 @@ private Expression GetReadOnlyMemberPopulation() var targetMemberAccess = MapperData.GetTargetMemberAccess(); var targetMemberNotNull = targetMemberAccess.GetIsNotDefaultComparison(); - return Expression.IfThen(targetMemberNotNull, _dataSources.ValueExpression); + return Expression.IfThen(targetMemberNotNull, _dataSources.BuildValue()); } private Expression GetPopulationExpression() @@ -167,14 +167,14 @@ private Expression GetPopulationExpression() population = dataSource.AddCondition(population); } - population = dataSource.AddPreCondition(population); + population = dataSource.Finalise(population); continue; } var memberPopulation = MapperData.GetTargetMemberPopulation(dataSource.Value); population = dataSource.AddCondition(memberPopulation, population); - population = dataSource.AddPreCondition(population); + population = dataSource.Finalise(population); } return population; @@ -188,14 +188,10 @@ private Expression GetPopulationWithVariables(Expression population) } var populationBlock = (BlockExpression)population; - var variables = _dataSources.Variables; - if (Enumerable.Any(populationBlock.Variables)) - { - variables = variables.Append(populationBlock.Variables); - } - - return Expression.Block(variables, populationBlock.Expressions); + return Expression.Block( + _dataSources.Variables.Append(populationBlock.Variables), + populationBlock.Expressions); } #region ExcludeFromCodeCoverage diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs index a27e5ccae..f3ca4fbca 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs @@ -467,7 +467,7 @@ public ConstructionData(ConstructionDataInfo info) } } - argumentValues.Add(dataSources.ValueExpression); + argumentValues.Add(dataSources.BuildValue()); if (info.IsUnconditional) { diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs index 32635e63c..1dc09cc77 100644 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs @@ -11,6 +11,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using static System.Linq.Expressions.Expression; #endif using Configuration; + using DataSources; using Extensions; using Extensions.Internal; using Members; @@ -497,24 +498,5 @@ private static Expression GetTargetValidCheckOrNull(Type targetType, IMemberMapp private static Expression GetTargetIsDerivedTypeCheck(Type targetType, IMemberMapperData mapperData) => TypeIs(mapperData.TargetObject, targetType); - - private static void Insert(this IList mappingExpressions, Expression mapping, int insertionOffset) - { - var insertionIndex = mappingExpressions.Count - insertionOffset; - mappingExpressions.Insert(insertionIndex, mapping); - } - - private class TypePairGroup - { - public TypePairGroup(IGrouping typePairGroup) - { - DerivedTargetType = typePairGroup.Key; - TypePairs = typePairGroup.ToArray(); - } - - public Type DerivedTargetType { get; } - - public IList TypePairs { get; } - } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/DerivedMappingFactory.cs b/AgileMapper/ObjectPopulation/DerivedMappingFactory.cs index 3b2cb6a87..62b297d3c 100644 --- a/AgileMapper/ObjectPopulation/DerivedMappingFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedMappingFactory.cs @@ -16,14 +16,27 @@ public static Expression GetDerivedTypeMapping( Expression sourceValue, Type targetType) { + return GetDerivedTypeMapping( + declaredTypeMappingData, + sourceValue, + targetType, + out _); + } + + public static Expression GetDerivedTypeMapping( + IObjectMappingData declaredTypeMappingData, + Expression sourceValue, + Type targetType, + out IObjectMappingData derivedTypeMappingData) + { + derivedTypeMappingData = declaredTypeMappingData.WithTypes(sourceValue.Type, targetType); + var declaredTypeMapperData = declaredTypeMappingData.MapperData; var targetValue = declaredTypeMapperData.TargetMember.IsReadable ? declaredTypeMapperData.TargetObject.GetConversionTo(targetType) : targetType.ToDefaultExpression(); - var derivedTypeMappingData = declaredTypeMappingData.WithTypes(sourceValue.Type, targetType); - if (declaredTypeMappingData.IsRoot) { return GetDerivedTypeRootMapping(derivedTypeMappingData, sourceValue, targetValue); diff --git a/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs b/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs index 23f7b532d..a8d4734b4 100644 --- a/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs +++ b/AgileMapper/ObjectPopulation/DerivedSourceTypeCheck.cs @@ -1,26 +1,26 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System; - using Extensions.Internal; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions.Internal; + using Members; internal class DerivedSourceTypeCheck { - private readonly Type _derivedSourceType; - public DerivedSourceTypeCheck(Type derivedSourceType) { - _derivedSourceType = derivedSourceType; + DerivedSourceType = derivedSourceType; var typedVariableName = "source" + derivedSourceType.GetVariableNameInPascalCase(); TypedVariable = Expression.Variable(derivedSourceType, typedVariableName); } + public Type DerivedSourceType { get; } + public ParameterExpression TypedVariable { get; } public Expression GetTypedVariableAssignment(IMemberMapperData declaredTypeMapperData) @@ -28,7 +28,7 @@ public Expression GetTypedVariableAssignment(IMemberMapperData declaredTypeMappe public Expression GetTypedVariableAssignment(Expression sourceObject) { - var typeAsConversion = Expression.TypeAs(sourceObject, _derivedSourceType); + var typeAsConversion = Expression.TypeAs(sourceObject, DerivedSourceType); var typedVariableAssignment = TypedVariable.AssignTo(typeAsConversion); return typedVariableAssignment; diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs index df9c9545a..860b6c014 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs @@ -259,7 +259,7 @@ private Expression GetPopulation( var sourceMember = mappingData.MapperData.SourceMember; var mappingDataSource = new AdHocDataSource(sourceMember, elementMapping); - var mappingDataSources = new DataSourceSet(elementMapperData, mappingDataSource); + var mappingDataSources = DataSourceSet.For(mappingDataSource, elementMapperData); var populationExpression = MemberPopulator .WithoutRegistration(mappingDataSources) diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index 11eda902c..c949adde4 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -52,7 +52,7 @@ public ObjectMapper GetOrCreateRoot(ObjectMa public ObjectMapper Create(ObjectMappingData mappingData) { - var mappingExpression = DataSourceSetFactory.CreateFor(mappingData).ValueExpression; + var mappingExpression = DataSourceSetFactory.CreateFor(mappingData).BuildValue(); if (mappingExpression == Constants.EmptyExpression) { From 985953ad926559bab1dc978bf5637d4d3014a9ce Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 19 Aug 2019 08:10:18 +0100 Subject: [PATCH 11/17] Rearranging derived type mapping generation Adding condition-or-default helper method Tidying --- .../DataSources/ComplexTypeDataSource.cs | 3 +- AgileMapper/DataSources/DataSourceBase.cs | 21 ++- .../DerivedComplexTypeDataSourcesFactory.cs | 159 ++++++++++-------- .../DataSources/DictionaryEntryDataSource.cs | 13 +- AgileMapper/DataSources/IDataSource.cs | 4 +- .../DataSources/ValueExpressionBuilders.cs | 59 ++++--- .../Internal/EnumerableExtensions.cs | 29 +--- .../Internal/ExpressionExtensions.cs | 10 +- .../Internal/IConditionallyChainable.cs | 2 - .../Members/Population/MemberPopulator.cs | 15 +- .../ComplexTypeConstructionFactory.cs | 43 +++-- .../ComplexTypeMappingExpressionFactory.cs | 43 ++++- .../DerivedComplexTypeMappingsFactory.cs | 5 +- ...ourceObjectDictionaryPopulationLoopData.cs | 11 +- .../EnumerablePopulationBuilder.cs | 10 +- .../MappingCreationContext.cs | 12 +- .../MappingExpressionFactoryBase.cs | 43 ++--- AgileMapper/TypeConversion/ToBoolConverter.cs | 5 +- .../TypeConversion/ToNumericConverter.cs | 19 +-- .../TypeConversion/ToStringConverter.cs | 6 +- 20 files changed, 252 insertions(+), 260 deletions(-) diff --git a/AgileMapper/DataSources/ComplexTypeDataSource.cs b/AgileMapper/DataSources/ComplexTypeDataSource.cs index ea73ebd5c..051e08440 100644 --- a/AgileMapper/DataSources/ComplexTypeDataSource.cs +++ b/AgileMapper/DataSources/ComplexTypeDataSource.cs @@ -5,6 +5,7 @@ #else using System.Linq.Expressions; #endif + using Extensions.Internal; using Members; using ObjectPopulation; using ObjectPopulation.ComplexTypes; @@ -25,8 +26,6 @@ private ComplexTypeDataSource(IQualifiedMember sourceMember, Expression mapping) public static IDataSource Create(IObjectMappingData mappingData) { - var derivedTypeDataSources = DerivedComplexTypeDataSourcesFactory.CreateFor(mappingData); - var mapping = ComplexTypeMappingExpressionFactory.Instance.Create(mappingData); return new ComplexTypeDataSource(mappingData.MapperData.SourceMember, mapping); diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index 1eb871da3..785c671cb 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -180,9 +180,7 @@ private static bool IsNotOptionalEntityMemberId(IMemberMapperData mapperData) public Expression SourceMemberTypeTest { get; protected set; } public virtual bool IsValid => Value != Constants.EmptyExpression; - - public virtual Expression PreCondition => null; - + public bool IsConditional => Condition != null; public virtual bool IsFallback => false; @@ -193,13 +191,18 @@ private static bool IsNotOptionalEntityMemberId(IMemberMapperData mapperData) public Expression Value { get; } - public virtual Expression Finalise(Expression population) => population; - - public Expression AddCondition(Expression value, Expression alternateBranch = null) + public virtual Expression Finalise(Expression memberPopulation, Expression alternatePopulation) { - return alternateBranch != null - ? Expression.IfThenElse(Condition, value, alternateBranch) - : Expression.IfThen(Condition, value); + if (IsConditional) + { + memberPopulation = (alternatePopulation != null) + ? Expression.IfThenElse(Condition, memberPopulation, alternatePopulation) + : Expression.IfThen(Condition, memberPopulation); + } + + return memberPopulation; } + + public virtual Expression AddSourceCondition(Expression value) => value; } } \ No newline at end of file diff --git a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs index eb1fcfe08..1ca61744b 100644 --- a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs @@ -21,13 +21,13 @@ internal static class DerivedComplexTypeDataSourcesFactory { - public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) + public static IList CreateFor(IObjectMappingData declaredTypeMappingData) { var declaredTypeMapperData = declaredTypeMappingData.MapperData; if (DoNotMapDerivedTypes(declaredTypeMapperData)) { - return EmptyExpression; + return Enumerable.EmptyArray; } var derivedSourceTypes = GetDerivedSourceTypesIfNecessary(declaredTypeMapperData); @@ -42,7 +42,7 @@ public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) if (hasNoDerivedSourceTypes && !hasDerivedTargetTypes && !hasDerivedTypePairs) { - return EmptyExpression; + return Enumerable.EmptyArray; } var derivedTypeDataSources = new List(); @@ -56,7 +56,7 @@ public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) if (hasNoDerivedSourceTypes && !derivedTypeDataSources.Last().IsConditional) { - goto ReturnDerivedTypeMappings; + return derivedTypeDataSources; } } @@ -76,14 +76,7 @@ public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) derivedTypeDataSources); } - ReturnDerivedTypeMappings: - - var derivedTypeDataSourceSet = DataSourceSet.For( - derivedTypeDataSources, - declaredTypeMapperData, - ValueExpressionBuilders.SequentialValues); - - return derivedTypeDataSourceSet.BuildValue(); + return derivedTypeDataSources; } private static bool DoNotMapDerivedTypes(IMemberMapperData mapperData) @@ -146,18 +139,15 @@ private static void AddDeclaredSourceTypeDataSources( if (sourceValueCondition != null) { - derivedTypeMapping = Condition( - sourceValueCondition, - derivedTypeMapping, - derivedTypeMapping.Type.ToDefaultExpression()); + derivedTypeMapping = derivedTypeMapping.ToIfFalseDefaultCondition(sourceValueCondition); } var returnMappingResult = Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); - var derivedTypeMappingDataSource = new AdHocDataSource( + var derivedTypeMappingDataSource = new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, - returnMappingResult, - condition); + condition, + returnMappingResult); derivedTypeDataSources.Add(derivedTypeMappingDataSource); @@ -310,10 +300,10 @@ private static IDataSource GetMapFromConditionOrDefaultDataSource( typePairGroup.DerivedTargetType, out derivedTypeMappingData); - var typePairDataSource = new AdHocDataSource( + var typePairDataSource = new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, - derivedTypeMapping, - typePairsCondition); + typePairsCondition, + derivedTypeMapping); typePairDataSources.Add(typePairDataSource); } @@ -321,7 +311,7 @@ private static IDataSource GetMapFromConditionOrDefaultDataSource( var derivedTargetTypeDataSources = DataSourceSet.For( typePairDataSources, declaredTypeMapperData, - ValueExpressionBuilders.SequentialValues); + ValueExpressionBuilders.ValueSequence); derivedTypeMapping = GetReturnMappingResultExpression( declaredTypeMappingData, @@ -330,15 +320,32 @@ private static IDataSource GetMapFromConditionOrDefaultDataSource( out derivedTypeMappingData); var derivedTypeMappings = Block( - derivedSourceCheck.GetTypedVariableAssignment(declaredTypeMapperData), derivedTargetTypeDataSources.BuildValue(), derivedTypeMapping); - return new AdHocDataSource( + return new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, - derivedTypeMappings, + derivedSourceCheck, condition, - derivedSourceCheck.TypedVariable); + derivedTypeMappings, + declaredTypeMapperData); + } + + private static Expression GetTypePairsCondition( + this IMemberMapperData mapperData, + IEnumerable derivedTypePairs) + { + var conditionalPairs = derivedTypePairs + .Filter(pair => pair.HasConfiguredCondition) + .ToArray(); + + var pairConditions = conditionalPairs.Chain( + firstPair => firstPair.GetConditionOrNull(mapperData), + (conditionSoFar, pair) => OrElse( + conditionSoFar, + pair.GetConditionOrNull(mapperData))); + + return pairConditions; } private static Expression AppendTargetValidCheckIfAppropriate( @@ -428,10 +435,10 @@ private static void AddDerivedTargetTypeDataSources( derivedTargetType, out var derivedTypeMappingData); - var derivedTargetTypeDataSouce = new AdHocDataSource( + var derivedTargetTypeDataSouce = new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, - derivedTypeMapping, - targetTypeCondition); + targetTypeCondition, + derivedTypeMapping); derivedTypeDataSources.Add(derivedTargetTypeDataSouce); } @@ -443,20 +450,18 @@ private static IDataSource GetReturnMappingResultDataSource( DerivedSourceTypeCheck derivedSourceCheck, Type targetType) { - var typedVariableAssignment = derivedSourceCheck - .GetTypedVariableAssignment(declaredTypeMappingData.MapperData); - var derivedTypeMapping = GetReturnMappingResultExpression( declaredTypeMappingData, derivedSourceCheck.TypedVariable, targetType, out var derivedTypeMappingData); - return new AdHocDataSource( + return new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, - Block(typedVariableAssignment, derivedTypeMapping), + derivedSourceCheck, condition, - derivedSourceCheck.TypedVariable); + derivedTypeMapping, + declaredTypeMappingData.MapperData); } private static Expression GetReturnMappingResultExpression( @@ -475,41 +480,8 @@ private static Expression GetReturnMappingResultExpression( ? Return(declaredTypeMappingData.MapperData.ReturnLabelTarget, mapping) : mapping; } - } - - internal class TypePairGroup - { - public TypePairGroup(IGrouping typePairGroup) - { - DerivedTargetType = typePairGroup.Key; - TypePairs = typePairGroup.ToArray(); - } - - public Type DerivedTargetType { get; } - - public IList TypePairs { get; } - } - - internal static class DerivedTypeMappingExtensions - { - public static Expression GetTypePairsCondition( - this IMemberMapperData mapperData, - IEnumerable derivedTypePairs) - { - var conditionalPairs = derivedTypePairs - .Filter(pair => pair.HasConfiguredCondition) - .ToArray(); - - var pairConditions = conditionalPairs.Chain( - firstPair => firstPair.GetConditionOrNull(mapperData), - (conditionSoFar, pair) => OrElse( - conditionSoFar, - pair.GetConditionOrNull(mapperData))); - - return pairConditions; - } - public static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapperData, Type targetType) + private static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapperData, Type targetType) { if (!mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated()) { @@ -529,7 +501,52 @@ public static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapper return targetIsValid; } - public static Expression GetTargetIsDerivedTypeCheck(this IMemberMapperData mapperData, Type targetType) + private static Expression GetTargetIsDerivedTypeCheck(this IMemberMapperData mapperData, Type targetType) => TypeIs(mapperData.TargetObject, targetType); + + private class DerivedComplexTypeDataSource : DataSourceBase + { + private readonly Expression _typedVariableAssignment; + + public DerivedComplexTypeDataSource( + IQualifiedMember sourceMember, + Expression condition, + Expression value) + : base(sourceMember, Enumerable.EmptyArray, value, condition) + { + } + + public DerivedComplexTypeDataSource( + IQualifiedMember sourceMember, + DerivedSourceTypeCheck derivedSourceCheck, + Expression condition, + Expression value, + IMemberMapperData declaredTypeMapperData) + : base(sourceMember, new[] { derivedSourceCheck.TypedVariable }, value, condition) + { + _typedVariableAssignment = derivedSourceCheck + .GetTypedVariableAssignment(declaredTypeMapperData); + } + + public override Expression AddSourceCondition(Expression value) + { + return (_typedVariableAssignment != null) + ? Block(_typedVariableAssignment, value) + : base.AddSourceCondition(value); + } + } + } + + internal class TypePairGroup + { + public TypePairGroup(IGrouping typePairGroup) + { + DerivedTargetType = typePairGroup.Key; + TypePairs = typePairGroup.ToArray(); + } + + public Type DerivedTargetType { get; } + + public IList TypePairs { get; } } } \ No newline at end of file diff --git a/AgileMapper/DataSources/DictionaryEntryDataSource.cs b/AgileMapper/DataSources/DictionaryEntryDataSource.cs index 8a780a23e..2eeed8f49 100644 --- a/AgileMapper/DataSources/DictionaryEntryDataSource.cs +++ b/AgileMapper/DataSources/DictionaryEntryDataSource.cs @@ -50,7 +50,12 @@ private static Expression GetValidEntryExistsTest(DictionaryEntryVariablePair di return valueNonNull; } - public override Expression PreCondition => _preCondition ?? (_preCondition = CreatePreCondition()); + public override Expression AddSourceCondition(Expression value) + { + var preCondition = _preCondition ?? (_preCondition = CreatePreCondition()); + + return value.ToIfFalseDefaultCondition(preCondition); + } private Expression CreatePreCondition() { @@ -66,10 +71,12 @@ private Expression CreatePreCondition() return Expression.Block(keyAssignment, matchingKeyExists); } - public override Expression Finalise(Expression population) + public override Expression Finalise(Expression memberPopulation, Expression alternatePopulation) { + memberPopulation = base.Finalise(memberPopulation, alternatePopulation); + var matchingKeyExists = GetMatchingKeyExistsTest(); - var ifKeyExistsPopulate = Expression.IfThen(matchingKeyExists, population); + var ifKeyExistsPopulate = Expression.IfThen(matchingKeyExists, memberPopulation); if (_dictionaryVariables.HasConstantTargetMemberKey) { diff --git a/AgileMapper/DataSources/IDataSource.cs b/AgileMapper/DataSources/IDataSource.cs index 2b42b596c..8ae56619f 100644 --- a/AgileMapper/DataSources/IDataSource.cs +++ b/AgileMapper/DataSources/IDataSource.cs @@ -23,8 +23,8 @@ internal interface IDataSource : IConditionallyChainable IList Variables { get; } - Expression Finalise(Expression population); + Expression AddSourceCondition(Expression value); - Expression AddCondition(Expression value, Expression alternateBranch = null); + Expression Finalise(Expression memberPopulation, Expression alternatePopulation = null); } } diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs index 0df077d1d..26efa469f 100644 --- a/AgileMapper/DataSources/ValueExpressionBuilders.cs +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -13,63 +13,66 @@ internal static class ValueExpressionBuilders { public static Expression SingleDataSource(IList dataSources, IMemberMapperData mapperData) { - var dataSource = dataSources[0]; + var dataSource = dataSources.Last(); var value = dataSource.IsConditional - ? Expression.Condition( - dataSource.Condition, - dataSource.Value, - dataSource.Value.Type.ToDefaultExpression()) + ? dataSource.Value.ToIfFalseDefaultCondition(dataSource.Condition) : dataSource.Value; - return dataSource.AddPreConditionIfNecessary(value); + return dataSource.AddSourceCondition(value); } public static Expression ConditionTree(IList dataSources, IMemberMapperData mapperData) { - var value = default(Expression); + var value = SingleDataSource(dataSources, mapperData); - for (var i = dataSources.Count - 1; i >= 0;) + for (var i = dataSources.Count - 2; i >= 0;) { - var isFirstDataSource = value == default(Expression); var dataSource = dataSources[i--]; var dataSourceValue = dataSource.IsConditional ? Expression.Condition( dataSource.Condition, - isFirstDataSource - ? dataSource.Value - : dataSource.Value.GetConversionTo(value.Type), - isFirstDataSource - ? dataSource.Value.Type.ToDefaultExpression() - : value) + dataSource.Value.GetConversionTo(value.Type), + value) : dataSource.Value; - value = dataSource.AddPreConditionIfNecessary(dataSourceValue); + value = dataSource.AddSourceCondition(dataSourceValue); } return value; } - public static Expression SequentialValues(IList dataSources, IMemberMapperData mapperData) + public static Expression ValueSequence(IList dataSources, IMemberMapperData mapperData) { - var mappingExpressions = new List(); - - foreach (var dataSource in dataSources) + if (dataSources.HasOne()) { - var mapping = dataSource.Finalise(dataSource.Value); + return dataSources.First().GetValueSequenceValue(); + } + + var mappingExpressions = dataSources + .ProjectToArray(dataSource => dataSource.GetValueSequenceValue()); + + return Expression.Block(mappingExpressions); + } - if (dataSource.IsConditional) - { - mapping = Expression.IfThen(dataSource.Condition, mapping); - } + private static Expression GetValueSequenceValue(this IDataSource dataSource) + { + var mapping = dataSource.Value; + + if (dataSource.IsConditional) + { + mapping = Expression.IfThen(dataSource.Condition, mapping); + } - mapping = dataSource.AddPreConditionIfNecessary(mapping); + mapping = dataSource.AddSourceCondition(mapping); - mappingExpressions.Add(mapping); + if (dataSource.Variables.Any()) + { + mapping = Expression.Block(dataSource.Variables, mapping); } - return Expression.Block(mappingExpressions); + return mapping; } } } \ No newline at end of file diff --git a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs index caea7f2e9..035733c0b 100644 --- a/AgileMapper/Extensions/Internal/EnumerableExtensions.cs +++ b/AgileMapper/Extensions/Internal/EnumerableExtensions.cs @@ -163,31 +163,6 @@ public static T[] CopyToArray(this IList items) return clonedArray; } - public static Expression ReverseChain(this IList items) - where T : IConditionallyChainable - { - return Chain( - items, - i => i.Last(), - item => item.AddPreConditionIfNecessary(item.Value), - (valueSoFar, item) => item.AddPreConditionIfNecessary( - Expression.Condition(item.Condition, item.Value, valueSoFar)), - i => i.Reverse()); - } - - public static Expression AddPreConditionIfNecessary(this IConditionallyChainable item, Expression ifTrueBranch) - { - if (item.PreCondition == null) - { - return ifTrueBranch; - } - - return Expression.Condition( - item.PreCondition, - ifTrueBranch, - ifTrueBranch.Type.ToDefaultExpression()); - } - public static Expression Chain( this IList items, Func seedValueFactory, @@ -196,8 +171,8 @@ public static Expression Chain( return Chain(items, i => i.First(), seedValueFactory, itemValueFactory, i => i); } - private static Expression Chain( - IList items, + public static Expression Chain( + this IList items, Func, TItem> seedFactory, Func seedValueFactory, Func itemValueFactory, diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index a3b3d1e76..4e02abc79 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -5,9 +5,6 @@ using System.Diagnostics; using System.Linq; using System.Reflection; - using NetStandardPolyfills; - using ObjectPopulation.Enumerables; - using ReadableExpressions.Extensions; #if NET35 using Microsoft.Scripting.Ast; using ReadableExpressions.Translations; @@ -17,6 +14,9 @@ using System.Linq.Expressions; using static System.Linq.Expressions.ExpressionType; #endif + using NetStandardPolyfills; + using ObjectPopulation.Enumerables; + using ReadableExpressions.Extensions; internal static partial class ExpressionExtensions { @@ -65,6 +65,10 @@ public static ConstantExpression ToConstantExpression(this TItem item, Ty [DebuggerStepThrough] public static DefaultExpression ToDefaultExpression(this Type type) => Expression.Default(type); + [DebuggerStepThrough] + public static ConditionalExpression ToIfFalseDefaultCondition(this Expression value, Expression condition) + => Expression.Condition(condition, value, value.Type.ToDefaultExpression()); + public static Expression AndTogether(this IList expressions) { if (expressions.None()) diff --git a/AgileMapper/Extensions/Internal/IConditionallyChainable.cs b/AgileMapper/Extensions/Internal/IConditionallyChainable.cs index 894f7c9fc..6c619f709 100644 --- a/AgileMapper/Extensions/Internal/IConditionallyChainable.cs +++ b/AgileMapper/Extensions/Internal/IConditionallyChainable.cs @@ -8,8 +8,6 @@ internal interface IConditionallyChainable { - Expression PreCondition { get; } - Expression Condition { get; } Expression Value { get; } diff --git a/AgileMapper/Members/Population/MemberPopulator.cs b/AgileMapper/Members/Population/MemberPopulator.cs index 461194d88..fcb554a23 100644 --- a/AgileMapper/Members/Population/MemberPopulator.cs +++ b/AgileMapper/Members/Population/MemberPopulator.cs @@ -124,10 +124,7 @@ private Expression GetBinding(Expression populationGuard) if (MapperData.RuleSet.Settings.AllowGuardedBindings && (populationGuard != null)) { - bindingValue = Expression.Condition( - populationGuard, - bindingValue, - bindingValue.Type.ToDefaultExpression()); + bindingValue = bindingValue.ToIfFalseDefaultCondition(populationGuard); } return MapperData.GetTargetMemberPopulation(bindingValue); @@ -161,20 +158,12 @@ private Expression GetPopulationExpression() } population = MapperData.GetTargetMemberPopulation(finalValue); - - if (dataSource.IsConditional) - { - population = dataSource.AddCondition(population); - } - population = dataSource.Finalise(population); continue; } var memberPopulation = MapperData.GetTargetMemberPopulation(dataSource.Value); - - population = dataSource.AddCondition(memberPopulation, population); - population = dataSource.Finalise(population); + population = dataSource.Finalise(memberPopulation, population); } return population; diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs index f3ca4fbca..ab692448c 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs @@ -513,8 +513,9 @@ private static Expression BuildConditions(DataSourceSet dataSources) public Construction Construction { get; } } - private class Construction : IConditionallyChainable + private class Construction { + private readonly Expression _condition; private readonly Expression _construction; private ParameterExpression _mappingDataObject; @@ -524,29 +525,41 @@ public Construction(ConfiguredObjectFactory configuredFactory, IMemberMapperData UsesMappingDataObjectParameter = configuredFactory.UsesMappingDataObjectParameter; } - private Construction(IList constructions) - : this(constructions.ReverseChain()) - { - UsesMappingDataObjectParameter = constructions.Any(c => c.UsesMappingDataObjectParameter); - } - - public Construction(Expression construction, Expression condition = null) + public Construction( + Expression construction, + Expression condition = null, + bool usesMappingDataObjectParameter = false) { _construction = construction; - Condition = condition; + _condition = condition; + UsesMappingDataObjectParameter = usesMappingDataObjectParameter; } #region Factory Methods public static Construction For(IList constructions, ConstructionKey key) { - var construction = constructions.HasOne() - ? constructions.First() - : new Construction(constructions); + if (constructions.HasOne()) + { + return constructions.First().With(key); + } + + var construction = new Construction( + ReverseChain(constructions), + usesMappingDataObjectParameter: constructions.Any(c => c.UsesMappingDataObjectParameter)); return construction.With(key); } + private static Expression ReverseChain(IList constructions) + { + return constructions.Chain( + cs => cs.Last(), + item => item._construction, + (valueSoFar, item) => Expression.Condition(item._condition, item._construction, valueSoFar), + i => i.Reverse()); + } + public Construction With(ConstructionKey key) { _mappingDataObject = key.MappingData.MapperData.MappingDataObject; @@ -555,12 +568,6 @@ public Construction With(ConstructionKey key) #endregion - public Expression PreCondition => null; - - public Expression Condition { get; } - - Expression IConditionallyChainable.Value => _construction; - public bool UsesMappingDataObjectParameter { get; } public Expression GetConstruction(IObjectMappingData mappingData) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 2274560a7..70c1dc55f 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -1,11 +1,13 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes { using System.Collections.Generic; + using System.Linq; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using DataSources; using Extensions.Internal; using Members; using NetStandardPolyfills; @@ -69,12 +71,48 @@ private static bool DerivedTypesExistForTarget(IMemberMapperData mapperData) #region Short-Circuits - protected override IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) + protected override bool ShortCircuitMapping(MappingCreationContext context, out Expression mapping) + { + var derivedTypeDataSources = DerivedComplexTypeDataSourcesFactory.CreateFor(context.MappingData); + + if (derivedTypeDataSources.None()) + { + return base.ShortCircuitMapping(context, out mapping); + } + + var derivedTypeDataSourceSet = DataSourceSet.For( + derivedTypeDataSources, + context.MapperData, + ValueExpressionBuilders.ValueSequence); + + mapping = derivedTypeDataSourceSet.BuildValue(); + + if (derivedTypeDataSources.Last().IsConditional) + { + context.MappingExpressions.Add(mapping); + return false; + } + + var shortCircuitReturns = GetShortCircuitReturns(context.MappingData).ToArray(); + + if (shortCircuitReturns.Any()) + { + mapping = Expression.Block(shortCircuitReturns.Append(mapping)); + } + + return true; + } + + protected override IEnumerable GetShortCircuitReturns(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; if (SourceObjectCouldBeNull(mapperData)) { + var returnNull = Expression.Return( + mapperData.ReturnLabelTarget, + mapperData.TargetType.ToDefaultExpression()); + yield return Expression.IfThen(mapperData.SourceObject.GetIsDefaultComparison(), returnNull); } @@ -143,9 +181,6 @@ private bool TryGetShortCircuitFactory(ObjectMapperData mapperData, out ISourceS #endregion - protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingData) - => DerivedComplexTypeMappingsFactory.CreateFor(mappingData); - protected override IEnumerable GetObjectPopulation(MappingCreationContext context) { var expressionFactory = context.MapperData.UseMemberInitialisations() diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs index 1dc09cc77..0cc79e8dd 100644 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs @@ -142,10 +142,7 @@ private static void AddDeclaredSourceTypeMappings( if (sourceValueCondition != null) { - derivedTypeMapping = Condition( - sourceValueCondition, - derivedTypeMapping, - derivedTypeMapping.Type.ToDefaultExpression()); + derivedTypeMapping = derivedTypeMapping.ToIfFalseDefaultCondition(sourceValueCondition); } var returnMappingResult = Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryPopulationLoopData.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryPopulationLoopData.cs index cc40bd781..6a6255bcd 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryPopulationLoopData.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryPopulationLoopData.cs @@ -1,12 +1,12 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables.Dictionaries { - using DataSources; - using Extensions.Internal; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using DataSources; + using Extensions.Internal; internal class SourceObjectDictionaryPopulationLoopData : IPopulationLoopData { @@ -85,12 +85,7 @@ public static BinaryExpression GetSourceEnumerableFoundTest( } private Expression GetEnumeratorIfNecessary(Expression getEnumeratorCall) - { - return Expression.Condition( - _sourceEnumerableFound, - getEnumeratorCall, - getEnumeratorCall.Type.ToDefaultExpression()); - } + => getEnumeratorCall.ToIfFalseDefaultCondition(_sourceEnumerableFound); private Expression DisposeEnumeratorIfNecessary(Expression disposeEnumeratorCall) => Expression.IfThen(_sourceEnumerableFound, disposeEnumeratorCall); diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 933a8e2d2..92cce4468 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -4,17 +4,17 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif using Caching; using Extensions; using Extensions.Internal; using Members; using NetStandardPolyfills; using ReadableExpressions.Extensions; -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif internal class EnumerablePopulationBuilder { diff --git a/AgileMapper/ObjectPopulation/MappingCreationContext.cs b/AgileMapper/ObjectPopulation/MappingCreationContext.cs index 0e943913b..7221ab50e 100644 --- a/AgileMapper/ObjectPopulation/MappingCreationContext.cs +++ b/AgileMapper/ObjectPopulation/MappingCreationContext.cs @@ -21,22 +21,20 @@ internal class MappingCreationContext public MappingCreationContext(IObjectMappingData mappingData) { - var mapperData = mappingData.MapperData; - MappingData = mappingData; - MapToNullCondition = GetMapToNullConditionOrNull(mapperData); + MapToNullCondition = GetMapToNullConditionOrNull(MapperData); InstantiateLocalVariable = true; MappingExpressions = new List(); - if (mapperData.RuleSet.Settings.UseSingleRootMappingExpression) + if (RuleSet.Settings.UseSingleRootMappingExpression) { return; } - var basicMapperData = mapperData.WithNoTargetMember(); + var basicMapperData = MapperData.WithNoTargetMember(); - PreMappingCallback = basicMapperData.GetMappingCallbackOrNull(Before, mapperData); - PostMappingCallback = basicMapperData.GetMappingCallbackOrNull(After, mapperData); + PreMappingCallback = basicMapperData.GetMappingCallbackOrNull(Before, MapperData); + PostMappingCallback = basicMapperData.GetMappingCallbackOrNull(After, MapperData); } private static Expression GetMapToNullConditionOrNull(IMemberMapperData mapperData) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 7db65db6d..9a51e79f5 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -5,8 +5,10 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Linq; #if NET35 using Microsoft.Scripting.Ast; + using static Microsoft.Scripting.Ast.ExpressionType; #else using System.Linq.Expressions; + using static System.Linq.Expressions.ExpressionType; #endif using DataSources; using Extensions; @@ -15,11 +17,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using NetStandardPolyfills; using ReadableExpressions; using ReadableExpressions.Extensions; -#if NET35 - using static Microsoft.Scripting.Ast.ExpressionType; -#else - using static System.Linq.Expressions.ExpressionType; -#endif internal abstract class MappingExpressionFactoryBase { @@ -34,23 +31,13 @@ public Expression Create(IObjectMappingData mappingData) GetNullMappingFallbackValue(mapperData)); } - var returnNull = Expression.Return( - mapperData.ReturnLabelTarget, - mapperData.TargetType.ToDefaultExpression()); + var context = new MappingCreationContext(mappingData); - if (MappingAlwaysBranchesToDerivedType(mappingData, out var derivedTypeMappings)) + if (ShortCircuitMapping(context, out var mapping)) { - var shortCircuitReturns = GetShortCircuitReturns(returnNull, mappingData).ToArray(); - - return shortCircuitReturns.Any() - ? Expression.Block(shortCircuitReturns.Append(derivedTypeMappings)) - : derivedTypeMappings; + return mapping; } - var context = new MappingCreationContext(mappingData); - - context.MappingExpressions.AddUnlessNullOrEmpty(derivedTypeMappings); - AddPopulationsAndCallbacks(context); if (NothingIsBeingMapped(context)) @@ -58,7 +45,7 @@ public Expression Create(IObjectMappingData mappingData) return mapperData.IsEntryPoint ? mapperData.TargetObject : Constants.EmptyExpression; } - context.MappingExpressions.InsertRange(0, GetShortCircuitReturns(returnNull, mappingData)); + context.MappingExpressions.InsertRange(0, GetShortCircuitReturns(mappingData)); var mappingBlock = GetMappingBlock(context); @@ -79,23 +66,13 @@ protected virtual bool TargetCannotBeMapped(IObjectMappingData mappingData, out protected virtual Expression GetNullMappingFallbackValue(IMemberMapperData mapperData) => mapperData.TargetType.ToDefaultExpression(); - private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, out Expression derivedTypeMappings) + protected virtual bool ShortCircuitMapping(MappingCreationContext context, out Expression mapping) { - derivedTypeMappings = GetDerivedTypeMappings(mappingData); - - if (derivedTypeMappings.NodeType != Goto) - { - return false; - } - - var returnExpression = (GotoExpression)derivedTypeMappings; - derivedTypeMappings = returnExpression.Value; - return true; + mapping = null; + return false; } - protected virtual Expression GetDerivedTypeMappings(IObjectMappingData mappingData) => Constants.EmptyExpression; - - protected virtual IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) + protected virtual IEnumerable GetShortCircuitReturns(IObjectMappingData mappingData) => Enumerable.Empty; private void AddPopulationsAndCallbacks(MappingCreationContext context) diff --git a/AgileMapper/TypeConversion/ToBoolConverter.cs b/AgileMapper/TypeConversion/ToBoolConverter.cs index 32da2d8cd..9e8135d9e 100644 --- a/AgileMapper/TypeConversion/ToBoolConverter.cs +++ b/AgileMapper/TypeConversion/ToBoolConverter.cs @@ -45,10 +45,7 @@ public Expression GetConversion(Expression sourceValue, Type targetType) var sourceValueConversion = Expression.Condition( sourceEqualsTrueTests, true.ToConstantExpression(typeof(bool?)), - Expression.Condition( - sourceEqualsFalseTests, - false.ToConstantExpression(typeof(bool?)), - typeof(bool?).ToDefaultExpression())); + false.ToConstantExpression(typeof(bool?)).ToIfFalseDefaultCondition(sourceEqualsFalseTests)); return sourceValueConversion; } diff --git a/AgileMapper/TypeConversion/ToNumericConverter.cs b/AgileMapper/TypeConversion/ToNumericConverter.cs index 6f20a9e4b..bfe885644 100644 --- a/AgileMapper/TypeConversion/ToNumericConverter.cs +++ b/AgileMapper/TypeConversion/ToNumericConverter.cs @@ -15,7 +15,7 @@ internal class ToNumericConverter : TryParseConverter { #region Cached Items - public new static readonly ToNumericConverter Instance = new ToNumericConverter(); + public static new readonly ToNumericConverter Instance = new ToNumericConverter(); private static readonly Type[] _coercibleNumericTypes = typeof(TNumeric).GetCoercibleNumericTypes(); @@ -75,26 +75,19 @@ private static Type GetNonEnumSourceType(Expression sourceValue) private static Expression GetBoolToNumericConversion(Expression sourceValue, Type targetType) { - var sourceIsNotNullable = sourceValue.Type == typeof(bool); - - var testValue = sourceIsNotNullable - ? sourceValue - : sourceValue.GetConversionTo(); + var sourceIsNullable = sourceValue.Type != typeof(bool); var boolConversion = Expression.Condition( - testValue, + sourceIsNullable ? sourceValue.GetConversionTo() : sourceValue, One.GetConversionTo(targetType), Zero.GetConversionTo(targetType)); - if (sourceIsNotNullable) + if (sourceIsNullable) { - return boolConversion; + boolConversion = boolConversion.ToIfFalseDefaultCondition(sourceValue.GetIsNotDefaultComparison()); } - return Expression.Condition( - sourceValue.GetIsNotDefaultComparison(), - boolConversion, - boolConversion.Type.ToDefaultExpression()); + return boolConversion; } private static bool IsCoercible(Type sourceType) => _coercibleNumericTypes.Contains(sourceType); diff --git a/AgileMapper/TypeConversion/ToStringConverter.cs b/AgileMapper/TypeConversion/ToStringConverter.cs index d9a29412c..78c9dda64 100644 --- a/AgileMapper/TypeConversion/ToStringConverter.cs +++ b/AgileMapper/TypeConversion/ToStringConverter.cs @@ -118,10 +118,8 @@ private static Expression GetBoolToStringConversion(Expression sourceValue, Type return GetTrueOrFalseTernary(sourceValue); } - var nullTrueOrFalse = Expression.Condition( - Expression.Property(sourceValue, "HasValue"), - GetTrueOrFalseTernary(sourceValue.GetNullableValueAccess()), - typeof(string).ToDefaultExpression()); + var nullTrueOrFalse = GetTrueOrFalseTernary(sourceValue.GetNullableValueAccess()) + .ToIfFalseDefaultCondition(Expression.Property(sourceValue, "HasValue")); return nullTrueOrFalse; } From 9bd50d8473ad45c32ee34de3db8ba6d45dcab769 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 19 Aug 2019 15:59:36 +0100 Subject: [PATCH 12/17] Handling unconditional derived type mappings Detecting derived new element mappings --- AgileMapper/DataSources/ValueExpressionBuilders.cs | 4 ++++ AgileMapper/ObjectPopulation/MappingFactory.cs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs index 26efa469f..80ad24682 100644 --- a/AgileMapper/DataSources/ValueExpressionBuilders.cs +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -64,6 +64,10 @@ private static Expression GetValueSequenceValue(this IDataSource dataSource) { mapping = Expression.IfThen(dataSource.Condition, mapping); } + else if (mapping.NodeType == ExpressionType.Goto) + { + mapping = ((GotoExpression)mapping).Value; + } mapping = dataSource.AddSourceCondition(mapping); diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 05c9215d0..93b07e704 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -143,7 +143,9 @@ public static Expression GetElementMapping( targetElementValue, enumerableIndex); - elementMapperData.Context.IsForNewElement = targetElementValue.NodeType == ExpressionType.Default; + elementMapperData.Context.IsForNewElement = + (targetElementValue.NodeType == ExpressionType.Default) || + (elementMapperData.DeclaredTypeMapperData?.Context.IsForNewElement == true); if (elementMapperData.IsRepeatMapping && elementMapperData.RuleSet.RepeatMappingStrategy.AppliesTo(elementMapperData)) From 40f37280008090bcece2e3d11ce3564e7fe18f60 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 19 Aug 2019 16:17:13 +0100 Subject: [PATCH 13/17] Fixing --- .../DataSources/DerivedComplexTypeDataSourcesFactory.cs | 7 +++++-- AgileMapper/DataSources/ValueExpressionBuilders.cs | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs index 1ca61744b..621507bc4 100644 --- a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs @@ -142,7 +142,7 @@ private static void AddDeclaredSourceTypeDataSources( derivedTypeMapping = derivedTypeMapping.ToIfFalseDefaultCondition(sourceValueCondition); } - var returnMappingResult = Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); + var returnMappingResult = GetReturnMappingResultExpression(declaredTypeMapperData, derivedTypeMapping); var derivedTypeMappingDataSource = new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, @@ -477,10 +477,13 @@ private static Expression GetReturnMappingResultExpression( out derivedTypeMappingData); return (mapping != EmptyExpression) - ? Return(declaredTypeMappingData.MapperData.ReturnLabelTarget, mapping) + ? GetReturnMappingResultExpression(declaredTypeMappingData.MapperData, mapping) : mapping; } + private static Expression GetReturnMappingResultExpression(ObjectMapperData mapperData, Expression mapping) + => Return(mapperData.ReturnLabelTarget, mapping, mapperData.TargetType); + private static Expression GetTargetValidCheckOrNull(this IMemberMapperData mapperData, Type targetType) { if (!mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated()) diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs index 80ad24682..26efa469f 100644 --- a/AgileMapper/DataSources/ValueExpressionBuilders.cs +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -64,10 +64,6 @@ private static Expression GetValueSequenceValue(this IDataSource dataSource) { mapping = Expression.IfThen(dataSource.Condition, mapping); } - else if (mapping.NodeType == ExpressionType.Goto) - { - mapping = ((GotoExpression)mapping).Value; - } mapping = dataSource.AddSourceCondition(mapping); From 98d36d6c3c2c2a5c5ad39dbd2f35aa78bb369aef Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 19 Aug 2019 20:32:33 +0100 Subject: [PATCH 14/17] Fixing unconditional derived type mapping branching --- AgileMapper/DataSources/ValueExpressionBuilders.cs | 4 ++++ .../ComplexTypes/ComplexTypeMappingExpressionFactory.cs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs index 26efa469f..80ad24682 100644 --- a/AgileMapper/DataSources/ValueExpressionBuilders.cs +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -64,6 +64,10 @@ private static Expression GetValueSequenceValue(this IDataSource dataSource) { mapping = Expression.IfThen(dataSource.Condition, mapping); } + else if (mapping.NodeType == ExpressionType.Goto) + { + mapping = ((GotoExpression)mapping).Value; + } mapping = dataSource.AddSourceCondition(mapping); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 70c1dc55f..ee64cf4b6 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -93,6 +93,8 @@ protected override bool ShortCircuitMapping(MappingCreationContext context, out return false; } + mapping = context.MapperData.GetReturnLabel(mapping); + var shortCircuitReturns = GetShortCircuitReturns(context.MappingData).ToArray(); if (shortCircuitReturns.Any()) From f4c96636e8f3f4401a4c4bd9a04a7b6a233f55f6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 20 Aug 2019 11:45:42 +0100 Subject: [PATCH 15/17] Fixing derived type mapping, all tests pass! --- .../DataSources/DerivedComplexTypeDataSourcesFactory.cs | 5 +++++ AgileMapper/DataSources/ValueExpressionBuilders.cs | 4 ---- .../ComplexTypes/ComplexTypeMappingExpressionFactory.cs | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs index 621507bc4..741a2f408 100644 --- a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs @@ -435,6 +435,11 @@ private static void AddDerivedTargetTypeDataSources( derivedTargetType, out var derivedTypeMappingData); + if (derivedTypeMapping == EmptyExpression) + { + continue; + } + var derivedTargetTypeDataSouce = new DerivedComplexTypeDataSource( derivedTypeMappingData.MapperData.SourceMember, targetTypeCondition, diff --git a/AgileMapper/DataSources/ValueExpressionBuilders.cs b/AgileMapper/DataSources/ValueExpressionBuilders.cs index 80ad24682..26efa469f 100644 --- a/AgileMapper/DataSources/ValueExpressionBuilders.cs +++ b/AgileMapper/DataSources/ValueExpressionBuilders.cs @@ -64,10 +64,6 @@ private static Expression GetValueSequenceValue(this IDataSource dataSource) { mapping = Expression.IfThen(dataSource.Condition, mapping); } - else if (mapping.NodeType == ExpressionType.Goto) - { - mapping = ((GotoExpression)mapping).Value; - } mapping = dataSource.AddSourceCondition(mapping); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index ee64cf4b6..a30386a20 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -93,15 +93,17 @@ protected override bool ShortCircuitMapping(MappingCreationContext context, out return false; } - mapping = context.MapperData.GetReturnLabel(mapping); - var shortCircuitReturns = GetShortCircuitReturns(context.MappingData).ToArray(); if (shortCircuitReturns.Any()) { - mapping = Expression.Block(shortCircuitReturns.Append(mapping)); + context.MappingExpressions.AddRange(shortCircuitReturns); } + context.MappingExpressions.Add(mapping); + context.MappingExpressions.Add(context.MapperData.GetReturnLabel(mapping.Type.ToDefaultExpression())); + + mapping = Expression.Block(context.MappingExpressions); return true; } From 4a737b5e18a6ca0d0b0e3ccad86cdbf9ba81a538 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 20 Aug 2019 11:55:23 +0100 Subject: [PATCH 16/17] Organising data source factory files --- .../CustomDataSourceTargetMemberSpecifier.cs | 2 +- ...mDictionaryMappingTargetMemberSpecifier.cs | 2 +- .../Configuration/ConfiguredIgnoredMember.cs | 2 +- .../Dictionaries/CustomDictionaryKey.cs | 2 +- .../Configuration/UserConfigurationSet.cs | 11 +- .../ConfiguredDataSourceFactory.cs | 239 --------- .../Factories/ConfiguredDataSourceFactory.cs | 235 ++++++++- .../Factories/ConfiguredDataSourcesFactory.cs | 28 + ...figuredDictionaryEntryDataSourceFactory.cs | 2 +- .../Factories/DataSourceSetFactory.cs | 10 +- ...SourceFactory.cs => DataSourcesFactory.cs} | 2 +- .../DefaultValueFallbackDataSourceFactory.cs | 2 +- .../DerivedComplexTypeDataSourcesFactory.cs | 2 +- .../DictionaryDataSourceFactory.cs | 2 +- ...OrDefaultValueFallbackDataSourceFactory.cs | 2 +- .../IFallbackDataSourceFactory.cs | 2 +- .../IMaptimeDataSourceFactory.cs | 2 +- ...actory.cs => MaptimeDataSourcesFactory.cs} | 2 +- ...ory.cs => MetaMemberDataSourcesFactory.cs} | 2 +- ...y.cs => SourceMemberDataSourcesFactory.cs} | 2 +- AgileMapper/MappingRuleSet.cs | 12 +- AgileMapper/MappingRuleSetCollection.cs | 2 +- .../Members/MemberMapperDataExtensions.cs | 1 + .../ComplexTypeMappingExpressionFactory.cs | 1 + .../DerivedComplexTypeMappingsFactory.cs | 499 ------------------ .../DictionaryMappingExpressionFactory.cs | 2 +- .../ObjectPopulation/MappingFactory.cs | 6 +- .../ObjectPopulation/ObjectMappingData.cs | 10 +- 28 files changed, 294 insertions(+), 792 deletions(-) delete mode 100644 AgileMapper/DataSources/ConfiguredDataSourceFactory.cs create mode 100644 AgileMapper/DataSources/Factories/ConfiguredDataSourcesFactory.cs rename AgileMapper/DataSources/{ => Factories}/ConfiguredDictionaryEntryDataSourceFactory.cs (95%) rename AgileMapper/DataSources/Factories/{DataSourceFactory.cs => DataSourcesFactory.cs} (51%) rename AgileMapper/DataSources/{ => Factories}/DefaultValueFallbackDataSourceFactory.cs (91%) rename AgileMapper/DataSources/{ => Factories}/DerivedComplexTypeDataSourcesFactory.cs (99%) rename AgileMapper/DataSources/{ => Factories}/DictionaryDataSourceFactory.cs (97%) rename AgileMapper/DataSources/{ => Factories}/ExistingOrDefaultValueFallbackDataSourceFactory.cs (97%) rename AgileMapper/DataSources/{ => Factories}/IFallbackDataSourceFactory.cs (71%) rename AgileMapper/DataSources/{ => Factories}/IMaptimeDataSourceFactory.cs (81%) rename AgileMapper/DataSources/Factories/{MaptimeDataSourceFactory.cs => MaptimeDataSourcesFactory.cs} (96%) rename AgileMapper/DataSources/Factories/{MetaMemberDataSourceFactory.cs => MetaMemberDataSourcesFactory.cs} (99%) rename AgileMapper/DataSources/Factories/{SourceMemberDataSourceFactory.cs => SourceMemberDataSourcesFactory.cs} (98%) delete mode 100644 AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index bc4425731..e42fc7d97 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -7,7 +7,7 @@ using System.Linq.Expressions; using System.Reflection; using AgileMapper.Configuration; - using DataSources; + using DataSources.Factories; using Extensions; using Extensions.Internal; using Members; diff --git a/AgileMapper/Api/Configuration/Dictionaries/CustomDictionaryMappingTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/Dictionaries/CustomDictionaryMappingTargetMemberSpecifier.cs index d4493a5b0..66c0cfabf 100644 --- a/AgileMapper/Api/Configuration/Dictionaries/CustomDictionaryMappingTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/Dictionaries/CustomDictionaryMappingTargetMemberSpecifier.cs @@ -4,7 +4,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Dictionaries using System.Linq.Expressions; using AgileMapper.Configuration; using AgileMapper.Configuration.Dictionaries; - using DataSources; + using DataSources.Factories; #if FEATURE_DYNAMIC using Dynamics; #endif diff --git a/AgileMapper/Configuration/ConfiguredIgnoredMember.cs b/AgileMapper/Configuration/ConfiguredIgnoredMember.cs index e9fbd8189..b74c46dcb 100644 --- a/AgileMapper/Configuration/ConfiguredIgnoredMember.cs +++ b/AgileMapper/Configuration/ConfiguredIgnoredMember.cs @@ -6,7 +6,7 @@ namespace AgileObjects.AgileMapper.Configuration #else using System.Linq.Expressions; #endif - using DataSources; + using DataSources.Factories; using Members; using ReadableExpressions; diff --git a/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs b/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs index 9be9db8d8..cc11334a5 100644 --- a/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs +++ b/AgileMapper/Configuration/Dictionaries/CustomDictionaryKey.cs @@ -2,7 +2,7 @@ { using System; using System.Dynamic; - using DataSources; + using DataSources.Factories; using Members; using ReadableExpressions.Extensions; #if NET35 diff --git a/AgileMapper/Configuration/UserConfigurationSet.cs b/AgileMapper/Configuration/UserConfigurationSet.cs index d1bb91764..295224e0e 100644 --- a/AgileMapper/Configuration/UserConfigurationSet.cs +++ b/AgileMapper/Configuration/UserConfigurationSet.cs @@ -3,7 +3,13 @@ using System; using System.Collections.Generic; using System.Linq; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif using DataSources; + using DataSources.Factories; using Dictionaries; using Extensions; using Extensions.Internal; @@ -11,11 +17,6 @@ using ObjectPopulation; using Projection; using ReadableExpressions.Extensions; -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif internal class UserConfigurationSet { diff --git a/AgileMapper/DataSources/ConfiguredDataSourceFactory.cs b/AgileMapper/DataSources/ConfiguredDataSourceFactory.cs deleted file mode 100644 index 284cdea9c..000000000 --- a/AgileMapper/DataSources/ConfiguredDataSourceFactory.cs +++ /dev/null @@ -1,239 +0,0 @@ -namespace AgileObjects.AgileMapper.DataSources -{ -#if NET35 - using System; -#endif - using Configuration; - using Members; -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif - - internal class ConfiguredDataSourceFactory : - UserConfiguredItemBase, - IPotentialAutoCreatedItem -#if NET35 - , IComparable -#endif - { - private readonly ConfiguredLambdaInfo _dataSourceLambda; - private bool _valueCouldBeSourceMember; - private MappingConfigInfo _reverseConfigInfo; - private bool _isReversal; - - public ConfiguredDataSourceFactory( - MappingConfigInfo configInfo, - ConfiguredLambdaInfo dataSourceLambda, - QualifiedMember targetMember) - : base(configInfo, targetMember) - { - _dataSourceLambda = dataSourceLambda; - } - - public ConfiguredDataSourceFactory( - MappingConfigInfo configInfo, - ConfiguredLambdaInfo dataSourceLambda, - LambdaExpression targetMemberLambda, - bool valueCouldBeSourceMember) - : base(configInfo, targetMemberLambda) - { - _valueCouldBeSourceMember = valueCouldBeSourceMember; - _dataSourceLambda = dataSourceLambda; - } - - public bool CannotBeReversed(out string reason) => CannotBeReversed(out _, out reason); - - private bool CannotBeReversed(out QualifiedMember targetMember, out string reason) - { - if (_valueCouldBeSourceMember == false) - { - targetMember = null; - reason = $"configured value '{_dataSourceLambda.GetDescription(ConfigInfo)}' is not a source member"; - return true; - } - - if (ConfigInfo.HasCondition) - { - targetMember = null; - reason = $"configuration has condition '{ConfigInfo.GetConditionDescription(ConfigInfo)}'"; - return true; - } - - if (!TargetMember.IsReadable) - { - targetMember = null; - reason = $"target member '{GetTargetMemberPath()}' is not readable, so cannot be used as a source member"; - return true; - } - - if (!_dataSourceLambda.IsSourceMember(out var sourceMemberLambda)) - { - targetMember = null; - reason = $"configured value '{_dataSourceLambda.GetDescription(ConfigInfo)}' is not a source member"; - return true; - } - - targetMember = sourceMemberLambda.ToTargetMemberOrNull( - ConfigInfo.SourceType, - ConfigInfo.MapperContext, - out reason); - - if (targetMember != null) - { - return false; - } - - var sourceMember = sourceMemberLambda.ToSourceMember(ConfigInfo.MapperContext); - var sourceMemberPath = sourceMember.GetFriendlySourcePath(ConfigInfo); - - reason = $"source member '{sourceMemberPath}' is not a useable target member. {reason}"; - return true; - - } - - public ConfiguredDataSourceFactory CreateReverseIfAppropriate(bool isAutoReversal) - { - if (CannotBeReversed(out var targetMember, out _)) - { - return null; - } - - var reverseConfigInfo = GetReverseConfigInfo(); - - var sourceParameter = Parameters.Create(reverseConfigInfo.SourceType, "source"); - var sourceMemberAccess = TargetMember.GetQualifiedAccess(sourceParameter); - - var sourceMemberAccessLambda = Expression.Lambda( - Expression.GetFuncType(sourceParameter.Type, sourceMemberAccess.Type), - sourceMemberAccess, - sourceParameter); - - var sourceMemberLambdaInfo = ConfiguredLambdaInfo.For(sourceMemberAccessLambda); - - return new ConfiguredDataSourceFactory(reverseConfigInfo, sourceMemberLambdaInfo, targetMember) - { - _isReversal = true, - WasAutoCreated = isAutoReversal - }; - } - - public MappingConfigInfo GetReverseConfigInfo() - { - return _reverseConfigInfo ?? (_reverseConfigInfo = ConfigInfo - .Copy() - .ForSourceType(ConfigInfo.TargetType) - .ForTargetType(ConfigInfo.SourceType) - .ForSourceValueType(TargetMember.Type)); - } - - public override bool ConflictsWith(UserConfiguredItemBase otherConfiguredItem) - { - if (!base.ConflictsWith(otherConfiguredItem)) - { - return false; - } - - var otherDataSource = otherConfiguredItem as ConfiguredDataSourceFactory; - var isOtherDataSource = otherDataSource != null; - var dataSourceLambdasAreTheSame = HasSameDataSourceLambdaAs(otherDataSource); - - if (WasAutoCreated && - (otherConfiguredItem is IPotentialAutoCreatedItem otherItem) && - !otherItem.WasAutoCreated) - { - return isOtherDataSource && dataSourceLambdasAreTheSame; - } - - if (isOtherDataSource == false) - { - return true; - } - - if (SourceAndTargetTypesAreTheSame(otherDataSource)) - { - return true; - } - - return dataSourceLambdasAreTheSame; - } - - #region ConflictsWith Helpers - - private bool HasSameDataSourceLambdaAs(ConfiguredDataSourceFactory otherDataSource) - => _dataSourceLambda.IsSameAs(otherDataSource?._dataSourceLambda); - - protected override bool MembersConflict(UserConfiguredItemBase otherConfiguredItem) - => TargetMember.LeafMember.Equals(otherConfiguredItem.TargetMember.LeafMember); - - #endregion - - public string GetConflictMessage(ConfiguredDataSourceFactory conflictingDataSource) - { - var existingDataSource = conflictingDataSource.GetDataSourceDescription(); - - var reason = conflictingDataSource._isReversal - ? " from an automatically-configured reverse data source" : null; - - - return $"{GetTargetMemberPath()} already has configured data source '{existingDataSource}'{reason}"; - } - - public string GetDescription() - { - var sourceMemberPath = GetDataSourceDescription(); - var targetMemberPath = GetTargetMemberPath(); - - return sourceMemberPath + " -> " + targetMemberPath; - } - - private string GetDataSourceDescription() => _dataSourceLambda.GetDescription(ConfigInfo); - - private string GetTargetMemberPath() => TargetMember.GetFriendlyTargetPath(ConfigInfo); - - public override bool AppliesTo(IBasicMapperData mapperData) - => base.AppliesTo(mapperData) && _dataSourceLambda.Supports(mapperData.RuleSet); - - protected override bool TargetMembersAreCompatible(IBasicMapperData mapperData) - { - if (base.TargetMembersAreCompatible(mapperData)) - { - return true; - } - - return TargetMember.IsRoot && TargetMember.HasCompatibleType(mapperData.TargetMember.Type); - } - - public IConfiguredDataSource Create(IMemberMapperData mapperData) - { - var configuredCondition = GetConditionOrNull(mapperData); - var value = _dataSourceLambda.GetBody(mapperData); - - return new ConfiguredDataSource(configuredCondition, value, mapperData); - } - - #region IPotentialAutoCreatedItem Members - - public bool WasAutoCreated { get; private set; } - - public IPotentialAutoCreatedItem Clone() - { - return new ConfiguredDataSourceFactory(ConfigInfo, _dataSourceLambda, TargetMember) - { - _valueCouldBeSourceMember = _valueCouldBeSourceMember, - WasAutoCreated = true - }; - } - - public bool IsReplacementFor(IPotentialAutoCreatedItem autoCreatedDataSourceFactory) - => ConflictsWith((ConfiguredDataSourceFactory)autoCreatedDataSourceFactory); - - #endregion - -#if NET35 - int IComparable.CompareTo(ConfiguredDataSourceFactory other) - => DoComparisonTo(other); -#endif - } -} \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs index bac98be68..e2485dd10 100644 --- a/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/ConfiguredDataSourceFactory.cs @@ -1,28 +1,237 @@ namespace AgileObjects.AgileMapper.DataSources.Factories { - using System.Collections.Generic; - using Extensions.Internal; +#if NET35 + using System; + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif + using Configuration; + using Members; - internal static class ConfiguredDataSourceFactory + internal class ConfiguredDataSourceFactory : + UserConfiguredItemBase, + IPotentialAutoCreatedItem +#if NET35 + , IComparable +#endif { - public static IEnumerable Create(DataSourceFindContext context) + private readonly ConfiguredLambdaInfo _dataSourceLambda; + private bool _valueCouldBeSourceMember; + private MappingConfigInfo _reverseConfigInfo; + private bool _isReversal; + + public ConfiguredDataSourceFactory( + MappingConfigInfo configInfo, + ConfiguredLambdaInfo dataSourceLambda, + QualifiedMember targetMember) + : base(configInfo, targetMember) + { + _dataSourceLambda = dataSourceLambda; + } + + public ConfiguredDataSourceFactory( + MappingConfigInfo configInfo, + ConfiguredLambdaInfo dataSourceLambda, + LambdaExpression targetMemberLambda, + bool valueCouldBeSourceMember) + : base(configInfo, targetMemberLambda) { - if (context.ConfiguredDataSources.None()) + _valueCouldBeSourceMember = valueCouldBeSourceMember; + _dataSourceLambda = dataSourceLambda; + } + + public bool CannotBeReversed(out string reason) => CannotBeReversed(out _, out reason); + + private bool CannotBeReversed(out QualifiedMember targetMember, out string reason) + { + if (_valueCouldBeSourceMember == false) + { + targetMember = null; + reason = $"configured value '{_dataSourceLambda.GetDescription(ConfigInfo)}' is not a source member"; + return true; + } + + if (ConfigInfo.HasCondition) { - yield break; + targetMember = null; + reason = $"configuration has condition '{ConfigInfo.GetConditionDescription(ConfigInfo)}'"; + return true; } - foreach (var configuredDataSource in context.ConfiguredDataSources) + if (!TargetMember.IsReadable) { - yield return context.GetFinalDataSource(configuredDataSource); + targetMember = null; + reason = $"target member '{GetTargetMemberPath()}' is not readable, so cannot be used as a source member"; + return true; + } + + if (!_dataSourceLambda.IsSourceMember(out var sourceMemberLambda)) + { + targetMember = null; + reason = $"configured value '{_dataSourceLambda.GetDescription(ConfigInfo)}' is not a source member"; + return true; + } - if (!configuredDataSource.IsConditional) - { - yield break; - } + targetMember = sourceMemberLambda.ToTargetMemberOrNull( + ConfigInfo.SourceType, + ConfigInfo.MapperContext, + out reason); - ++context.DataSourceIndex; + if (targetMember != null) + { + return false; } + + var sourceMember = sourceMemberLambda.ToSourceMember(ConfigInfo.MapperContext); + var sourceMemberPath = sourceMember.GetFriendlySourcePath(ConfigInfo); + + reason = $"source member '{sourceMemberPath}' is not a useable target member. {reason}"; + return true; + } + + public ConfiguredDataSourceFactory CreateReverseIfAppropriate(bool isAutoReversal) + { + if (CannotBeReversed(out var targetMember, out _)) + { + return null; + } + + var reverseConfigInfo = GetReverseConfigInfo(); + + var sourceParameter = Parameters.Create(reverseConfigInfo.SourceType, "source"); + var sourceMemberAccess = TargetMember.GetQualifiedAccess(sourceParameter); + + var sourceMemberAccessLambda = Expression.Lambda( + Expression.GetFuncType(sourceParameter.Type, sourceMemberAccess.Type), + sourceMemberAccess, + sourceParameter); + + var sourceMemberLambdaInfo = ConfiguredLambdaInfo.For(sourceMemberAccessLambda); + + return new ConfiguredDataSourceFactory(reverseConfigInfo, sourceMemberLambdaInfo, targetMember) + { + _isReversal = true, + WasAutoCreated = isAutoReversal + }; + } + + public MappingConfigInfo GetReverseConfigInfo() + { + return _reverseConfigInfo ?? (_reverseConfigInfo = ConfigInfo + .Copy() + .ForSourceType(ConfigInfo.TargetType) + .ForTargetType(ConfigInfo.SourceType) + .ForSourceValueType(TargetMember.Type)); + } + + public override bool ConflictsWith(UserConfiguredItemBase otherConfiguredItem) + { + if (!base.ConflictsWith(otherConfiguredItem)) + { + return false; + } + + var otherDataSource = otherConfiguredItem as ConfiguredDataSourceFactory; + var isOtherDataSource = otherDataSource != null; + var dataSourceLambdasAreTheSame = HasSameDataSourceLambdaAs(otherDataSource); + + if (WasAutoCreated && + (otherConfiguredItem is IPotentialAutoCreatedItem otherItem) && + !otherItem.WasAutoCreated) + { + return isOtherDataSource && dataSourceLambdasAreTheSame; + } + + if (isOtherDataSource == false) + { + return true; + } + + if (SourceAndTargetTypesAreTheSame(otherDataSource)) + { + return true; + } + + return dataSourceLambdasAreTheSame; + } + + #region ConflictsWith Helpers + + private bool HasSameDataSourceLambdaAs(ConfiguredDataSourceFactory otherDataSource) + => _dataSourceLambda.IsSameAs(otherDataSource?._dataSourceLambda); + + protected override bool MembersConflict(UserConfiguredItemBase otherConfiguredItem) + => TargetMember.LeafMember.Equals(otherConfiguredItem.TargetMember.LeafMember); + + #endregion + + public string GetConflictMessage(ConfiguredDataSourceFactory conflictingDataSource) + { + var existingDataSource = conflictingDataSource.GetDataSourceDescription(); + + var reason = conflictingDataSource._isReversal + ? " from an automatically-configured reverse data source" : null; + + + return $"{GetTargetMemberPath()} already has configured data source '{existingDataSource}'{reason}"; + } + + public string GetDescription() + { + var sourceMemberPath = GetDataSourceDescription(); + var targetMemberPath = GetTargetMemberPath(); + + return sourceMemberPath + " -> " + targetMemberPath; + } + + private string GetDataSourceDescription() => _dataSourceLambda.GetDescription(ConfigInfo); + + private string GetTargetMemberPath() => TargetMember.GetFriendlyTargetPath(ConfigInfo); + + public override bool AppliesTo(IBasicMapperData mapperData) + => base.AppliesTo(mapperData) && _dataSourceLambda.Supports(mapperData.RuleSet); + + protected override bool TargetMembersAreCompatible(IBasicMapperData mapperData) + { + if (base.TargetMembersAreCompatible(mapperData)) + { + return true; + } + + return TargetMember.IsRoot && TargetMember.HasCompatibleType(mapperData.TargetMember.Type); + } + + public IConfiguredDataSource Create(IMemberMapperData mapperData) + { + var configuredCondition = GetConditionOrNull(mapperData); + var value = _dataSourceLambda.GetBody(mapperData); + + return new ConfiguredDataSource(configuredCondition, value, mapperData); + } + + #region IPotentialAutoCreatedItem Members + + public bool WasAutoCreated { get; private set; } + + public IPotentialAutoCreatedItem Clone() + { + return new ConfiguredDataSourceFactory(ConfigInfo, _dataSourceLambda, TargetMember) + { + _valueCouldBeSourceMember = _valueCouldBeSourceMember, + WasAutoCreated = true + }; + } + + public bool IsReplacementFor(IPotentialAutoCreatedItem autoCreatedDataSourceFactory) + => ConflictsWith((ConfiguredDataSourceFactory)autoCreatedDataSourceFactory); + + #endregion + +#if NET35 + int IComparable.CompareTo(ConfiguredDataSourceFactory other) + => DoComparisonTo(other); +#endif } } \ No newline at end of file diff --git a/AgileMapper/DataSources/Factories/ConfiguredDataSourcesFactory.cs b/AgileMapper/DataSources/Factories/ConfiguredDataSourcesFactory.cs new file mode 100644 index 000000000..996364a34 --- /dev/null +++ b/AgileMapper/DataSources/Factories/ConfiguredDataSourcesFactory.cs @@ -0,0 +1,28 @@ +namespace AgileObjects.AgileMapper.DataSources.Factories +{ + using System.Collections.Generic; + using Extensions.Internal; + + internal static class ConfiguredDataSourcesFactory + { + public static IEnumerable Create(DataSourceFindContext context) + { + if (context.ConfiguredDataSources.None()) + { + yield break; + } + + foreach (var configuredDataSource in context.ConfiguredDataSources) + { + yield return context.GetFinalDataSource(configuredDataSource); + + if (!configuredDataSource.IsConditional) + { + yield break; + } + + ++context.DataSourceIndex; + } + } + } +} \ No newline at end of file diff --git a/AgileMapper/DataSources/ConfiguredDictionaryEntryDataSourceFactory.cs b/AgileMapper/DataSources/Factories/ConfiguredDictionaryEntryDataSourceFactory.cs similarity index 95% rename from AgileMapper/DataSources/ConfiguredDictionaryEntryDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/ConfiguredDictionaryEntryDataSourceFactory.cs index 04f72efec..e79ec6f81 100644 --- a/AgileMapper/DataSources/ConfiguredDictionaryEntryDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/ConfiguredDictionaryEntryDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using Configuration; using Members; diff --git a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs index 4e376e924..031e57aa7 100644 --- a/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourceSetFactory.cs @@ -18,12 +18,12 @@ internal static class DataSourceSetFactory new ComplexTypeMappingRootDataSourceFactory() }; - private static readonly DataSourceFactory[] _childDataSourceFactories = + private static readonly DataSourcesFactory[] _childDataSourceFactories = { - ConfiguredDataSourceFactory.Create, - MaptimeDataSourceFactory.Create, - SourceMemberDataSourceFactory.Create, - MetaMemberDataSourceFactory.Create + ConfiguredDataSourcesFactory.Create, + MaptimeDataSourcesFactory.Create, + SourceMemberDataSourcesFactory.Create, + MetaMemberDataSourcesFactory.Create }; public static DataSourceSet CreateFor(IObjectMappingData rootMappingData) diff --git a/AgileMapper/DataSources/Factories/DataSourceFactory.cs b/AgileMapper/DataSources/Factories/DataSourcesFactory.cs similarity index 51% rename from AgileMapper/DataSources/Factories/DataSourceFactory.cs rename to AgileMapper/DataSources/Factories/DataSourcesFactory.cs index ca0ec81a3..feeb31788 100644 --- a/AgileMapper/DataSources/Factories/DataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/DataSourcesFactory.cs @@ -2,5 +2,5 @@ { using System.Collections.Generic; - internal delegate IEnumerable DataSourceFactory(DataSourceFindContext context); + internal delegate IEnumerable DataSourcesFactory(DataSourceFindContext context); } \ No newline at end of file diff --git a/AgileMapper/DataSources/DefaultValueFallbackDataSourceFactory.cs b/AgileMapper/DataSources/Factories/DefaultValueFallbackDataSourceFactory.cs similarity index 91% rename from AgileMapper/DataSources/DefaultValueFallbackDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/DefaultValueFallbackDataSourceFactory.cs index 84a7d58cc..4f08cae79 100644 --- a/AgileMapper/DataSources/DefaultValueFallbackDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/DefaultValueFallbackDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using Members; diff --git a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs similarity index 99% rename from AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs rename to AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs index 741a2f408..e4e6d64bb 100644 --- a/AgileMapper/DataSources/DerivedComplexTypeDataSourcesFactory.cs +++ b/AgileMapper/DataSources/Factories/DerivedComplexTypeDataSourcesFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using System; using System.Collections.Generic; diff --git a/AgileMapper/DataSources/DictionaryDataSourceFactory.cs b/AgileMapper/DataSources/Factories/DictionaryDataSourceFactory.cs similarity index 97% rename from AgileMapper/DataSources/DictionaryDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/DictionaryDataSourceFactory.cs index 3d3a99d8e..401e819f5 100644 --- a/AgileMapper/DataSources/DictionaryDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/DictionaryDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using System; using System.Collections.Generic; diff --git a/AgileMapper/DataSources/ExistingOrDefaultValueFallbackDataSourceFactory.cs b/AgileMapper/DataSources/Factories/ExistingOrDefaultValueFallbackDataSourceFactory.cs similarity index 97% rename from AgileMapper/DataSources/ExistingOrDefaultValueFallbackDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/ExistingOrDefaultValueFallbackDataSourceFactory.cs index f136fd257..38f5ed30c 100644 --- a/AgileMapper/DataSources/ExistingOrDefaultValueFallbackDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/ExistingOrDefaultValueFallbackDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { #if NET35 using Microsoft.Scripting.Ast; diff --git a/AgileMapper/DataSources/IFallbackDataSourceFactory.cs b/AgileMapper/DataSources/Factories/IFallbackDataSourceFactory.cs similarity index 71% rename from AgileMapper/DataSources/IFallbackDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/IFallbackDataSourceFactory.cs index d3d4c2ddf..0cd813e6b 100644 --- a/AgileMapper/DataSources/IFallbackDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/IFallbackDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using Members; diff --git a/AgileMapper/DataSources/IMaptimeDataSourceFactory.cs b/AgileMapper/DataSources/Factories/IMaptimeDataSourceFactory.cs similarity index 81% rename from AgileMapper/DataSources/IMaptimeDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/IMaptimeDataSourceFactory.cs index 0834e3fa8..25920ac35 100644 --- a/AgileMapper/DataSources/IMaptimeDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/IMaptimeDataSourceFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.DataSources +namespace AgileObjects.AgileMapper.DataSources.Factories { using System.Collections.Generic; using Members; diff --git a/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MaptimeDataSourcesFactory.cs similarity index 96% rename from AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/MaptimeDataSourcesFactory.cs index 85527b3fe..34b2a8a93 100644 --- a/AgileMapper/DataSources/Factories/MaptimeDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MaptimeDataSourcesFactory.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Extensions.Internal; - internal static class MaptimeDataSourceFactory + internal static class MaptimeDataSourcesFactory { private static readonly IMaptimeDataSourceFactory[] _mapTimeDataSourceFactories = { diff --git a/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs b/AgileMapper/DataSources/Factories/MetaMemberDataSourcesFactory.cs similarity index 99% rename from AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/MetaMemberDataSourcesFactory.cs index 20322ef6b..ca5df75a2 100644 --- a/AgileMapper/DataSources/Factories/MetaMemberDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/MetaMemberDataSourcesFactory.cs @@ -17,7 +17,7 @@ using ReadableExpressions.Extensions; using TypeConversion; - internal static class MetaMemberDataSourceFactory + internal static class MetaMemberDataSourcesFactory { public static IEnumerable Create(DataSourceFindContext context) { diff --git a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs b/AgileMapper/DataSources/Factories/SourceMemberDataSourcesFactory.cs similarity index 98% rename from AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs rename to AgileMapper/DataSources/Factories/SourceMemberDataSourcesFactory.cs index e9cd96a2c..17d3d7528 100644 --- a/AgileMapper/DataSources/Factories/SourceMemberDataSourceFactory.cs +++ b/AgileMapper/DataSources/Factories/SourceMemberDataSourcesFactory.cs @@ -4,7 +4,7 @@ using Extensions.Internal; using Members; - internal static class SourceMemberDataSourceFactory + internal static class SourceMemberDataSourcesFactory { public static IEnumerable Create(DataSourceFindContext context) { diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index 9d015972e..e8c4e7230 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -1,16 +1,16 @@ namespace AgileObjects.AgileMapper { - using DataSources; - using Extensions.Internal; - using Members.Population; - using ObjectPopulation.Enumerables; - using ObjectPopulation.MapperKeys; - using ObjectPopulation.RepeatedMappings; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using DataSources.Factories; + using Extensions.Internal; + using Members.Population; + using ObjectPopulation.Enumerables; + using ObjectPopulation.MapperKeys; + using ObjectPopulation.RepeatedMappings; internal class MappingRuleSet { diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 8b9b91372..d61223f9e 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -1,7 +1,7 @@ namespace AgileObjects.AgileMapper { using System.Collections.Generic; - using DataSources; + using DataSources.Factories; using Extensions.Internal; using Members.Population; using ObjectPopulation.Enumerables; diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 2ea9abd07..4d08d9ef8 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -12,6 +12,7 @@ namespace AgileObjects.AgileMapper.Members using System.Reflection; using Configuration; using DataSources; + using DataSources.Factories; using Dictionaries; using Extensions; using Extensions.Internal; diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index a30386a20..b11252c20 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -8,6 +8,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using System.Linq.Expressions; #endif using DataSources; + using DataSources.Factories; using Extensions.Internal; using Members; using NetStandardPolyfills; diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs deleted file mode 100644 index 0cc79e8dd..000000000 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ /dev/null @@ -1,499 +0,0 @@ -namespace AgileObjects.AgileMapper.ObjectPopulation -{ - using System; - using System.Collections.Generic; - using System.Linq; -#if NET35 - using Microsoft.Scripting.Ast; - using static Microsoft.Scripting.Ast.Expression; -#else - using System.Linq.Expressions; - using static System.Linq.Expressions.Expression; -#endif - using Configuration; - using DataSources; - using Extensions; - using Extensions.Internal; - using Members; - using NetStandardPolyfills; - using static Constants; - - internal static class DerivedComplexTypeMappingsFactory - { - public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) - { - var declaredTypeMapperData = declaredTypeMappingData.MapperData; - - if (DoNotMapDerivedTypes(declaredTypeMapperData)) - { - return EmptyExpression; - } - - var derivedSourceTypes = GetDerivedSourceTypesIfNecessary(declaredTypeMapperData); - var hasDerivedSourceTypes = derivedSourceTypes.Any(); - var hasNoDerivedSourceTypes = !hasDerivedSourceTypes; - - var derivedTargetTypes = GetDerivedTargetTypesIfNecessary(declaredTypeMapperData); - var hasDerivedTargetTypes = derivedTargetTypes.Any(); - - var derivedTypePairs = GetTypePairsFor(declaredTypeMapperData, declaredTypeMapperData); - var hasDerivedTypePairs = derivedTypePairs.Any(); - - if (hasNoDerivedSourceTypes && !hasDerivedTargetTypes && !hasDerivedTypePairs) - { - return EmptyExpression; - } - - var derivedTypeMappingExpressions = new List(); - - if (hasDerivedTypePairs) - { - AddDeclaredSourceTypeMappings( - derivedTypePairs, - declaredTypeMappingData, - derivedTypeMappingExpressions, - out var declaredTypeHasUnconditionalTypePair); - - if (declaredTypeHasUnconditionalTypePair && hasNoDerivedSourceTypes) - { - return derivedTypeMappingExpressions.First(); - } - } - - var typedObjectVariables = new List(); - - if (hasDerivedSourceTypes) - { - AddDerivedSourceTypeMappings( - derivedSourceTypes, - declaredTypeMappingData, - typedObjectVariables, - derivedTypeMappingExpressions); - } - - if (hasDerivedTargetTypes) - { - AddDerivedTargetTypeMappings( - declaredTypeMappingData, - derivedTargetTypes, - derivedTypeMappingExpressions); - } - - if (derivedTypeMappingExpressions.None()) - { - return EmptyExpression; - } - - return typedObjectVariables.Any() - ? Block(typedObjectVariables, derivedTypeMappingExpressions) - : derivedTypeMappingExpressions.HasOne() - ? derivedTypeMappingExpressions.First() - : Block(derivedTypeMappingExpressions); - } - - private static bool DoNotMapDerivedTypes(IMemberMapperData mapperData) - { - if (mapperData.Context.IsForDerivedType) - { - return !mapperData.TargetType.IsInterface(); - } - - return mapperData.HasSameSourceAsParent(); - } - - private static ICollection GetDerivedSourceTypesIfNecessary(IMemberMapperData mapperData) - { - return mapperData.RuleSet.Settings.CheckDerivedSourceTypes - ? mapperData.GetDerivedSourceTypes() - : EmptyTypeArray; - } - - private static ICollection GetDerivedTargetTypesIfNecessary(IMemberMapperData mapperData) - { - return mapperData.TargetCouldBePopulated() - ? mapperData.GetDerivedTargetTypes() - : EmptyTypeArray; - } - - private static void AddDeclaredSourceTypeMappings( - IEnumerable derivedTypePairs, - IObjectMappingData declaredTypeMappingData, - ICollection derivedTypeMappingExpressions, - out bool declaredTypeHasUnconditionalTypePair) - { - var declaredTypeMapperData = declaredTypeMappingData.MapperData; - - derivedTypePairs = derivedTypePairs - .OrderBy(tp => tp.DerivedSourceType, TypeComparer.MostToLeastDerived); - - foreach (var derivedTypePair in derivedTypePairs) - { - var condition = GetTypePairCondition(derivedTypePair, declaredTypeMapperData); - - var sourceValue = GetDerivedTypeSourceValue( - derivedTypePair, - declaredTypeMappingData, - out var sourceValueCondition); - - var derivedTypeMapping = DerivedMappingFactory.GetDerivedTypeMapping( - declaredTypeMappingData, - sourceValue, - derivedTypePair.DerivedTargetType); - - if (sourceValueCondition != null) - { - derivedTypeMapping = derivedTypeMapping.ToIfFalseDefaultCondition(sourceValueCondition); - } - - var returnMappingResult = Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); - - if (condition == null) - { - declaredTypeHasUnconditionalTypePair = true; - derivedTypeMappingExpressions.Add(returnMappingResult); - return; - } - - var ifConditionThenMap = IfThen(condition, returnMappingResult); - - derivedTypeMappingExpressions.Add(ifConditionThenMap); - } - - declaredTypeHasUnconditionalTypePair = false; - } - - private static Expression GetTypePairCondition(DerivedTypePair derivedTypePair, IMemberMapperData declaredTypeMapperData) - { - var condition = GetTargetValidCheckOrNull(derivedTypePair.DerivedTargetType, declaredTypeMapperData); - - if (!derivedTypePair.HasConfiguredCondition) - { - return condition; - } - - var pairCondition = derivedTypePair.GetConditionOrNull(declaredTypeMapperData); - - return (condition != null) ? AndAlso(pairCondition, condition) : pairCondition; - } - - private static Expression GetDerivedTypeSourceValue( - DerivedTypePair derivedTypePair, - IObjectMappingData declaredTypeMappingData, - out Expression sourceValueCondition) - { - if (!derivedTypePair.IsImplementationPairing) - { - sourceValueCondition = null; - return declaredTypeMappingData.MapperData.SourceObject; - } - - var implementationMappingData = declaredTypeMappingData - .WithTypes(derivedTypePair.DerivedSourceType, derivedTypePair.DerivedTargetType); - - if (implementationMappingData.IsTargetConstructable()) - { - sourceValueCondition = null; - return declaredTypeMappingData.MapperData.SourceObject; - } - - // Derived Type is an implementation Type for an unconstructable target Type, - // and is itself unconstructable; only way we get here is if a ToTarget data - // source has been configured: - var toTargetDataSource = implementationMappingData - .GetToTargetDataSourceOrNullForTargetType(); - - sourceValueCondition = toTargetDataSource.IsConditional - ? toTargetDataSource.Condition.Replace( - implementationMappingData.MapperData.SourceObject, - declaredTypeMappingData.MapperData.SourceObject, - ExpressionEvaluation.Equivalator) - : null; - - return toTargetDataSource.Value.Replace( - implementationMappingData.MapperData.SourceObject, - declaredTypeMappingData.MapperData.SourceObject, - ExpressionEvaluation.Equivalator); - } - - private static void AddDerivedSourceTypeMappings( - IEnumerable derivedSourceTypes, - IObjectMappingData declaredTypeMappingData, - ICollection typedObjectVariables, - IList derivedTypeMappingExpressions) - { - var declaredTypeMapperData = declaredTypeMappingData.MapperData; - var insertionOffset = derivedTypeMappingExpressions.Count; - - var orderedDerivedSourceTypes = derivedSourceTypes - .OrderBy(t => t, TypeComparer.MostToLeastDerived); - - foreach (var derivedSourceType in orderedDerivedSourceTypes) - { - var derivedSourceCheck = new DerivedSourceTypeCheck(derivedSourceType); - var typedVariableAssignment = derivedSourceCheck.GetTypedVariableAssignment(declaredTypeMapperData); - - typedObjectVariables.Add(derivedSourceCheck.TypedVariable); - derivedTypeMappingExpressions.Insert(typedVariableAssignment, insertionOffset); - - var targetType = declaredTypeMapperData.TargetType.GetRuntimeTargetType(derivedSourceType); - - var outerCondition = derivedSourceCheck.TypeCheck; - outerCondition = AppendTargetValidCheckIfAppropriate(outerCondition, targetType, declaredTypeMapperData); - - var derivedTypePairs = GetTypePairsFor(derivedSourceType, targetType, declaredTypeMapperData); - - Expression ifSourceVariableIsDerivedTypeThenMap; - - if (derivedTypePairs.None()) - { - ifSourceVariableIsDerivedTypeThenMap = GetIfConditionThenMapExpression( - declaredTypeMappingData, - outerCondition, - derivedSourceCheck.TypedVariable, - targetType); - - derivedTypeMappingExpressions.Insert(ifSourceVariableIsDerivedTypeThenMap, insertionOffset); - continue; - } - - var hasUnconditionalDerivedTargetTypeMapping = HasUnconditionalDerivedTargetTypeMapping( - derivedTypePairs, - declaredTypeMapperData, - out var unconditionalDerivedTargetType, - out var groupedTypePairs); - - if (hasUnconditionalDerivedTargetTypeMapping) - { - ifSourceVariableIsDerivedTypeThenMap = GetIfConditionThenMapExpression( - declaredTypeMappingData, - outerCondition, - derivedSourceCheck.TypedVariable, - unconditionalDerivedTargetType); - - derivedTypeMappingExpressions.Insert(ifSourceVariableIsDerivedTypeThenMap, insertionOffset); - continue; - } - - ifSourceVariableIsDerivedTypeThenMap = GetMapFromConditionOrDefaultExpression( - declaredTypeMappingData, - outerCondition, - derivedSourceCheck.TypedVariable, - groupedTypePairs, - targetType); - - derivedTypeMappingExpressions.Insert(ifSourceVariableIsDerivedTypeThenMap, insertionOffset); - } - } - - private static bool HasUnconditionalDerivedTargetTypeMapping( - IEnumerable derivedTypePairs, - IMemberMapperData declaredTypeMapperData, - out Type unconditionalDerivedTargetType, - out TypePairGroup[] groupedTypePairs) - { - groupedTypePairs = derivedTypePairs - .GroupBy(tp => tp.DerivedTargetType) - .Project(group => new TypePairGroup(group)) - .OrderBy(tp => tp.DerivedTargetType, TypeComparer.MostToLeastDerived) - .ToArray(); - - var unconditionalTypePairs = groupedTypePairs - .Filter(tpg => tpg.TypePairs.None(tp => tp.HasConfiguredCondition)); - - foreach (var unconditionalTypePair in unconditionalTypePairs) - { - var typePairsCondition = GetTargetValidCheckOrNull( - unconditionalTypePair.DerivedTargetType, - declaredTypeMapperData); - - if (typePairsCondition == null) - { - unconditionalDerivedTargetType = unconditionalTypePair.DerivedTargetType; - return true; - } - } - - unconditionalDerivedTargetType = null; - return false; - } - - private static void AddDerivedTargetTypeMappings( - IObjectMappingData declaredTypeMappingData, - IEnumerable derivedTargetTypes, - ICollection derivedTypeMappingExpressions) - { - var declaredTypeMapperData = declaredTypeMappingData.MapperData; - - derivedTargetTypes = derivedTargetTypes - .OrderBy(t => t, TypeComparer.MostToLeastDerived); - - foreach (var derivedTargetType in derivedTargetTypes) - { - var targetTypeCondition = GetTargetIsDerivedTypeCheck(derivedTargetType, declaredTypeMapperData); - - var ifDerivedTargetTypeThenMap = GetIfConditionThenMapExpression( - declaredTypeMappingData, - targetTypeCondition, - declaredTypeMapperData.SourceObject, - derivedTargetType); - - derivedTypeMappingExpressions.AddUnlessNullOrEmpty(ifDerivedTargetTypeThenMap); - } - } - - private static Expression GetIfConditionThenMapExpression( - IObjectMappingData mappingData, - Expression condition, - Expression sourceValue, - Type targetType) - { - var returnMappingResult = GetReturnMappingResultExpression(mappingData, sourceValue, targetType); - - if (returnMappingResult == EmptyExpression) - { - return EmptyExpression; - } - - var ifConditionThenMap = IfThen(condition, returnMappingResult); - - return ifConditionThenMap; - } - - private static Expression GetReturnMappingResultExpression( - IObjectMappingData mappingData, - Expression sourceValue, - Type targetType) - { - var mapping = DerivedMappingFactory.GetDerivedTypeMapping(mappingData, sourceValue, targetType); - - if (mapping == EmptyExpression) - { - return mapping; - } - - var returnMappingResult = Return(mappingData.MapperData.ReturnLabelTarget, mapping); - - return returnMappingResult; - } - - private static Expression GetMapFromConditionOrDefaultExpression( - IObjectMappingData mappingData, - Expression condition, - Expression typedVariable, - IEnumerable typePairGroups, - Type targetType) - { - var mappingExpressions = new List(); - - foreach (var typePairGroup in typePairGroups) - { - var typePairsCondition = - GetTypePairsCondition(typePairGroup.TypePairs, mappingData.MapperData) ?? - GetTargetValidCheckOrNull(typePairGroup.DerivedTargetType, mappingData.MapperData); - - var ifTypePairsConditionThenMap = GetIfConditionThenMapExpression( - mappingData, - typePairsCondition, - typedVariable, - typePairGroup.DerivedTargetType); - - mappingExpressions.Add(ifTypePairsConditionThenMap); - } - - var mapToDeclaredTargetType = - GetReturnMappingResultExpression(mappingData, typedVariable, targetType); - - mappingExpressions.Add(mapToDeclaredTargetType); - - var ifSourceVariableIsDerivedTypeThenMap = IfThen(condition, Block(mappingExpressions)); - - return ifSourceVariableIsDerivedTypeThenMap; - } - - private static ICollection GetTypePairsFor( - Type derivedSourceType, - Type targetType, - IMemberMapperData mapperData) - { - var pairTestMapperData = new BasicMapperData( - mapperData.RuleSet, - derivedSourceType, - targetType, - mapperData.TargetMember.WithType(targetType), - mapperData.Parent); - - return GetTypePairsFor(pairTestMapperData, mapperData); - } - - private static ICollection GetTypePairsFor(IBasicMapperData pairTestMapperData, IMemberMapperData mapperData) - { - var derivedTypePairs = mapperData.MapperContext.UserConfigurations - .DerivedTypes - .GetDerivedTypePairsFor(pairTestMapperData, mapperData.MapperContext); - - return derivedTypePairs; - } - - private static Expression GetTypePairsCondition( - IEnumerable derivedTypePairs, - IMemberMapperData mapperData) - { - var conditionalPairs = derivedTypePairs - .Filter(pair => pair.HasConfiguredCondition) - .ToArray(); - - var pairConditions = conditionalPairs.Chain( - firstPair => firstPair.GetConditionOrNull(mapperData), - (conditionSoFar, pair) => OrElse( - conditionSoFar, - pair.GetConditionOrNull(mapperData))); - - return pairConditions; - } - - private static Expression AppendTargetValidCheckIfAppropriate( - Expression condition, - Type targetType, - IMemberMapperData mapperData) - { - if (targetType == mapperData.TargetType) - { - return condition; - } - - var targetIsValid = GetTargetValidCheckOrNull(targetType, mapperData); - - if (targetIsValid == null) - { - return condition; - } - - condition = AndAlso(condition, targetIsValid); - - return condition; - } - - private static Expression GetTargetValidCheckOrNull(Type targetType, IMemberMapperData mapperData) - { - if (!mapperData.TargetMember.IsReadable || mapperData.TargetIsDefinitelyUnpopulated()) - { - return null; - } - - var targetIsOfDerivedType = GetTargetIsDerivedTypeCheck(targetType, mapperData); - - if (mapperData.TargetIsDefinitelyPopulated()) - { - return targetIsOfDerivedType; - } - - var targetIsNull = mapperData.TargetObject.GetIsDefaultComparison(); - var targetIsValid = OrElse(targetIsNull, targetIsOfDerivedType); - - return targetIsValid; - } - - private static Expression GetTargetIsDerivedTypeCheck(Type targetType, IMemberMapperData mapperData) - => TypeIs(mapperData.TargetObject, targetType); - } -} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index eb926317e..5a4df4d02 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -10,7 +10,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation #endif using System.Reflection; using ComplexTypes; - using DataSources; + using DataSources.Factories; using Enumerables.Dictionaries; using Extensions; using Extensions.Internal; diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 93b07e704..160c87163 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { - using Extensions; - using Extensions.Internal; - using Members; #if NET35 using Microsoft.Scripting.Ast; #else using System.Linq.Expressions; #endif + using Extensions; + using Extensions.Internal; + using Members; internal static class MappingFactory { diff --git a/AgileMapper/ObjectPopulation/ObjectMappingData.cs b/AgileMapper/ObjectPopulation/ObjectMappingData.cs index f21e16dfd..76fe8be37 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingData.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingData.cs @@ -2,17 +2,17 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System; using System.Collections.Generic; +#if NET35 + using Microsoft.Scripting.Ast; +#else + using System.Linq.Expressions; +#endif using Caching; using Extensions.Internal; using MapperKeys; using Members; using NetStandardPolyfills; using Validation; -#if NET35 - using Microsoft.Scripting.Ast; -#else - using System.Linq.Expressions; -#endif internal class ObjectMappingData : MappingInstanceData, From b6290141c0dbc00edffd6bdb303f93ee49cd4727 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 20 Aug 2019 12:13:29 +0100 Subject: [PATCH 17/17] Tidying always-derived mappings --- .../ComplexTypeMappingExpressionFactory.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index b11252c20..b3c26b330 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -101,8 +101,16 @@ protected override bool ShortCircuitMapping(MappingCreationContext context, out context.MappingExpressions.AddRange(shortCircuitReturns); } - context.MappingExpressions.Add(mapping); - context.MappingExpressions.Add(context.MapperData.GetReturnLabel(mapping.Type.ToDefaultExpression())); + if (mapping.NodeType == ExpressionType.Goto) + { + mapping = ((GotoExpression)mapping).Value; + context.MappingExpressions.Add(context.MapperData.GetReturnLabel(mapping)); + } + else + { + context.MappingExpressions.Add(mapping); + context.MappingExpressions.Add(context.MapperData.GetReturnLabel(mapping.Type.ToDefaultExpression())); + } mapping = Expression.Block(context.MappingExpressions); return true;