From f149a39410524595b39258904d03c50c52be4dcc Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 16 Oct 2017 22:16:42 +0100 Subject: [PATCH 1/8] Switching from MappingTypes.Fixed() to MappingTypes<>.Fixed --- AgileMapper/MappingExecutor.cs | 2 +- AgileMapper/Members/MappingTypes.cs | 32 +++++++++---------- .../ObjectPopulation/MappingDataFactory.cs | 4 +-- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index 460d81d6b..177c6f132 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -68,7 +68,7 @@ private ObjectMappingData CreateTypedRootMappingData( _source, target, null, // <- No enumerable index because we're at the root - new RootObjectMapperKey(MappingTypes.Fixed(), this), + new RootObjectMapperKey(MappingTypes.Fixed, this), this, parent: null); } diff --git a/AgileMapper/Members/MappingTypes.cs b/AgileMapper/Members/MappingTypes.cs index c3982fd71..22d226a50 100644 --- a/AgileMapper/Members/MappingTypes.cs +++ b/AgileMapper/Members/MappingTypes.cs @@ -6,7 +6,7 @@ namespace AgileObjects.AgileMapper.Members internal class MappingTypes { - private MappingTypes( + public MappingTypes( Type sourceType, Type targetType, bool runtimeTypesAreTheSame, @@ -20,13 +20,11 @@ private MappingTypes( #region Factory Method - public static MappingTypes Fixed() => MappingTypesCache.Instance; - public static MappingTypes For(TSource source, TTarget target) { - if (MappingTypesCache.SkipTypesCheck) + if (MappingTypes.SkipTypesCheck) { - return Fixed(); + return MappingTypes.Fixed; } var runtimeSourceTypeNeeded = TypeInfo.RuntimeTypeNeeded; @@ -78,7 +76,7 @@ public static MappingTypes For(TSource source, TTarget target) if (!runtimeTypesNeeded) { - return Fixed(); + return MappingTypes.Fixed; } var isEnumerable = TypeInfo.IsEnumerable || @@ -121,17 +119,17 @@ public MappingTypes WithTypes() RuntimeTypesAreTheSame, IsEnumerable); } + } - private static class MappingTypesCache - { - public static readonly bool SkipTypesCheck = - !(TypeInfo.RuntimeTypeNeeded || TypeInfo.RuntimeTypeNeeded); - - public static readonly MappingTypes Instance = new MappingTypes( - typeof(TSource), - typeof(TTarget), - true, // <- runtimeTypesAreTheSame - TypeInfo.IsEnumerable); - } + internal static class MappingTypes + { + public static readonly bool SkipTypesCheck = + !(TypeInfo.RuntimeTypeNeeded || TypeInfo.RuntimeTypeNeeded); + + public static readonly MappingTypes Fixed = new MappingTypes( + typeof(TSource), + typeof(TTarget), + true, // <- runtimeTypesAreTheSame + TypeInfo.IsEnumerable); } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/MappingDataFactory.cs b/AgileMapper/ObjectPopulation/MappingDataFactory.cs index a90474b89..633ce04fd 100644 --- a/AgileMapper/ObjectPopulation/MappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingDataFactory.cs @@ -25,7 +25,7 @@ public static ObjectMappingData ForChild( var mapperKey = new ChildObjectMapperKey( targetMemberRegistrationName, dataSourceIndex, - MappingTypes.Fixed()); + MappingTypes.Fixed); var mappingData = CreateMappingData(source, target, enumerableIndex, mapperKey, parent); @@ -53,7 +53,7 @@ public static ObjectMappingData ForElement()); + var mapperKey = new ElementObjectMapperKey(MappingTypes.Fixed); var mappingData = CreateMappingData(sourceElement, targetElement, enumerableIndex, mapperKey, parent); From f5c03b005a6b3f4d4cbe43975b17f04dc71667fa Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 20 Oct 2017 11:14:36 +0100 Subject: [PATCH 2/8] Support for accessing mapping plans for anonymous sources and user-defined struct mappings --- .../WhenViewingMappingPlans.cs | 63 +++++++++++++++++++ AgileMapper/Api/PlanTargetTypeSelector.cs | 6 +- AgileMapper/IMapper.cs | 14 +++++ AgileMapper/Mapper.cs | 16 +++++ 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 08b82bb6c..921691dc8 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -19,6 +19,69 @@ public void ShouldIncludeASimpleTypeMemberMapping() plan.ShouldContain("publicProperty_String.Value = pfsToPpsData.Source.Value;"); } + [Fact] + public void ShouldSupportAnonymousSourceTypesFromTheStaticApi() + { + var plan = Mapper + .GetPlanFor(new { Name = default(string), Discount = default(int) }) + .ToANew(); + + plan.ShouldContain("Map AnonymousType -> MysteryCustomer"); + plan.ShouldContain("mysteryCustomer.Name = fatsiToMcData.Source.Name;"); + plan.ShouldContain("mysteryCustomer.Discount = (decimal)fatsiToMcData.Source.Discount;"); + plan.ShouldContain("// No data source for Report"); + } + + [Fact] + public void ShouldSupportAnonymousSourceTypesFromTheInstanceApi() + { + using (var mapper = Mapper.CreateNew()) + { + var plan = mapper + .GetPlanFor(new { Name = default(string), AddressLine1 = default(string) }) + .OnTo(); + + plan.ShouldContain("Map AnonymousType -> Customer"); + plan.ShouldContain("customer.Name = sourceF__AnonymousType29_String_String.Name;"); + plan.ShouldContain("address.Line1 = sourceF__AnonymousType29_String_String.AddressLine1;"); + } + } + + [Fact] + public void ShouldSupportStructsFromTheStaticApi() + { + var plan = Mapper + .GetPlanFor>() + .Over>(); + + plan.ShouldContain("publicTwoFieldsStruct_String_String.Value1 = ptfsiiToPtfsssData.Source.Value1.ToString();"); + } + + [Fact] + public void ShouldSupportStructMergePlansFromTheStaticApi() + { + var plan = Mapper + .GetPlanFor>() + .OnTo>(); + + plan.ShouldContain("publicTwoFieldsStruct_String_String.Value1 = ptfsiiToPtfsssData.Source.Value1.ToString();"); + } + + [Fact] + public void ShouldSupportStructsFromTheInstanceApi() + { + using (var mapper = Mapper.CreateNew()) + { + var plan = mapper + .GetPlanFor>() + .ToANew>(); + + plan.ShouldContain("Map PublicPropertyStruct -> PublicCtorStruct"); + plan.ShouldContain("return new PublicCtorStruct"); + plan.ShouldContain("ppssToPcssData.Source.Value"); + } + } + [Fact] public void ShouldIncludeAComplexTypeMemberMapping() { diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index e8d48a16e..159addd3d 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -22,7 +22,7 @@ internal PlanTargetTypeSelector(MapperContext mapperContext) /// /// The type of object for which to create the mapping plans. /// A string mapping plan showing the functions to be executed during a mapping. - public string ToANew() where TResult : class + public string ToANew() => GetMappingPlan(_mapperContext.RuleSets.CreateNew); /// @@ -31,7 +31,7 @@ public string ToANew() where TResult : class /// /// The type of object for which to create the mapping plans. /// A string mapping plan showing the functions to be executed during a mapping. - public string OnTo() where TTarget : class + public string OnTo() => GetMappingPlan(_mapperContext.RuleSets.Merge); /// @@ -40,7 +40,7 @@ public string OnTo() where TTarget : class /// /// The type of object for which to create the mapping plans. /// A string mapping plan showing the functions to be executed during a mapping. - public string Over() where TTarget : class + public string Over() => GetMappingPlan(_mapperContext.RuleSets.Overwrite); private string GetMappingPlan(MappingRuleSet ruleSet) diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index 9f52404eb..5093f6f81 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -15,6 +15,20 @@ public interface IMapper : IDisposable /// A cloned copy of this mapper. IMapper CloneSelf(); + /// + /// Create and compile mapping functions for a particular type of mapping of the source type specified by + /// the given . Use this overload for anonymous types. + /// + /// The type of the given . + /// + /// An instance specifying the source type for which a mapping plan should be created. + /// + /// + /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should + /// be cached. + /// + PlanTargetTypeSelector GetPlanFor(TSource exampleInstance); + /// /// Create and compile mapping functions for a particular type of mapping of the source type /// specified by the type argument. diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index d258f4d18..617a37ed3 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -28,6 +28,8 @@ private Mapper(MapperContext mapperContext) #endregion + PlanTargetTypeSelector IMapper.GetPlanFor(TSource exampleInstance) => GetPlanFor(); + PlanTargetTypeSelector IMapper.GetPlanFor() => new PlanTargetTypeSelector(_mapperContext); @@ -37,6 +39,20 @@ PlanTargetTypeSelector IMapper.GetPlanFor() #region Static Access Methods + /// + /// Create and compile mapping functions for a particular type of mapping of the source type specified by + /// the given . Use this overload for anonymous types. + /// + /// The type of the given . + /// + /// An instance specifying the source type for which a mapping plan should be created. + /// + /// + /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should + /// be cached. + /// + public static PlanTargetTypeSelector GetPlanFor(TSource exampleInstance) => GetPlanFor(); + /// /// Create and compile mapping functions for a particular type of mapping of the source type /// specified by the type argument. From 287d17255dd140367959e1a041e4192906c4efa8 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 20 Oct 2017 11:57:23 +0100 Subject: [PATCH 3/8] Adding Mapper.GetPlansFor() to generate and cache mapping plans for a type pair for all rule sets in one call --- .../WhenViewingMappingPlans.cs | 12 +++++ .../Api/IPlanTargetTypeAndRuleSetSelector.cs | 33 +++++++++++++ AgileMapper/Api/IPlanTargetTypeSelector.cs | 17 +++++++ AgileMapper/Api/PlanTargetTypeSelector.cs | 48 +++++++------------ AgileMapper/IMapper.cs | 23 ++++++--- AgileMapper/Mapper.cs | 31 ++++++++---- AgileMapper/MappingRuleSetCollection.cs | 2 + AgileMapper/Plans/MappingPlanSet.cs | 23 +++++++++ 8 files changed, 145 insertions(+), 44 deletions(-) create mode 100644 AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs create mode 100644 AgileMapper/Api/IPlanTargetTypeSelector.cs create mode 100644 AgileMapper/Plans/MappingPlanSet.cs diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 921691dc8..75684b85b 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -19,6 +19,18 @@ public void ShouldIncludeASimpleTypeMemberMapping() plan.ShouldContain("publicProperty_String.Value = pfsToPpsData.Source.Value;"); } + [Fact] + public void ShouldGenerateAllRuleSets() + { + Mapper + .GetPlansFor>() + .To>(); + + //plan.ShouldContain("Rule Set: CreateNew"); + //plan.ShouldContain("Rule Set: Merge"); + //plan.ShouldContain("Rule Set: Overwrite"); + } + [Fact] public void ShouldSupportAnonymousSourceTypesFromTheStaticApi() { diff --git a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs new file mode 100644 index 000000000..9fcfaa19d --- /dev/null +++ b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs @@ -0,0 +1,33 @@ +namespace AgileObjects.AgileMapper.Api +{ + /// + /// Provides options to create and compile mapping functions for a particular type of mapping from the + /// source type being configured to a specified target type. + /// + public interface IPlanTargetTypeAndRuleSetSelector + { + /// + /// Create and compile mapping functions for a create new mapping from the source type being + /// configured to the type specified by the type argument. + /// + /// The type of object for which to create the mapping plan. + /// A string mapping plan showing the functions to be executed during a mapping. + string ToANew(); + + /// + /// Create and compile mapping functions for an OnTo (merge) mapping from the source type being + /// configured to the type specified by the type argument. + /// + /// The type of object for which to create the mapping plan. + /// A string mapping plan showing the functions to be executed during a mapping. + string OnTo(); + + /// + /// Create and compile mapping functions for an Over (overwrite) mapping from the source type being + /// configured to the type specified by the type argument. + /// + /// The type of object for which to create the mapping plan. + /// A string mapping plan showing the functions to be executed during a mapping. + string Over(); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/IPlanTargetTypeSelector.cs b/AgileMapper/Api/IPlanTargetTypeSelector.cs new file mode 100644 index 000000000..55f6611f9 --- /dev/null +++ b/AgileMapper/Api/IPlanTargetTypeSelector.cs @@ -0,0 +1,17 @@ +namespace AgileObjects.AgileMapper.Api +{ + /// + /// Provides the option to create and compile mapping functions for mappings from the source type + /// being configured to a specified target type, for all mapping types (create new, merge, overwrite). + /// + public interface IPlanTargetTypeSelector + { + /// + /// Create and compile mapping functions from the source type being configured to the type specified + /// by the type argument, for all mapping types (create new, merge, overwrite). + /// + /// The type of object for which to create the mapping plans. + /// A set of string mapping plans showing the functions to be executed during a mapping. + string To(); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index 159addd3d..f4e7dc99a 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -1,52 +1,40 @@ namespace AgileObjects.AgileMapper.Api { + using System.Linq; using Plans; - /// - /// Provides options to create and compile mapping functions for a particular type of mapping from the - /// source type being configured to a specified target type. - /// - /// The source type to which the configuration should apply. - public class PlanTargetTypeSelector + internal class PlanTargetTypeSelector : IPlanTargetTypeSelector, IPlanTargetTypeAndRuleSetSelector { private readonly MapperContext _mapperContext; internal PlanTargetTypeSelector(MapperContext mapperContext) { _mapperContext = mapperContext; + } + + public string To() + { + return new MappingPlanSet( + _mapperContext + .RuleSets + .All + .Select(GetMappingPlan) + .ToArray()); } - /// - /// Create and compile mapping functions for a create new mapping from the source type being - /// configured to the type specified by the type argument. - /// - /// The type of object for which to create the mapping plans. - /// A string mapping plan showing the functions to be executed during a mapping. public string ToANew() - => GetMappingPlan(_mapperContext.RuleSets.CreateNew); - - /// - /// Create and compile mapping functions for an OnTo (merge) mapping from the source type being - /// configured to the type specified by the type argument. - /// - /// The type of object for which to create the mapping plans. - /// A string mapping plan showing the functions to be executed during a mapping. + => GetMappingPlan(_mapperContext.RuleSets.CreateNew); + public string OnTo() - => GetMappingPlan(_mapperContext.RuleSets.Merge); - - /// - /// Create and compile mapping functions for an Over (overwrite) mapping from the source type being - /// configured to the type specified by the type argument. - /// - /// The type of object for which to create the mapping plans. - /// A string mapping plan showing the functions to be executed during a mapping. + => GetMappingPlan(_mapperContext.RuleSets.Merge); + public string Over() => GetMappingPlan(_mapperContext.RuleSets.Overwrite); - private string GetMappingPlan(MappingRuleSet ruleSet) + private MappingPlan GetMappingPlan(MappingRuleSet ruleSet) { var planContext = new MappingExecutor(ruleSet, _mapperContext); - + return new MappingPlan(planContext); } } diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index 5093f6f81..bb6218c49 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -15,6 +15,17 @@ public interface IMapper : IDisposable /// A cloned copy of this mapper. IMapper CloneSelf(); + /// + /// Create and compile mapping functions for the source type specified by the type argument, for all + /// mapping types (create new, merge, overwrite). + /// + /// The source type for which to create the mapping functions. + /// + /// An IPlanTargetTypeSelector with which to specify the target type the mapping functions for which + /// should be cached. + /// + IPlanTargetTypeSelector GetPlansFor(); + /// /// Create and compile mapping functions for a particular type of mapping of the source type specified by /// the given . Use this overload for anonymous types. @@ -24,10 +35,10 @@ public interface IMapper : IDisposable /// An instance specifying the source type for which a mapping plan should be created. /// /// - /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should - /// be cached. + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// should be cached. /// - PlanTargetTypeSelector GetPlanFor(TSource exampleInstance); + IPlanTargetTypeAndRuleSetSelector GetPlanFor(TSource exampleInstance); /// /// Create and compile mapping functions for a particular type of mapping of the source type @@ -35,10 +46,10 @@ public interface IMapper : IDisposable /// /// The source type for which to create the mapping functions. /// - /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should - /// be cached. + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// should be cached. /// - PlanTargetTypeSelector GetPlanFor(); + IPlanTargetTypeAndRuleSetSelector GetPlanFor(); /// /// Configure callbacks to be executed before a particular type of event occurs for all source diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index 617a37ed3..680796358 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -28,9 +28,13 @@ private Mapper(MapperContext mapperContext) #endregion - PlanTargetTypeSelector IMapper.GetPlanFor(TSource exampleInstance) => GetPlanFor(); + IPlanTargetTypeSelector IMapper.GetPlansFor() => GetPlan(); - PlanTargetTypeSelector IMapper.GetPlanFor() + IPlanTargetTypeAndRuleSetSelector IMapper.GetPlanFor(TSource exampleInstance) => GetPlan(); + + IPlanTargetTypeAndRuleSetSelector IMapper.GetPlanFor() => GetPlan(); + + private PlanTargetTypeSelector GetPlan() => new PlanTargetTypeSelector(_mapperContext); PreEventConfigStartingPoint IMapper.Before => new PreEventConfigStartingPoint(_mapperContext); @@ -39,6 +43,17 @@ PlanTargetTypeSelector IMapper.GetPlanFor() #region Static Access Methods + /// + /// Create and compile mapping functions for the source type specified by the type argument, for all + /// mapping types (create new, merge, overwrite). + /// + /// The source type for which to create the mapping functions. + /// + /// An IPlanTargetTypeSelector with which to specify the target type the mapping functions for which + /// should be cached. + /// + public static IPlanTargetTypeSelector GetPlansFor() => _default.GetPlansFor(); + /// /// Create and compile mapping functions for a particular type of mapping of the source type specified by /// the given . Use this overload for anonymous types. @@ -48,10 +63,10 @@ PlanTargetTypeSelector IMapper.GetPlanFor() /// An instance specifying the source type for which a mapping plan should be created. /// /// - /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should - /// be cached. + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// should be cached. /// - public static PlanTargetTypeSelector GetPlanFor(TSource exampleInstance) => GetPlanFor(); + public static IPlanTargetTypeAndRuleSetSelector GetPlanFor(TSource exampleInstance) => GetPlanFor(); /// /// Create and compile mapping functions for a particular type of mapping of the source type @@ -59,10 +74,10 @@ PlanTargetTypeSelector IMapper.GetPlanFor() /// /// The source type for which to create the mapping functions. /// - /// A PlanTargetTypeSelector with which to specify the type of mapping the functions for which should - /// be cached. + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// should be cached. /// - public static PlanTargetTypeSelector GetPlanFor() => _default.GetPlanFor(); + public static IPlanTargetTypeAndRuleSetSelector GetPlanFor() => _default.GetPlanFor(); /// /// Configure callbacks to be executed before a particular type of event occurs for all source diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index f9e53f80c..47d619d01 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -40,6 +40,8 @@ public MappingRuleSetCollection() _ruleSets = new List { CreateNew, Merge, Overwrite }; } + public IEnumerable All => _ruleSets; + public MappingRuleSet CreateNew => _createNew; public MappingRuleSet Merge => _merge; diff --git a/AgileMapper/Plans/MappingPlanSet.cs b/AgileMapper/Plans/MappingPlanSet.cs new file mode 100644 index 000000000..9292eec3f --- /dev/null +++ b/AgileMapper/Plans/MappingPlanSet.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.Plans +{ + using System; + using System.Collections.Generic; + using System.Linq; + + internal class MappingPlanSet + { + private readonly IEnumerable> _mappingPlans; + + public MappingPlanSet(IEnumerable> mappingPlans) + { + _mappingPlans = mappingPlans; + } + + public static implicit operator string(MappingPlanSet mappingPlans) + { + return string.Join( + Environment.NewLine + Environment.NewLine, + mappingPlans._mappingPlans.Select(plan => (string)plan)); + } + } +} \ No newline at end of file From 5e04bc8cf6717d1a20e6325d6494377b3d671773 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 20 Oct 2017 12:14:31 +0100 Subject: [PATCH 4/8] Re-instating test asserts --- AgileMapper.UnitTests/WhenViewingMappingPlans.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 75684b85b..811c8ef07 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -22,13 +22,13 @@ public void ShouldIncludeASimpleTypeMemberMapping() [Fact] public void ShouldGenerateAllRuleSets() { - Mapper + var plan = Mapper .GetPlansFor>() .To>(); - //plan.ShouldContain("Rule Set: CreateNew"); - //plan.ShouldContain("Rule Set: Merge"); - //plan.ShouldContain("Rule Set: Overwrite"); + plan.ShouldContain("Rule Set: CreateNew"); + plan.ShouldContain("Rule Set: Merge"); + plan.ShouldContain("Rule Set: Overwrite"); } [Fact] From cee732915dbdd62a239d03d3e54e081d5f643afc Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 20 Oct 2017 15:29:13 +0100 Subject: [PATCH 5/8] Test coverage for unmappable members appearing in test plans --- .../WhenViewingMappingPlans.cs | 72 +++++++++++++++++++ AgileMapper/Members/MemberExtensions.cs | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 811c8ef07..67437d259 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Text.RegularExpressions; using Shouldly; using TestClasses; @@ -412,5 +413,76 @@ public void ShouldIncludeMemberFilterExpressions() plan.ShouldContain("member.IsPropertyMatching(p => p.Name == \"Line2\")"); } } + + [Fact] + public void ShouldIncludeUnmappableStructComplexTypeMemberDetails() + { + using (var mapper = Mapper.CreateNew()) + { + var plan = mapper + .GetPlanFor>() + .ToANew>(); + + plan.ShouldContain("int.TryParse(ptfpsToPtfspiData.Source.Value2"); + plan.ShouldContain("Person member on a struct"); + } + } + + [Fact] + public void ShouldIncludeUnmappableReadOnlyArrayMemberDetails() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From>() + .To>() + .Map(ctx => ctx.Source.Value) + .ToCtor(); + + var plan = mapper + .GetPlanFor>() + .ToANew>(); + + plan.ShouldContain("readonly array"); + } + } + + [Fact] + public void ShouldIncludeUnmappableReadOnlyReadOnlyCollectionMemberDetails() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From>() + .To>>() + .Map(ctx => ctx.Source.Value) + .ToCtor("readOnlyValue"); + + var plan = mapper + .GetPlanFor>() + .ToANew>>(); + + plan.ShouldContain("readonly ReadOnlyCollection"); + } + } + + [Fact] + public void ShouldIncludeUnmappableReadOnlyIntMemberDetails() + { + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From>() + .To>() + .Map(ctx => ctx.Source.Value) + .ToCtor("readOnlyValue"); + + var plan = mapper + .GetPlanFor>() + .ToANew>(); + + plan.ShouldContain("readonly int"); + } + } } } diff --git a/AgileMapper/Members/MemberExtensions.cs b/AgileMapper/Members/MemberExtensions.cs index a85a27a62..b07f347cf 100644 --- a/AgileMapper/Members/MemberExtensions.cs +++ b/AgileMapper/Members/MemberExtensions.cs @@ -92,7 +92,7 @@ public static bool IsUnmappable(this QualifiedMember member, out string reason) member.Type.IsGenericType() && (member.Type.GetGenericTypeDefinition() == typeof(ReadOnlyCollection<>))) { - reason = "readonly ReadOnlyCollection"; + reason = "readonly " + member.Type.GetFriendlyName(); return true; } From 3689f8aaf5d1e55e3271f8cdc970281cbc15ce7f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 20 Oct 2017 15:43:41 +0100 Subject: [PATCH 6/8] Returning MappingPlan objects from GetPlanFor().To() methods to avoid the overhead of creating the plan string if all the caller wants to do is cache --- .../WhenConfiguringEnumMapping.cs | 2 +- .../WhenViewingDictionaryMappingPlans.cs | 8 +-- .../WhenMappingCircularReferences.cs | 4 +- .../WhenViewingMappingPlans.cs | 68 +++++++++---------- .../Api/IPlanTargetTypeAndRuleSetSelector.cs | 28 ++++++-- AgileMapper/Api/IPlanTargetTypeSelector.cs | 12 +++- AgileMapper/Api/PlanTargetTypeSelector.cs | 10 +-- AgileMapper/IMapper.cs | 6 +- AgileMapper/Mapper.cs | 12 ++-- AgileMapper/Plans/MappingPlan.cs | 27 +++++++- AgileMapper/Plans/MappingPlanSet.cs | 30 +++++++- 11 files changed, 138 insertions(+), 69 deletions(-) diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringEnumMapping.cs index 11e4f85da..cf95a09eb 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringEnumMapping.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringEnumMapping.cs @@ -36,7 +36,7 @@ public void ShouldRemovePairedEnumsFromEnumMismatchWarnings() mapper.WhenMapping .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check); - var plan = mapper + string plan = mapper .GetPlanFor>() .OnTo>(); diff --git a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs index a74d8717e..54d81176a 100644 --- a/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs +++ b/AgileMapper.UnitTests/Dictionaries/WhenViewingDictionaryMappingPlans.cs @@ -11,7 +11,7 @@ public class WhenViewingDictionaryMappingPlans [Fact] public void ShouldShowATargetObjectMappingPlan() { - var plan = Mapper + string plan = Mapper .GetPlanFor>() .ToANew(); @@ -25,7 +25,7 @@ public void ShouldShowATargetObjectMappingPlan() [Fact] public void ShouldShowATargetComplexTypeCollectionMappingPlan() { - var plan = Mapper + string plan = Mapper .GetPlanFor>() .ToANew>(); @@ -39,7 +39,7 @@ public void ShouldShowATargetComplexTypeCollectionMappingPlan() [Fact] public void ShouldShowASourceObjectMappingPlan() { - var plan = Mapper + string plan = Mapper .GetPlanFor() .ToANew>(); @@ -51,7 +51,7 @@ public void ShouldShowASourceObjectMappingPlan() [Fact] public void ShouldShowASourceComplexTypeEnumerableMappingPlan() { - var plan = Mapper + string plan = Mapper .GetPlanFor>() .ToANew>(); diff --git a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs index 2bf841df6..ac419949c 100644 --- a/AgileMapper.UnitTests/WhenMappingCircularReferences.cs +++ b/AgileMapper.UnitTests/WhenMappingCircularReferences.cs @@ -307,7 +307,7 @@ public void ShouldMapNestedMultiplyRecursiveRelationships() [Fact] public void ShouldGenerateAMappingPlanForLinkRelationships() { - var plan = Mapper.GetPlanFor