From 981427054b6b6347a5640f3804ba118408bebe60 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 09:06:56 +0000 Subject: [PATCH 001/176] Initial EF6 test project, test and public ProjectTo extension method --- .../AgileMapper.UnitTests.Ef6.csproj | 125 ++++++++++++++++++ AgileMapper.UnitTests.Ef6/App.config | 6 + AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs | 29 ++++ .../Properties/AssemblyInfo.cs | 7 + .../TestClasses/Product.cs | 12 ++ .../TestClasses/ProductDto.cs | 9 ++ AgileMapper.UnitTests.Ef6/TestContext.cs | 19 +++ .../TestContextCollection.cs | 10 ++ AgileMapper.UnitTests.Ef6/TestDbContext.cs | 15 +++ AgileMapper.UnitTests.Ef6/WhenProjecting.cs | 40 ++++++ AgileMapper.UnitTests.Ef6/packages.config | 15 +++ AgileMapper.sln | 8 +- AgileMapper/AgileMapper.csproj | 6 + AgileMapper/Constants.cs | 2 + AgileMapper/MappingRuleSetCollection.cs | 11 +- AgileMapper/ProjectionExtensions.cs | 24 ++++ 16 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj create mode 100644 AgileMapper.UnitTests.Ef6/App.config create mode 100644 AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs create mode 100644 AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/Product.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestContext.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestContextCollection.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestDbContext.cs create mode 100644 AgileMapper.UnitTests.Ef6/WhenProjecting.cs create mode 100644 AgileMapper.UnitTests.Ef6/packages.config create mode 100644 AgileMapper/ProjectionExtensions.cs diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj new file mode 100644 index 000000000..5179ed271 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -0,0 +1,125 @@ + + + + + + + Debug + AnyCPU + {63B8975D-0CDE-48F5-8CA9-8AF8FE729610} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Ef6 + AgileObjects.AgileMapper.UnitTests.Ef6 + v4.6.2 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\Effort.EF6.1.3.0\lib\net45\Effort.dll + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + + + ..\packages\NMemory.1.1.2\lib\net45\NMemory.dll + + + ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + + + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + CommonAssemblyInfo.cs + + + VersionInfo.cs + + + + + + + + + + + + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/App.config b/AgileMapper.UnitTests.Ef6/App.config new file mode 100644 index 000000000..e6079b964 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs new file mode 100644 index 000000000..4b1570798 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using System; + using Xunit; + + [Collection("EF6 collection")] + public abstract class Ef6TestClassBase + { + private readonly TestDbContext _context; + + protected Ef6TestClassBase(TestContext context) + { + _context = context.DbContext; + } + + protected void RunTest(Action testAction) + { + testAction.Invoke(_context); + + EmptyDbContext(); + } + + private void EmptyDbContext() + { + _context.Products.RemoveRange(_context.Products); + _context.SaveChanges(); + } + } +} diff --git a/AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..34dc22ba0 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Ef6")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Ef6")] + +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs b/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs new file mode 100644 index 000000000..07a424852 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using System.ComponentModel.DataAnnotations; + + public class Product + { + [Key] + public int ProductId { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs b/AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs new file mode 100644 index 000000000..e1b06611b --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + public class ProductDto + { + public int ProductId { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestContext.cs b/AgileMapper.UnitTests.Ef6/TestContext.cs new file mode 100644 index 000000000..03188243d --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestContext.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using System; + + public class TestContext : IDisposable + { + public TestContext() + { + DbContext = new TestDbContext(); + } + + public TestDbContext DbContext { get; } + + public void Dispose() + { + DbContext?.Dispose(); + } + } +} diff --git a/AgileMapper.UnitTests.Ef6/TestContextCollection.cs b/AgileMapper.UnitTests.Ef6/TestContextCollection.cs new file mode 100644 index 000000000..93f8fe4ca --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestContextCollection.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using Xunit; + + [CollectionDefinition(Name)] + public class TestContextCollection : ICollectionFixture + { + public const string Name = "EF6 collection"; + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs new file mode 100644 index 000000000..889f882e9 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using System.Data.Entity; + using Effort; + + public class TestDbContext : DbContext + { + public TestDbContext() + : base(DbConnectionFactory.CreateTransient(), true) + { + } + + public DbSet Products { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/WhenProjecting.cs b/AgileMapper.UnitTests.Ef6/WhenProjecting.cs new file mode 100644 index 000000000..43f65f4e8 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/WhenProjecting.cs @@ -0,0 +1,40 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6 +{ + using System.Linq; + using Shouldly; + using TestClasses; + using Xunit; + + public class WhenProjecting : Ef6TestClassBase + { + public WhenProjecting(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAFlatTypeToAnArray() + { + RunTest(context => + { + context.Products.Add(new Product + { + ProductId = 1, + Name = "Product One" + }); + + context.Products.Add(new Product + { + ProductId = 2, + Name = "Product Two" + }); + + context.SaveChanges(); + + var products = context.Products.ProjectTo().ToArray(); + + products.Length.ShouldBe(2); + }); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/packages.config b/AgileMapper.UnitTests.Ef6/packages.config new file mode 100644 index 000000000..584fc2e86 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.sln b/AgileMapper.sln index c66a2f664..dc051c1a6 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.16 +VisualStudioVersion = 15.0.27004.2006 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05AB6D17-6066-41D5-8E79-31C342DFC2DC}" ProjectSection(SolutionItems) = preProject @@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper", "AgileMapper\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.MoreTestClasses", "AgileMapper.UnitTests.MoreTestClasses\AgileMapper.UnitTests.MoreTestClasses.csproj", "{049E1EE5-48CE-441A-B166-3CF6BEC17957}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Ef6", "AgileMapper.UnitTests.Ef6\AgileMapper.UnitTests.Ef6.csproj", "{63B8975D-0CDE-48F5-8CA9-8AF8FE729610}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +50,10 @@ Global {049E1EE5-48CE-441A-B166-3CF6BEC17957}.Debug|Any CPU.Build.0 = Debug|Any CPU {049E1EE5-48CE-441A-B166-3CF6BEC17957}.Release|Any CPU.ActiveCfg = Release|Any CPU {049E1EE5-48CE-441A-B166-3CF6BEC17957}.Release|Any CPU.Build.0 = Release|Any CPU + {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 4325c6c2a..bf28f3cce 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -50,4 +50,10 @@ + + + 4.3.0 + + + diff --git a/AgileMapper/Constants.cs b/AgileMapper/Constants.cs index a19bdf9eb..028553433 100644 --- a/AgileMapper/Constants.cs +++ b/AgileMapper/Constants.cs @@ -26,6 +26,8 @@ internal static class Constants public const string Overwrite = "Overwrite"; + public const string Project = "Project"; + public const int BeforeLoopExitCheck = 0; public const int AfterLoopExitCheck = 1; diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 47d619d01..dc3fa664b 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -31,13 +31,20 @@ internal class MappingRuleSetCollection NullMemberPopulationGuardFactory.Instance, DefaultValueDataSourceFactory.Instance); + private static readonly MappingRuleSet _project = new MappingRuleSet( + Constants.Project, + false, + CopySourceEnumerablePopulationStrategy.Instance, + NullMemberPopulationGuardFactory.Instance, + ExistingOrDefaultValueDataSourceFactory.Instance); + #endregion private readonly List _ruleSets; public MappingRuleSetCollection() { - _ruleSets = new List { CreateNew, Merge, Overwrite }; + _ruleSets = new List { CreateNew, Merge, Overwrite, Project }; } public IEnumerable All => _ruleSets; @@ -48,6 +55,8 @@ public MappingRuleSetCollection() public MappingRuleSet Overwrite => _overwrite; + public MappingRuleSet Project => _project; + public MappingRuleSet GetByName(string name) => _ruleSets.First(rs => rs.Name == name); } } \ No newline at end of file diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs new file mode 100644 index 000000000..900b32275 --- /dev/null +++ b/AgileMapper/ProjectionExtensions.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper +{ + using System.Linq; + + /// + /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. + /// + public static class ProjectionExtensions + { + /// + /// + /// + /// + /// + /// + public static IQueryable ProjectTo(this IQueryable items) + where TResult : class + { + //items. + + return null; + } + } +} From 98a0639b081ea9e2dcf2d74ecbf4919057eca296 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 11:27:40 +0000 Subject: [PATCH 002/176] Building a Queryable.Select statement for queryable projection / Using singleton instances for stateless mapping expression factories --- .../TestClasses/Product.cs | 2 +- AgileMapper.UnitTests.Ef6/TestDbContext.cs | 1 + AgileMapper/IMapper.cs | 5 ++ AgileMapper/Mapper.cs | 45 ++++++--------- .../ComplexTypeMappingExpressionFactory.cs | 6 +- .../DictionaryMappingExpressionFactory.cs | 14 ++--- .../EnumerableMappingExpressionFactory.cs | 11 +--- .../EnumerablePopulationBuilder.cs | 11 ++++ .../MappingExpressionFactoryBase.cs | 11 +++- .../ObjectPopulation/ObjectMapperFactory.cs | 10 ++-- .../ObjectMappingDataFactory.cs | 32 ++++++++++- .../SimpleTypeMappingExpressionFactory.cs | 18 +----- AgileMapper/Parameters.cs | 3 + AgileMapper/Plans/MappingPlan.cs | 2 +- AgileMapper/ProjectionExtensions.cs | 54 ++++++++++++++++-- .../QueryProjectionExpressionFactory.cs | 56 +++++++++++++++++++ AgileMapper/Queryables/QueryProjectorKey.cs | 46 +++++++++++++++ 17 files changed, 248 insertions(+), 79 deletions(-) create mode 100644 AgileMapper/Queryables/QueryProjectionExpressionFactory.cs create mode 100644 AgileMapper/Queryables/QueryProjectorKey.cs diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs b/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs index 07a424852..ac8349419 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs +++ b/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs index 889f882e9..255c13833 100644 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -2,6 +2,7 @@ { using System.Data.Entity; using Effort; + using TestClasses; public class TestDbContext : DbContext { diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index fff19e0a6..200944bb9 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -5,6 +5,11 @@ using Api; using Api.Configuration; + internal interface IMapperInternal : IMapper + { + MapperContext Context { get; } + } + /// /// Provides mapping and mapping configuration services. /// diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index a072965ea..262066bc7 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -10,9 +10,9 @@ /// Provides a configurable mapping service. Create new instances with Mapper.CreateNew or use the default /// instance via the static Mapper access methods. /// - public sealed class Mapper : IMapper + public sealed class Mapper : IMapperInternal { - private static readonly IMapper _default = CreateNew(); + internal static readonly IMapperInternal Default = CreateNewInternal(); private Mapper(MapperContext context) { @@ -25,17 +25,14 @@ private Mapper(MapperContext context) /// Creates an instance implementing IMapper with which to perform mappings. /// /// A new instance implementing IMapper. - public static IMapper CreateNew() - { - var mapper = new Mapper(new MapperContext()); - - MapperCache.Add(mapper); + public static IMapper CreateNew() => CreateNewInternal(); - return mapper; - } + private static IMapperInternal CreateNewInternal() => new Mapper(new MapperContext()); #endregion + MapperContext IMapperInternal.Context => Context; + internal MapperContext Context { get; } IPlanTargetTypeAndRuleSetSelector IMapper.GetPlanFor(TSource exampleInstance) => GetPlan(); @@ -80,7 +77,7 @@ private PlanTargetTypeSelector GetPlan() /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which /// should be cached. /// - public static IPlanTargetTypeAndRuleSetSelector GetPlanFor() => _default.GetPlanFor(); + public static IPlanTargetTypeAndRuleSetSelector GetPlanFor() => Default.GetPlanFor(); /// /// Create and compile mapping functions for mapping from the source type specified by the given @@ -106,30 +103,30 @@ private PlanTargetTypeSelector GetPlan() /// An IPlanTargetTypeSelector with which to specify the target type the mapping functions for which /// should be cached. /// - public static IPlanTargetTypeSelector GetPlansFor() => _default.GetPlansFor(); + public static IPlanTargetTypeSelector GetPlansFor() => Default.GetPlansFor(); /// /// Returns mapping plans for all mapping functions currently cached by the default . /// /// A string containing the currently-cached functions to be executed during mappings. - public static string GetPlansInCache() => _default.GetPlansInCache(); + public static string GetPlansInCache() => Default.GetPlansInCache(); /// /// Configure callbacks to be executed before a particular type of event occurs for all source /// and target types. /// - public static PreEventConfigStartingPoint Before => _default.Before; + public static PreEventConfigStartingPoint Before => Default.Before; /// /// Configure callbacks to be executed after a particular type of event occurs for all source /// and target types. /// - public static PostEventConfigStartingPoint After => _default.After; + public static PostEventConfigStartingPoint After => Default.After; /// /// Configure how the default mapper performs a mapping. /// - public static MappingConfigStartingPoint WhenMapping => _default.WhenMapping; + public static MappingConfigStartingPoint WhenMapping => Default.WhenMapping; /// /// Performs a deep clone of the given object and returns the result. @@ -137,7 +134,7 @@ private PlanTargetTypeSelector GetPlan() /// The type of object for which to perform a deep clone. /// The object to deep clone. /// A deep clone of the given object. - public static TSource Clone(TSource source) => _default.Clone(source); + public static TSource Clone(TSource source) => Default.Clone(source); /// /// Performs a deep clone of the given object and returns the result. @@ -153,7 +150,7 @@ public static TSource Clone( TSource source, params Expression>>[] configurations) { - return _default.Clone(source, configurations); + return Default.Clone(source, configurations); } /// @@ -167,7 +164,7 @@ public static TSource Clone( /// properties. /// public static dynamic Flatten(TSource source) where TSource : class - => _default.Flatten(source); + => Default.Flatten(source); /// /// Perform a mapping operation on the given object. @@ -175,9 +172,9 @@ public static dynamic Flatten(TSource source) where TSource : class /// The type of source object on which to perform the mapping. /// The source object on which to perform the mapping. /// A TargetTypeSelector with which to specify the type of mapping to perform. - public static ITargetTypeSelector Map(TSource source) => _default.Map(source); + public static ITargetTypeSelector Map(TSource source) => Default.Map(source); - internal static void ResetDefaultInstance() => _default.Dispose(); + internal static void ResetDefaultInstance() => Default.Dispose(); #endregion @@ -208,12 +205,4 @@ ITargetTypeSelector IMapper.Map(TSource source) #endregion } - - internal static class MapperCache - { - public static void Add(IMapper mapper) - { - - } - } } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index a2fd83658..5e07828c1 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -39,14 +39,12 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out { // If a target complex type is readonly or unconstructable // we still try to map to it using an existing non-null value: - nullMappingBlock = null; - return false; + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); } if (_constructionFactory.GetNewObjectCreation(mappingData) != null) { - nullMappingBlock = null; - return false; + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); } var targetType = mappingData.MapperData.TargetType; diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index 73eb2aa2b..930fdcbd0 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -15,6 +15,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal class DictionaryMappingExpressionFactory : MappingExpressionFactoryBase { + public static readonly MappingExpressionFactoryBase Instance = new DictionaryMappingExpressionFactory(); + private readonly MemberPopulationFactory _memberPopulationFactory; public DictionaryMappingExpressionFactory() @@ -128,16 +130,14 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out { if (mappingData.MapperKey.MappingTypes.SourceType.IsDictionary()) { - nullMappingBlock = null; - return false; + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); } var targetMember = (DictionaryTargetMember)mappingData.MapperData.TargetMember; if ((targetMember.KeyType == typeof(string)) || (targetMember.KeyType == typeof(object))) { - nullMappingBlock = null; - return false; + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); } nullMappingBlock = Expression.Block( @@ -147,12 +147,6 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out return true; } - protected override IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) - => Enumerable.Empty; - - protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingData) - => Constants.EmptyExpression; - protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs index 37575e902..27dbb977a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableMappingExpressionFactory.cs @@ -7,6 +7,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables internal class EnumerableMappingExpressionFactory : MappingExpressionFactoryBase { + public static readonly MappingExpressionFactoryBase Instance = new EnumerableMappingExpressionFactory(); + public override bool IsFor(IObjectMappingData mappingData) => mappingData.MapperKey.MappingTypes.IsEnumerable; @@ -14,8 +16,7 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out { if (mappingData.MapperData.SourceMember.IsEnumerable) { - nullMappingBlock = null; - return false; + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); } nullMappingBlock = Expression.Block( @@ -25,12 +26,6 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out return true; } - protected override IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) - => Enumerable.Empty; - - protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingData) - => Constants.EmptyExpression; - protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { yield return mappingData.MappingContext.RuleSet.EnumerablePopulationStrategy.GetPopulation(mappingData); diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index d1ed5f30e..f9a73905a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -513,6 +513,17 @@ public Expression GetSourceItemsProjection( return GetSourceItemsProjection( sourceEnumerableValue, _selectWithoutIndexMethod, + (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter)); + } + + public Expression GetSourceItemsProjection( + Expression sourceEnumerableValue, + MethodInfo selectMethod, + Func projectionFuncFactory) + { + return GetSourceItemsProjection( + sourceEnumerableValue, + selectMethod, (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter), _sourceElementParameter); } diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 6bb2fd3cb..15ee3fe72 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -63,9 +63,14 @@ public Expression Create(IObjectMappingData mappingData) return mappingBlock; } - protected abstract bool TargetCannotBeMapped(IObjectMappingData mappingData, out Expression nullMappingBlock); + protected virtual bool TargetCannotBeMapped(IObjectMappingData mappingData, out Expression nullMappingBlock) + { + nullMappingBlock = null; + return false; + } - protected abstract IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData); + protected virtual IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) + => Enumerable.Empty; private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, out Expression derivedTypeMappings) { @@ -81,7 +86,7 @@ private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, return true; } - protected abstract Expression GetDerivedTypeMappings(IObjectMappingData mappingData); + protected virtual Expression GetDerivedTypeMappings(IObjectMappingData mappingData) => Constants.EmptyExpression; private static MappingExtras GetMappingExtras(ObjectMapperData mapperData) { diff --git a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs index dbbacf9b5..0b7c217ad 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperFactory.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using ComplexTypes; using Enumerables; using Extensions; + using Queryables; internal class ObjectMapperFactory { @@ -14,11 +15,12 @@ internal class ObjectMapperFactory public ObjectMapperFactory(MapperContext mapperContext) { - _mappingExpressionFactories = new MappingExpressionFactoryBase[] + _mappingExpressionFactories = new[] { - new DictionaryMappingExpressionFactory(), - new SimpleTypeMappingExpressionFactory(), - new EnumerableMappingExpressionFactory(), + QueryProjectionExpressionFactory.Instance, + DictionaryMappingExpressionFactory.Instance, + SimpleTypeMappingExpressionFactory.Instance, + EnumerableMappingExpressionFactory.Instance, new ComplexTypeMappingExpressionFactory(mapperContext) }; diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 9f5124792..7a487e84a 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -16,16 +16,46 @@ internal class ObjectMappingDataFactory : IObjectMappingDataFactoryBridge { private static readonly IObjectMappingDataFactoryBridge _bridge = new ObjectMappingDataFactory(); + public static ObjectMappingData ForRootFixedTypes( + IMappingContext mappingContext) + { + return ForRootFixedTypes(default(TSource), default(TTarget), mappingContext); + } + + public static ObjectMappingData, IQueryable> ForProjection( + ObjectMapperKeyBase projectorKey, + IMapperInternal mapper) + { + return ForRootFixedTypes( + default(IQueryable), + default(IQueryable), + projectorKey, + new SimpleMappingContext(mapper.Context.RuleSets.Project, mapper.Context)); + } + public static ObjectMappingData ForRootFixedTypes( TSource source, TTarget target, IMappingContext mappingContext) + { + return ForRootFixedTypes( + source, + target, + new RootObjectMapperKey(MappingTypes.Fixed, mappingContext), + mappingContext); + } + + private static ObjectMappingData ForRootFixedTypes( + TSource source, + TTarget target, + ObjectMapperKeyBase mapperKey, + IMappingContext mappingContext) { return new ObjectMappingData( source, target, null, // <- No enumerable index because we're at the root - new RootObjectMapperKey(MappingTypes.Fixed, mappingContext), + mapperKey, mappingContext, parent: null); } diff --git a/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs index 0d6489f24..860412c8b 100644 --- a/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs @@ -7,22 +7,10 @@ namespace AgileObjects.AgileMapper.ObjectPopulation internal class SimpleTypeMappingExpressionFactory : MappingExpressionFactoryBase { - public override bool IsFor(IObjectMappingData mappingData) - { - return mappingData.MapperKey.MappingTypes.TargetType.IsSimple(); - } - - protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out Expression nullMappingBlock) - { - nullMappingBlock = null; - return false; - } + public static readonly MappingExpressionFactoryBase Instance = new SimpleTypeMappingExpressionFactory(); - protected override IEnumerable GetShortCircuitReturns(GotoExpression returnNull, IObjectMappingData mappingData) - => Enumerable.Empty; - - protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingData) - => Constants.EmptyExpression; + public override bool IsFor(IObjectMappingData mappingData) + => mappingData.MapperKey.MappingTypes.TargetType.IsSimple(); protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { diff --git a/AgileMapper/Parameters.cs b/AgileMapper/Parameters.cs index 7ec2a598f..9f1d3b1c0 100644 --- a/AgileMapper/Parameters.cs +++ b/AgileMapper/Parameters.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper { using System; + using System.Linq; using System.Linq.Expressions; using Extensions; using Members; @@ -11,6 +12,8 @@ internal static class Parameters public static readonly ParameterExpression MappingContext = Create(); public static readonly ParameterExpression MappingData = Create(); public static readonly ParameterExpression ObjectMappingData = Create(); + public static readonly ParameterExpression Queryable = Create(); + public static readonly ParameterExpression MapperInternal = Create(); public static ParameterExpression Create(string name = null) => Create(typeof(T), name); diff --git a/AgileMapper/Plans/MappingPlan.cs b/AgileMapper/Plans/MappingPlan.cs index 95daac9f9..438fcf78d 100644 --- a/AgileMapper/Plans/MappingPlan.cs +++ b/AgileMapper/Plans/MappingPlan.cs @@ -31,7 +31,7 @@ internal MappingPlan(IObjectMapper cachedMapper) internal static MappingPlan For(IMappingContext mappingContext) { var mappingData = ObjectMappingDataFactory - .ForRootFixedTypes(default(TSource), default(TTarget), mappingContext); + .ForRootFixedTypes(mappingContext); return new MappingPlan(mappingData.Mapper); } diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 900b32275..cedaa8ec9 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -1,6 +1,13 @@ namespace AgileObjects.AgileMapper { + using System; using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Members; + using NetStandardPolyfills; + using ObjectPopulation; + using Queryables; /// /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. @@ -10,12 +17,51 @@ public static class ProjectionExtensions /// /// /// - /// - /// + /// + /// /// - public static IQueryable ProjectTo(this IQueryable items) - where TResult : class + public static IQueryable ProjectTo(this IQueryable sourceQueryable) + where TResultElement : class { + var projectCaller = GlobalContext.Instance.Cache.GetOrAdd( + new SourceAndTargetTypesKey(sourceQueryable.ElementType, typeof(TResultElement)), + key => + { + // ReSharper disable once PossibleNullReferenceException + var projectQueryMethod = typeof(ProjectionExtensions) + .GetNonPublicStaticMethod("ProjectQuery") + .MakeGenericMethod(key.SourceType, key.TargetType); + + var projectQueryCall = Expression.Call( + projectQueryMethod, + Parameters.Queryable, + Parameters.MapperInternal); + + var projectQueryLambda = Expression.Lambda>>( + projectQueryCall, + Parameters.Queryable, + Parameters.MapperInternal); + + return projectQueryLambda.Compile(); + }); + + return projectCaller.Invoke(sourceQueryable, Mapper.Default); + } + + internal static IQueryable ProjectQuery( + IQueryable sourceQueryable, + IMapperInternal mapper) + { + var projectorKey = new QueryProjectorKey( + MappingTypes.Fixed, + sourceQueryable, + mapper.Context); + + var rootMappingData = ObjectMappingDataFactory + .ForProjection(projectorKey, mapper); + + //mapper.Context.ObjectMapperFactory.GetOrCreateRoot() + //items. return null; diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs new file mode 100644 index 000000000..d4c159e1c --- /dev/null +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -0,0 +1,56 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Extensions; + using NetStandardPolyfills; + using ObjectPopulation; + + internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase + { + public static readonly MappingExpressionFactoryBase Instance = new QueryProjectionExpressionFactory(); + + #region Cached Items + + private static readonly MethodInfo _queryableSelectMethod = typeof(Queryable) + .GetPublicStaticMethods() + .First(m => + (m.Name == "Select") && + (m.GetParameters().Last().ParameterType.GetGenericArguments().First().GetGenericArguments().Length == 2)); + + #endregion + + public override bool IsFor(IObjectMappingData mappingData) + { + var mapperData = mappingData.MapperData; + + return mapperData.TargetMember.IsEnumerable && + mapperData.SourceType.IsGenericType() && + mapperData.SourceType.GetGenericTypeDefinition() == typeof(IQueryable<>); + } + + protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) + { + var mapperData = mappingData.MapperData; + + var elementMapping = mapperData + .EnumerablePopulationBuilder + .GetSourceItemsProjection( + mapperData.SourceObject, + _queryableSelectMethod, + sourceParameter => MappingFactory.GetElementMapping( + sourceParameter, + mapperData.TargetMember.ElementType.ToDefaultExpression(), + mappingData)); + + yield return null; + } + + protected override Expression GetReturnValue(ObjectMapperData mapperData) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs new file mode 100644 index 000000000..6ebc1bc1a --- /dev/null +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -0,0 +1,46 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq; + using Members; + using Members.Sources; + using ObjectPopulation; + + internal class QueryProjectorKey : ObjectMapperKeyBase + { + private readonly MapperContext _mapperContext; + + public QueryProjectorKey( + MappingTypes mappingTypes, + IQueryable sourceQueryable, + MapperContext mapperContext) + : base(mappingTypes) + { + _mapperContext = mapperContext; + SourceQueryable = sourceQueryable; + } + + public IQueryable SourceQueryable { get; } + + public override IMembersSource GetMembersSource(IObjectMappingData parentMappingData) + => _mapperContext.RootMembersSource; + + protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTypes) + => new QueryProjectorKey(newMappingTypes, SourceQueryable, _mapperContext); + + public override bool Equals(object obj) + { + var otherKey = (QueryProjectorKey)obj; + + // ReSharper disable once PossibleNullReferenceException + return TypesMatch(otherKey) && + (otherKey.SourceQueryable.Provider == SourceQueryable.Provider); + } + + #region ExcludeFromCodeCoverage +#if DEBUG + [ExcludeFromCodeCoverage] +#endif + #endregion + public override int GetHashCode() => 0; + } +} \ No newline at end of file From 70f03faaa1a4d945ee173ccbe84124bbd411b1f1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 11:36:29 +0000 Subject: [PATCH 003/176] Making mapping try - catch use ruleset-dependent --- AgileMapper/Configuration/MappingConfigInfo.cs | 2 +- AgileMapper/MappingRuleSet.cs | 13 ++++++++++--- AgileMapper/MappingRuleSetCollection.cs | 8 ++++---- AgileMapper/Members/MemberMapperDataExtensions.cs | 4 ++-- AgileMapper/ObjectPopulation/MapperDataContext.cs | 3 ++- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/AgileMapper/Configuration/MappingConfigInfo.cs b/AgileMapper/Configuration/MappingConfigInfo.cs index e2006c74d..2ccd07ec3 100644 --- a/AgileMapper/Configuration/MappingConfigInfo.cs +++ b/AgileMapper/Configuration/MappingConfigInfo.cs @@ -15,7 +15,7 @@ internal class MappingConfigInfo { private static readonly Type _allSourceTypes = typeof(MappingConfigInfo); - private static readonly MappingRuleSet _allRuleSets = new MappingRuleSet("*", true, null, null, null); + private static readonly MappingRuleSet _allRuleSets = new MappingRuleSet("*", null, null, null, null); private ConfiguredLambdaInfo _conditionLambda; private bool _negateCondition; diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index 5ee1b4216..a2cb264f2 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -4,17 +4,24 @@ namespace AgileObjects.AgileMapper using Members.Population; using ObjectPopulation.Enumerables; + internal class MappingRuleSetSettings + { + public bool RootHasPopulatedTarget { get; set; } + + public bool UseTryCatch { get; set; } + } + internal class MappingRuleSet { public MappingRuleSet( string name, - bool rootHasPopulatedTarget, + MappingRuleSetSettings settings, IEnumerablePopulationStrategy enumerablePopulationStrategy, IMemberPopulationGuardFactory populationGuardFactory, IDataSourceFactory fallbackDataSourceFactory) { Name = name; - RootHasPopulatedTarget = rootHasPopulatedTarget; + Settings = settings; EnumerablePopulationStrategy = enumerablePopulationStrategy; PopulationGuardFactory = populationGuardFactory; FallbackDataSourceFactory = fallbackDataSourceFactory; @@ -22,7 +29,7 @@ public MappingRuleSet( public string Name { get; } - public bool RootHasPopulatedTarget { get; } + public MappingRuleSetSettings Settings { get; } public IEnumerablePopulationStrategy EnumerablePopulationStrategy { get; } diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index dc3fa664b..5aa99781b 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -12,28 +12,28 @@ internal class MappingRuleSetCollection private static readonly MappingRuleSet _createNew = new MappingRuleSet( Constants.CreateNew, - false, + new MappingRuleSetSettings { UseTryCatch = true }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _merge = new MappingRuleSet( Constants.Merge, - true, + new MappingRuleSetSettings { RootHasPopulatedTarget = true, UseTryCatch = true }, MergeEnumerablePopulationStrategy.Instance, PreserveExistingValueMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _overwrite = new MappingRuleSet( Constants.Overwrite, - true, + new MappingRuleSetSettings { RootHasPopulatedTarget = true, UseTryCatch = true }, OverwriteEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, DefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _project = new MappingRuleSet( Constants.Project, - false, + new MappingRuleSetSettings(), CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 4f289bb76..613515811 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -30,12 +30,12 @@ public static bool TargetCouldBePopulated(this IMemberMapperData mapperData) public static bool TargetIsDefinitelyPopulated(this IBasicMapperData mapperData) { - return mapperData.RuleSet.RootHasPopulatedTarget && + return mapperData.RuleSet.Settings.RootHasPopulatedTarget && (mapperData.IsRoot || mapperData.TargetMemberIsUserStruct()); } public static bool TargetIsDefinitelyUnpopulated(this IMemberMapperData mapperData) - => mapperData.Context.IsForNewElement || (mapperData.IsRoot && !mapperData.RuleSet.RootHasPopulatedTarget); + => mapperData.Context.IsForNewElement || (mapperData.IsRoot && !mapperData.RuleSet.Settings.RootHasPopulatedTarget); public static bool HasSameSourceAsParent(this IMemberMapperData mapperData) { diff --git a/AgileMapper/ObjectPopulation/MapperDataContext.cs b/AgileMapper/ObjectPopulation/MapperDataContext.cs index 146101e04..8fb5807fd 100644 --- a/AgileMapper/ObjectPopulation/MapperDataContext.cs +++ b/AgileMapper/ObjectPopulation/MapperDataContext.cs @@ -85,7 +85,8 @@ private void BubbleMappingNeededToParent() public bool UseLocalVariable { get; } - public bool UseMappingTryCatch => _mapperData.IsRoot || !IsPartOfUserStructMapping; + public bool UseMappingTryCatch + => _mapperData.RuleSet.Settings.UseTryCatch && (_mapperData.IsRoot || !IsPartOfUserStructMapping); public bool IsPartOfUserStructMapping { From 44bb5ee0ab8012f96173f33f0a82f79d15d38e22 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 11:41:27 +0000 Subject: [PATCH 004/176] Making source element null-checking ruleset-dependent --- AgileMapper/MappingRuleSet.cs | 2 ++ AgileMapper/MappingRuleSetCollection.cs | 20 ++++++++++++++++--- .../ComplexTypeMappingExpressionFactory.cs | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index a2cb264f2..a15762c60 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -8,6 +8,8 @@ internal class MappingRuleSetSettings { public bool RootHasPopulatedTarget { get; set; } + public bool SourceElementsCouldBeNull { get; set; } + public bool UseTryCatch { get; set; } } diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 5aa99781b..d55de0ffe 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -12,21 +12,35 @@ internal class MappingRuleSetCollection private static readonly MappingRuleSet _createNew = new MappingRuleSet( Constants.CreateNew, - new MappingRuleSetSettings { UseTryCatch = true }, + new MappingRuleSetSettings + { + SourceElementsCouldBeNull = true, + UseTryCatch = true + }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _merge = new MappingRuleSet( Constants.Merge, - new MappingRuleSetSettings { RootHasPopulatedTarget = true, UseTryCatch = true }, + new MappingRuleSetSettings + { + RootHasPopulatedTarget = true, + SourceElementsCouldBeNull = true, + UseTryCatch = true + }, MergeEnumerablePopulationStrategy.Instance, PreserveExistingValueMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _overwrite = new MappingRuleSet( Constants.Overwrite, - new MappingRuleSetSettings { RootHasPopulatedTarget = true, UseTryCatch = true }, + new MappingRuleSetSettings + { + RootHasPopulatedTarget = true, + SourceElementsCouldBeNull = true, + UseTryCatch = true + }, OverwriteEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, DefaultValueDataSourceFactory.Instance); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 5e07828c1..8195d0791 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -91,7 +91,7 @@ private static bool SourceObjectCouldBeNull(IMemberMapperData mapperData) return false; } - if (mapperData.TargetMemberIsEnumerableElement()) + if (mapperData.RuleSet.Settings.SourceElementsCouldBeNull && mapperData.TargetMemberIsEnumerableElement()) { return !mapperData.HasSameSourceAsParent(); } From 2c45b29f0e05177ae86e5b22f20a81cd5f8c5747 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 11:51:36 +0000 Subject: [PATCH 005/176] Making use of member initialisation object creations ruleset-dependent --- AgileMapper/MappingRuleSet.cs | 2 ++ AgileMapper/MappingRuleSetCollection.cs | 5 ++++- .../ComplexTypeMappingExpressionFactory.cs | 17 ++++++++++------- ...=> MemberInitPopulationExpressionFactory.cs} | 4 ++-- ...ultiStatementPopulationExpressionFactory.cs} | 4 ++-- .../QueryProjectionExpressionFactory.cs | 4 ++-- 6 files changed, 22 insertions(+), 14 deletions(-) rename AgileMapper/ObjectPopulation/ComplexTypes/{StructPopulationExpressionFactory.cs => MemberInitPopulationExpressionFactory.cs} (94%) rename AgileMapper/ObjectPopulation/ComplexTypes/{ClassPopulationExpressionFactory.cs => MultiStatementPopulationExpressionFactory.cs} (86%) diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index a15762c60..b8a8a8bb7 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -10,6 +10,8 @@ internal class MappingRuleSetSettings public bool SourceElementsCouldBeNull { get; set; } + public bool UseMemberInitialisation { get; set; } + public bool UseTryCatch { get; set; } } diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index d55de0ffe..3d130f2b8 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -47,7 +47,10 @@ internal class MappingRuleSetCollection private static readonly MappingRuleSet _project = new MappingRuleSet( Constants.Project, - new MappingRuleSetSettings(), + new MappingRuleSetSettings + { + UseMemberInitialisation = true + }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 8195d0791..bf8be60c5 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -15,15 +15,15 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes internal class ComplexTypeMappingExpressionFactory : MappingExpressionFactoryBase { private readonly ComplexTypeConstructionFactory _constructionFactory; - private readonly PopulationExpressionFactoryBase _structPopulationFactory; - private readonly PopulationExpressionFactoryBase _classPopulationFactory; + private readonly PopulationExpressionFactoryBase _memberInitPopulationFactory; + private readonly PopulationExpressionFactoryBase _multiStatementPopulationFactory; private readonly IEnumerable _shortCircuitFactories; public ComplexTypeMappingExpressionFactory(MapperContext mapperContext) { _constructionFactory = new ComplexTypeConstructionFactory(mapperContext); - _structPopulationFactory = new StructPopulationExpressionFactory(_constructionFactory); - _classPopulationFactory = new ClassPopulationExpressionFactory(_constructionFactory); + _memberInitPopulationFactory = new MemberInitPopulationExpressionFactory(_constructionFactory); + _multiStatementPopulationFactory = new MultiStatementPopulationExpressionFactory(_constructionFactory); _shortCircuitFactories = new[] { @@ -136,13 +136,16 @@ protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingD protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { - var expressionFactory = mappingData.MapperData.TargetMemberIsUserStruct() - ? _structPopulationFactory - : _classPopulationFactory; + var expressionFactory = UseMemberInitialisation(mappingData.MapperData) + ? _memberInitPopulationFactory + : _multiStatementPopulationFactory; return expressionFactory.GetPopulation(mappingData); } + private static bool UseMemberInitialisation(IBasicMapperData mapperData) + => mapperData.RuleSet.Settings.UseMemberInitialsation || mapperData.TargetMemberIsUserStruct(); + protected override Expression GetReturnValue(ObjectMapperData mapperData) => mapperData.TargetInstance; public override void Reset() => _constructionFactory.Reset(); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/StructPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs similarity index 94% rename from AgileMapper/ObjectPopulation/ComplexTypes/StructPopulationExpressionFactory.cs rename to AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs index 8ab09bbdf..1f116d1e6 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/StructPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs @@ -5,9 +5,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using Extensions; using Members.Population; - internal class StructPopulationExpressionFactory : PopulationExpressionFactoryBase + internal class MemberInitPopulationExpressionFactory : PopulationExpressionFactoryBase { - public StructPopulationExpressionFactory(ComplexTypeConstructionFactory constructionFactory) + public MemberInitPopulationExpressionFactory(ComplexTypeConstructionFactory constructionFactory) : base(constructionFactory) { } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ClassPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs similarity index 86% rename from AgileMapper/ObjectPopulation/ComplexTypes/ClassPopulationExpressionFactory.cs rename to AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs index 25b1128c8..43ba4399e 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ClassPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs @@ -6,9 +6,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using Members.Population; using static CallbackPosition; - internal class ClassPopulationExpressionFactory : PopulationExpressionFactoryBase + internal class MultiStatementPopulationExpressionFactory : PopulationExpressionFactoryBase { - public ClassPopulationExpressionFactory(ComplexTypeConstructionFactory constructionFactory) + public MultiStatementPopulationExpressionFactory(ComplexTypeConstructionFactory constructionFactory) : base(constructionFactory) { } diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index d4c159e1c..22fa4db53 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -35,7 +35,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat { var mapperData = mappingData.MapperData; - var elementMapping = mapperData + var queryProjection = mapperData .EnumerablePopulationBuilder .GetSourceItemsProjection( mapperData.SourceObject, @@ -45,7 +45,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); - yield return null; + yield return queryProjection; } protected override Expression GetReturnValue(ObjectMapperData mapperData) From 8c32b2a7abaaf368ace1ab111c2db1047a2d5cf5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 15:11:37 +0000 Subject: [PATCH 006/176] Support for straightforward projection! --- AgileMapper/Api/PlanTargetTypeSelector.cs | 2 ++ AgileMapper/MappingRuleSet.cs | 11 ----------- AgileMapper/MappingRuleSetCollection.cs | 3 ++- AgileMapper/MappingRuleSetSettings.cs | 15 +++++++++++++++ .../Members/MemberMapperDataExtensions.cs | 3 +++ .../ComplexTypeMappingExpressionFactory.cs | 4 +--- .../DictionaryMappingExpressionFactory.cs | 3 --- .../EnumerablePopulationBuilder.cs | 8 ++++---- .../ObjectPopulation/MapperDataContext.cs | 5 +++++ .../MappingExpressionFactoryBase.cs | 7 ++++++- .../ObjectPopulation/MappingFactory.cs | 4 +++- .../ObjectMappingDataFactory.cs | 7 ++++--- .../SimpleTypeMappingExpressionFactory.cs | 2 -- AgileMapper/ProjectionExtensions.cs | 19 +++++++++++-------- .../QueryProjectionExpressionFactory.cs | 5 ----- 15 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 AgileMapper/MappingRuleSetSettings.cs diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index b9748aace..6c01ba997 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -24,10 +24,12 @@ internal PlanTargetTypeSelector(MapperContext mapperContext) public MappingPlanSet To( Expression>>[] configurations) { + // TODO: Include projection mapping plans: return new MappingPlanSet( _mapperContext .RuleSets .All + .Except(new[] { _mapperContext.RuleSets.Project }) .Select(rs => GetMappingPlan(rs, configurations)) .ToArray()); } diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index b8a8a8bb7..df2b2cef8 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -4,17 +4,6 @@ namespace AgileObjects.AgileMapper using Members.Population; using ObjectPopulation.Enumerables; - internal class MappingRuleSetSettings - { - public bool RootHasPopulatedTarget { get; set; } - - public bool SourceElementsCouldBeNull { get; set; } - - public bool UseMemberInitialisation { get; set; } - - public bool UseTryCatch { get; set; } - } - internal class MappingRuleSet { public MappingRuleSet( diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 3d130f2b8..9a9e3408f 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -49,7 +49,8 @@ internal class MappingRuleSetCollection Constants.Project, new MappingRuleSetSettings { - UseMemberInitialisation = true + UseMemberInitialisation = true, + UseSingleRootMappingExpression = true }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs new file mode 100644 index 000000000..db14b1969 --- /dev/null +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper +{ + internal class MappingRuleSetSettings + { + public bool RootHasPopulatedTarget { get; set; } + + public bool SourceElementsCouldBeNull { get; set; } + + public bool UseSingleRootMappingExpression { get; set; } + + public bool UseMemberInitialisation { get; set; } + + public bool UseTryCatch { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 613515811..02d86b4f9 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -15,6 +15,9 @@ internal static class MemberMapperDataExtensions public static bool IsStandalone(this IObjectMappingData mappingData) => mappingData.IsRoot || mappingData.MapperKey.MappingTypes.RuntimeTypesNeeded; + public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) + => mapperData.IsRoot && mapperData.RuleSet.Settings.UseSingleRootMappingExpression; + public static IMemberMapperData GetRootMapperData(this IMemberMapperData mapperData) { while (!mapperData.IsRoot) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index bf8be60c5..aea50885e 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -144,9 +144,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat } private static bool UseMemberInitialisation(IBasicMapperData mapperData) - => mapperData.RuleSet.Settings.UseMemberInitialsation || mapperData.TargetMemberIsUserStruct(); - - protected override Expression GetReturnValue(ObjectMapperData mapperData) => mapperData.TargetInstance; + => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.TargetMemberIsUserStruct(); public override void Reset() => _constructionFactory.Reset(); } diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index 930fdcbd0..f988ea740 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -324,8 +324,5 @@ private static Expression GetEnumerableToDictionaryMapping(IObjectMappingData ma return builder; } - - protected override Expression GetReturnValue(ObjectMapperData mapperData) - => mapperData.TargetInstance; } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index f9a73905a..e78213551 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -513,7 +513,7 @@ public Expression GetSourceItemsProjection( return GetSourceItemsProjection( sourceEnumerableValue, _selectWithoutIndexMethod, - (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter)); + projectionFuncFactory.Invoke); } public Expression GetSourceItemsProjection( @@ -521,7 +521,7 @@ public Expression GetSourceItemsProjection( MethodInfo selectMethod, Func projectionFuncFactory) { - return GetSourceItemsProjection( + return CreateSourceItemsProjection( sourceEnumerableValue, selectMethod, (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter), @@ -532,7 +532,7 @@ public Expression GetSourceItemsProjection( Expression sourceEnumerableValue, Func projectionFuncFactory) { - return GetSourceItemsProjection( + return CreateSourceItemsProjection( sourceEnumerableValue, _selectWithIndexMethod, projectionFuncFactory, @@ -540,7 +540,7 @@ public Expression GetSourceItemsProjection( Counter); } - private Expression GetSourceItemsProjection( + private Expression CreateSourceItemsProjection( Expression sourceEnumerableValue, MethodInfo linqSelectOverload, Func projectionFuncFactory, diff --git a/AgileMapper/ObjectPopulation/MapperDataContext.cs b/AgileMapper/ObjectPopulation/MapperDataContext.cs index 8fb5807fd..63a721221 100644 --- a/AgileMapper/ObjectPopulation/MapperDataContext.cs +++ b/AgileMapper/ObjectPopulation/MapperDataContext.cs @@ -46,6 +46,11 @@ private static bool ShouldUseLocalVariable(IBasicMapperData mapperData) return false; } + if (mapperData.UseSingleMappingExpression()) + { + return false; + } + if (mapperData.TargetMember.IsComplex && (mapperData.TargetMember.IsReadOnly || mapperData.TargetIsDefinitelyPopulated()) && !mapperData.TargetMemberIsUserStruct()) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 15ee3fe72..5023f34ab 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -154,6 +154,11 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping CreateFullMappingBlock: + if (mapperData.UseSingleMappingExpression()) + { + return mappingExpressions.First(); + } + returnExpression = GetReturnExpression(GetReturnValue(mapperData), mappingExtras); mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); @@ -190,7 +195,7 @@ private static Expression GetReturnExpression(Expression returnValue, MappingExt : returnValue; } - protected abstract Expression GetReturnValue(ObjectMapperData mapperData); + protected virtual Expression GetReturnValue(ObjectMapperData mapperData) => mapperData.TargetInstance; private static Expression WrapInTryCatch(Expression mappingBlock, IMemberMapperData mapperData) { diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index de4839b32..9f1694de8 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -262,7 +262,9 @@ public static Expression UseLocalSourceValueVariableIfAppropriate( Expression mappingExpression, ObjectMapperData mapperData) { - if (mapperData.Context.IsForDerivedType || !mapperData.Context.IsStandalone) + if (mapperData.Context.IsForDerivedType || + !mapperData.Context.IsStandalone || + mapperData.UseSingleMappingExpression()) { return mappingExpression; } diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 7a487e84a..8071bdbe4 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -22,13 +22,14 @@ public static ObjectMappingData ForRootFixedTypes, IQueryable> ForProjection( + public static ObjectMappingData ForProjection( ObjectMapperKeyBase projectorKey, + TSourceQueryable sourceQueryable, IMapperInternal mapper) { return ForRootFixedTypes( - default(IQueryable), - default(IQueryable), + sourceQueryable, + default(TResultQueryable), projectorKey, new SimpleMappingContext(mapper.Context.RuleSets.Project, mapper.Context)); } diff --git a/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs index 860412c8b..704ad8983 100644 --- a/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/SimpleTypeMappingExpressionFactory.cs @@ -24,7 +24,5 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat yield return mapperData.TargetObject; } - - protected override Expression GetReturnValue(ObjectMapperData mapperData) => mapperData.TargetInstance; } } \ No newline at end of file diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index cedaa8ec9..79845c606 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -3,7 +3,7 @@ using System; using System.Linq; using System.Linq.Expressions; - using System.Reflection; + using Extensions; using Members; using NetStandardPolyfills; using ObjectPopulation; @@ -32,9 +32,11 @@ public static IQueryable ProjectTo(this IQueryab .GetNonPublicStaticMethod("ProjectQuery") .MakeGenericMethod(key.SourceType, key.TargetType); + var typedSourceQueryable = typeof(IQueryable<>).MakeGenericType(key.SourceType); + var projectQueryCall = Expression.Call( projectQueryMethod, - Parameters.Queryable, + Parameters.Queryable.GetConversionTo(typedSourceQueryable), Parameters.MapperInternal); var projectQueryLambda = Expression.Lambda>>( @@ -49,7 +51,7 @@ public static IQueryable ProjectTo(this IQueryab } internal static IQueryable ProjectQuery( - IQueryable sourceQueryable, + IQueryable sourceQueryable, IMapperInternal mapper) { var projectorKey = new QueryProjectorKey( @@ -58,13 +60,14 @@ internal static IQueryable ProjectQuery(projectorKey, mapper); - - //mapper.Context.ObjectMapperFactory.GetOrCreateRoot() + .ForProjection, IQueryable>( + projectorKey, + sourceQueryable, + mapper); - //items. + var queryProjection = rootMappingData.MapStart(); - return null; + return queryProjection; } } } diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 22fa4db53..a30dd260f 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -47,10 +47,5 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat yield return queryProjection; } - - protected override Expression GetReturnValue(ObjectMapperData mapperData) - { - throw new System.NotImplementedException(); - } } } From 70fd8ace72e160f9f6822f306da560816926ed4a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 15:19:05 +0000 Subject: [PATCH 007/176] Using a mapper-scoped projection MappingContext --- AgileMapper/MapperContext.cs | 3 +++ .../ObjectPopulation/MappingExpressionFactoryBase.cs | 10 +++++----- .../ObjectPopulation/ObjectMappingDataFactory.cs | 4 ++-- AgileMapper/ProjectionExtensions.cs | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/AgileMapper/MapperContext.cs b/AgileMapper/MapperContext.cs index 3a4ae20a0..482504d9b 100644 --- a/AgileMapper/MapperContext.cs +++ b/AgileMapper/MapperContext.cs @@ -28,6 +28,7 @@ public MapperContext(NamingSettings namingSettings = null) UserConfigurations = new UserConfigurationSet(this); ValueConverters = new ConverterSet(); RuleSets = new MappingRuleSetCollection(); + QueryProjectionMappingContext = new SimpleMappingContext(RuleSets.Project, this); } public CacheSet Cache { get; } @@ -52,6 +53,8 @@ public MapperContext(NamingSettings namingSettings = null) public MappingRuleSetCollection RuleSets { get; } + public IMappingContext QueryProjectionMappingContext { get; } + public MapperContext Clone() { var context = new MapperContext(); diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 5023f34ab..3986139a8 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -122,6 +122,11 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping AdjustForSingleExpressionBlockIfApplicable(ref mappingExpressions); + if (mapperData.UseSingleMappingExpression()) + { + return mappingExpressions.First(); + } + if (mappingExpressions[0].NodeType != ExpressionType.Block) { if (mappingExpressions[0].NodeType == ExpressionType.MemberAccess) @@ -154,11 +159,6 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping CreateFullMappingBlock: - if (mapperData.UseSingleMappingExpression()) - { - return mappingExpressions.First(); - } - returnExpression = GetReturnExpression(GetReturnValue(mapperData), mappingExtras); mappingExpressions.Add(mapperData.GetReturnLabel(returnExpression)); diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 8071bdbe4..9ffc1f2c0 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -25,13 +25,13 @@ public static ObjectMappingData ForRootFixedTypes ForProjection( ObjectMapperKeyBase projectorKey, TSourceQueryable sourceQueryable, - IMapperInternal mapper) + IMappingContext mappingContext) { return ForRootFixedTypes( sourceQueryable, default(TResultQueryable), projectorKey, - new SimpleMappingContext(mapper.Context.RuleSets.Project, mapper.Context)); + mappingContext); } public static ObjectMappingData ForRootFixedTypes( diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 79845c606..75e1201fa 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -63,7 +63,7 @@ internal static IQueryable ProjectQuery, IQueryable>( projectorKey, sourceQueryable, - mapper); + mapper.Context.QueryProjectionMappingContext); var queryProjection = rootMappingData.MapStart(); From 4f305c98909836fb5530e09f4216b2e5bfd57fce Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 16:24:10 +0000 Subject: [PATCH 008/176] Extending basic projection test --- .../AgileMapper.UnitTests.Ef6.csproj | 2 +- ...enProjecting.cs => WhenProjectingFlatTypes.cs} | 15 +++++++++++---- AgileMapper/Properties/AssemblyInfo.cs | 3 +-- 3 files changed, 13 insertions(+), 7 deletions(-) rename AgileMapper.UnitTests.Ef6/{WhenProjecting.cs => WhenProjectingFlatTypes.cs} (54%) diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index 5179ed271..054109d12 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -94,7 +94,7 @@ - + diff --git a/AgileMapper.UnitTests.Ef6/WhenProjecting.cs b/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs similarity index 54% rename from AgileMapper.UnitTests.Ef6/WhenProjecting.cs rename to AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs index 43f65f4e8..72b0b49b4 100644 --- a/AgileMapper.UnitTests.Ef6/WhenProjecting.cs +++ b/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs @@ -5,9 +5,9 @@ using TestClasses; using Xunit; - public class WhenProjecting : Ef6TestClassBase + public class WhenProjectingFlatTypes : Ef6TestClassBase { - public WhenProjecting(TestContext context) + public WhenProjectingFlatTypes(TestContext context) : base(context) { } @@ -31,9 +31,16 @@ public void ShouldProjectAFlatTypeToAnArray() context.SaveChanges(); - var products = context.Products.ProjectTo().ToArray(); + var products = context.Products.ToArray(); + var productDtos = context.Products.ProjectTo().ToArray(); - products.Length.ShouldBe(2); + productDtos.Length.ShouldBe(2); + + productDtos[0].ProductId.ShouldBe(products[0].ProductId); + productDtos[0].Name.ShouldBe(products[0].Name); + + productDtos[1].ProductId.ShouldBe(products[1].ProductId); + productDtos[1].Name.ShouldBe(products[1].Name); }); } } diff --git a/AgileMapper/Properties/AssemblyInfo.cs b/AgileMapper/Properties/AssemblyInfo.cs index 49b79d42d..17e4b6765 100644 --- a/AgileMapper/Properties/AssemblyInfo.cs +++ b/AgileMapper/Properties/AssemblyInfo.cs @@ -4,10 +4,9 @@ using System.Security; [assembly: AssemblyTitle("AgileObjects.AgileMapper")] -[assembly: AssemblyDescription("AgileMapper is a zero-configuration, highly-configurable object-object mapper with viewable execution plans via a static or instance API. It conforms to .NET Standard 1.0.")] +[assembly: AssemblyDescription("AgileMapper is zero-configuration, highly-configurable object-object mapper with viewable execution plans. Performs deep clones, updates and merges via a static or instance API. Conforms to .NET Standard 1.0.")] [assembly: AllowPartiallyTrustedCallers] [assembly: CLSCompliant(true)] [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.NonParallel, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] -[assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.Polyfills, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] \ No newline at end of file From 3f53b489017cabc34d569bc918e8b62dc9569c30 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 3 Nov 2017 16:51:23 +0000 Subject: [PATCH 009/176] Start of simple type conversion test coverage --- .../AgileMapper.UnitTests.Ef6.csproj | 4 ++ AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs | 2 + .../WhenConvertingToBools.cs | 43 +++++++++++++++++++ .../TestClasses/PublicBoolProperty.cs | 12 ++++++ .../TestClasses/PublicBoolPropertyDto.cs | 9 ++++ .../TestClasses/PublicIntProperty.cs | 13 ++++++ AgileMapper.UnitTests.Ef6/TestDbContext.cs | 4 ++ 7 files changed, 87 insertions(+) create mode 100644 AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index 054109d12..249c6203e 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -88,8 +88,12 @@ VersionInfo.cs + + + + diff --git a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs index 4b1570798..1268b7e84 100644 --- a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs +++ b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs @@ -23,6 +23,8 @@ protected void RunTest(Action testAction) private void EmptyDbContext() { _context.Products.RemoveRange(_context.Products); + _context.IntItems.RemoveRange(_context.IntItems); + _context.BoolItems.RemoveRange(_context.BoolItems); _context.SaveChanges(); } } diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs new file mode 100644 index 000000000..68f44cb3c --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion +{ + using System.Linq; + using Shouldly; + using TestClasses; + using Xunit; + + public class WhenConvertingToBools : Ef6TestClassBase + { + public WhenConvertingToBools(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldMapAnIntOneToTrue() + { + RunTest(context => + { + context.IntItems.Add(new PublicIntProperty { Value = 1 }); + context.SaveChanges(); + + var boolItem = context.IntItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldMapAnIntZeroToFalse() + { + RunTest(context => + { + context.IntItems.Add(new PublicIntProperty { Value = 0 }); + context.SaveChanges(); + + var boolItem = context.IntItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs new file mode 100644 index 000000000..430d8384b --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicBoolProperty + { + [Key] + public int Id { get; set; } + + public bool Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs new file mode 100644 index 000000000..dbd4e9aba --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + public class PublicBoolPropertyDto + { + public int Id { get; set; } + + public bool Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs new file mode 100644 index 000000000..43188771e --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicIntProperty + { + [Key] + public int Id { get; set; } + + + public int Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs index 255c13833..be5a74e3e 100644 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -12,5 +12,9 @@ public TestDbContext() } public DbSet Products { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet BoolItems { get; set; } } } \ No newline at end of file From 77ac5cf20ab20c0d49f4e50ccfc2c661abce43fa Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 4 Nov 2017 13:52:24 +0000 Subject: [PATCH 010/176] Using ExpressionEvaluation for comparisons of fallback datasource values --- .../Extensions/WhenEquatingExpressions.cs | 73 ++-- .../Configuration/ConfiguredLambdaInfo.cs | 2 +- .../Configuration/Inline/InlineMapperKey.cs | 2 +- AgileMapper/DataSources/DataSourceSet.cs | 2 +- AgileMapper/Extensions/ExpressionEquator.cs | 294 ---------------- .../Extensions/ExpressionEvaluation.cs | 324 ++++++++++++++++++ .../ExpressionReplacementDictionary.cs | 2 +- 7 files changed, 374 insertions(+), 325 deletions(-) delete mode 100644 AgileMapper/Extensions/ExpressionEquator.cs create mode 100644 AgileMapper/Extensions/ExpressionEvaluation.cs diff --git a/AgileMapper.UnitTests/Extensions/WhenEquatingExpressions.cs b/AgileMapper.UnitTests/Extensions/WhenEquatingExpressions.cs index 14fda35d9..e5ce33c5d 100644 --- a/AgileMapper.UnitTests/Extensions/WhenEquatingExpressions.cs +++ b/AgileMapper.UnitTests/Extensions/WhenEquatingExpressions.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.IO; using System.Linq.Expressions; using AgileMapper.Extensions; using Shouldly; @@ -16,7 +17,7 @@ public void ShouldEquateCheckedAdditions() Expression> bindingsOne = (x, y) => checked(x + y); Expression> bindingsTwo = (x, y) => checked(x + y); - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -25,7 +26,7 @@ public void ShouldEquateCheckedSubtractions() Expression> bindingsOne = (x, y) => checked(x - y); Expression> bindingsTwo = (x, y) => checked(x - y); - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -34,7 +35,7 @@ public void ShouldEquateCheckedMultiplications() Expression> bindingsOne = (x, y) => checked(x * y); Expression> bindingsTwo = (x, y) => checked(x * y); - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] public void ShouldEquateAModuloOperation() @@ -42,7 +43,7 @@ public void ShouldEquateAModuloOperation() Expression> bindingsOne = (x, y) => x % y == 0; Expression> bindingsTwo = (x, y) => x % y == 0; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -51,7 +52,7 @@ public void ShouldEquateNegatedDefaultComparisons() Expression> bindingsOne = x => !(x > default(int)); Expression> bindingsTwo = x => !(x > default(int)); - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -60,7 +61,7 @@ public void ShouldEquateTypeIsComparisons() Expression> bindingsOne = x => x is Person; Expression> bindingsTwo = x => x is Person; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -69,7 +70,7 @@ public void ShouldEquateTypeAsComparisons() Expression> bindingsOne = x => x as Person; Expression> bindingsTwo = x => x as Person; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -78,7 +79,7 @@ public void ShouldEquateAndComparisons() Expression> bindingsOne = x => x > 0 && x < 100; Expression> bindingsTwo = x => x > 0 && x < 100; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -87,7 +88,7 @@ public void ShouldEquateBitwiseAndComparisons() Expression> bindingsOne = x => x > 0 & x < 100; Expression> bindingsTwo = x => x > 0 & x < 100; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -96,7 +97,7 @@ public void ShouldEquateOrComparisons() Expression> bindingsOne = x => x > 0 || x < 100; Expression> bindingsTwo = x => x > 0 || x < 100; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -105,7 +106,7 @@ public void ShouldEquateBitwiseOrComparisons() Expression> bindingsOne = x => x > 0 | x < 100; Expression> bindingsTwo = x => x > 0 | x < 100; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -114,7 +115,25 @@ public void ShouldEquateExclusiveOrComparisons() Expression> bindingsOne = x => x > 0 ^ x < 100; Expression> bindingsTwo = x => x > 0 ^ x < 100; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); + } + + [Fact] + public void ShouldEquateMemberAccesses() + { + Expression> accessOne = m => m.Expression.NodeType; + Expression> accessTwo = m => m.Expression.NodeType; + + ExpressionEvaluation.AreEqual(accessOne, accessTwo).ShouldBeTrue(); + } + + [Fact] + public void ShouldEquivalateMemberAccesses() + { + Expression> accessOne = m => m.Expression.NodeType; + Expression> accessTwo = e => e.NodeType; + + ExpressionEvaluation.AreEquivalent(accessOne, accessTwo).ShouldBeTrue(); } [Fact] @@ -123,7 +142,7 @@ public void ShouldEquateListInitialisations() Expression>> bindingsOne = () => new List { 1, 2, 3 }; Expression>> bindingsTwo = () => new List { 1, 2, 3 }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -132,7 +151,7 @@ public void ShouldEquateBitwiseLeftShiftOperations() Expression> bindingsOne = (x, y) => x << y; Expression> bindingsTwo = (x, y) => x << y; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -141,7 +160,7 @@ public void ShouldEquateBitwiseRightShiftOperations() Expression> bindingsOne = (x, y) => x >> y; Expression> bindingsTwo = (x, y) => x >> y; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -150,7 +169,7 @@ public void ShouldEquateCoalesceOperations() Expression> bindingsOne = (x, y) => x ?? y; Expression> bindingsTwo = (x, y) => x ?? y; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -159,7 +178,7 @@ public void ShouldEquateNegationOperations() Expression> bindingsOne = x => -x; Expression> bindingsTwo = x => -x; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -168,7 +187,7 @@ public void ShouldEquateCheckedNegationOperations() Expression> bindingsOne = x => checked(-x); Expression> bindingsTwo = x => checked(-x); - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -177,7 +196,7 @@ public void ShouldEquateANewBoundedArrayCreation() Expression> bindingsOne = x => new int[x]; Expression> bindingsTwo = x => new int[x]; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -186,7 +205,7 @@ public void ShouldEquateArrayIndexAccesses() Expression> bindingsOne = x => x[0]; Expression> bindingsTwo = x => x[0]; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -195,7 +214,7 @@ public void ShouldEquateArrayLengthAccesses() Expression> bindingsOne = x => x.Length == 1; Expression> bindingsTwo = x => x.Length == 1; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -204,7 +223,7 @@ public void ShouldEquateListBindings() Expression>>> bindingsOne = () => new PublicField> { Value = { 1 } }; Expression>>> bindingsTwo = () => new PublicField> { Value = { 1 } }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeTrue(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeTrue(); } [Fact] @@ -213,7 +232,7 @@ public void ShouldDifferentiateListInitialisationsByInitialisationCount() Expression>> bindingsOne = () => new List { 1, 2, 3 }; Expression>> bindingsTwo = () => new List { 1, 2 }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeFalse(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeFalse(); } [Fact] @@ -222,7 +241,7 @@ public void ShouldDifferentiateExpressionsByListBindingInitialiserCount() Expression>>> bindingsOne = () => new PublicField> { Value = { 1 } }; Expression>>> bindingsTwo = () => new PublicField> { Value = { 1, 2 } }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeFalse(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeFalse(); } [Fact] @@ -231,7 +250,7 @@ public void ShouldDifferentiateExpressionsByBindingCount() Expression> oneBinding = () => new Address { Line1 = "One!" }; Expression> twoBindings = () => new Address { Line1 = "One!", Line2 = "Two!" }; - ExpressionEquator.Instance.Equals(oneBinding, twoBindings).ShouldBeFalse(); + ExpressionEvaluation.AreEqual(oneBinding, twoBindings).ShouldBeFalse(); } [Fact] @@ -249,7 +268,7 @@ public void ShouldDifferentiateExpressionsByBindingType() Value1 = "One!" }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeFalse(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeFalse(); } [Fact] @@ -258,7 +277,7 @@ public void ShouldDifferentiateExpressionsBySubBindings() Expression> bindingsOne = () => new Person { Address = { Line1 = "One!" } }; Expression> bindingsTwo = () => new Person { Address = { Line1 = "One!", Line2 = "Two!" } }; - ExpressionEquator.Instance.Equals(bindingsOne, bindingsTwo).ShouldBeFalse(); + ExpressionEvaluation.AreEqual(bindingsOne, bindingsTwo).ShouldBeFalse(); } } } diff --git a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs index c2ea7b725..349aba327 100644 --- a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs +++ b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs @@ -123,7 +123,7 @@ public bool IsSameAs(ConfiguredLambdaInfo otherLambdaInfo) return false; } - return ExpressionEquator.Instance.Equals(_lambda.Body, otherLambdaInfo._lambda.Body); + return ExpressionEvaluation.AreEquivalent(_lambda.Body, otherLambdaInfo._lambda.Body); } public Expression GetBody( diff --git a/AgileMapper/Configuration/Inline/InlineMapperKey.cs b/AgileMapper/Configuration/Inline/InlineMapperKey.cs index 2a95ac372..cd2ffc462 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperKey.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperKey.cs @@ -49,7 +49,7 @@ public override bool Equals(object obj) var configuration = Configurations[i].Body; var otherConfiguration = otherKey.Configurations[i].Body; - if (!ExpressionEquator.Instance.Equals(configuration, otherConfiguration)) + if (!ExpressionEvaluation.AreEquivalent(configuration, otherConfiguration)) { return false; } diff --git a/AgileMapper/DataSources/DataSourceSet.cs b/AgileMapper/DataSources/DataSourceSet.cs index 65827053d..c622702d2 100644 --- a/AgileMapper/DataSources/DataSourceSet.cs +++ b/AgileMapper/DataSources/DataSourceSet.cs @@ -104,7 +104,7 @@ private Expression GetFallbackValueOrNull(IMemberMapperData mapperData) var targetMemberAccess = mapperData.GetTargetMemberAccess(); - if (fallbackValue.ToString() == targetMemberAccess.ToString()) + if (ExpressionEvaluation.AreEqual(fallbackValue, targetMemberAccess)) { return null; } diff --git a/AgileMapper/Extensions/ExpressionEquator.cs b/AgileMapper/Extensions/ExpressionEquator.cs deleted file mode 100644 index abe5157c0..000000000 --- a/AgileMapper/Extensions/ExpressionEquator.cs +++ /dev/null @@ -1,294 +0,0 @@ -namespace AgileObjects.AgileMapper.Extensions -{ - using System; - using System.Collections.Generic; - using System.Linq.Expressions; -#if NET_STANDARD - using System.Reflection; -#endif - - internal class ExpressionEquator : IEqualityComparer - { - public static IEqualityComparer Instance = new ExpressionEquator(); - - public bool Equals(Expression x, Expression y) - { - while (true) - { - if (x.NodeType != y.NodeType) - { - return false; - } - - switch (x.NodeType) - { - case ExpressionType.Call: - return AreEqual((MethodCallExpression)x, (MethodCallExpression)y); - - case ExpressionType.Conditional: - return AreEqual((ConditionalExpression)x, (ConditionalExpression)y); - - case ExpressionType.Constant: - return AreEqual((ConstantExpression)x, (ConstantExpression)y); - - case ExpressionType.ArrayLength: - case ExpressionType.Convert: - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.Not: - case ExpressionType.TypeAs: - return AreEqual((UnaryExpression)x, (UnaryExpression)y); - - case ExpressionType.Index: - return AreEqual((IndexExpression)x, (IndexExpression)y); - - case ExpressionType.Lambda: - x = ((LambdaExpression)x).Body; - y = ((LambdaExpression)y).Body; - continue; - - case ExpressionType.ListInit: - return AreEqual((ListInitExpression)x, (ListInitExpression)y); - - case ExpressionType.MemberAccess: - return AreEqual((MemberExpression)x, (MemberExpression)y); - - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.ArrayIndex: - case ExpressionType.Coalesce: - case ExpressionType.Divide: - case ExpressionType.Equal: - case ExpressionType.ExclusiveOr: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LeftShift: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.Modulo: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.NotEqual: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.RightShift: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - return AreEqual((BinaryExpression)x, (BinaryExpression)y); - - case ExpressionType.MemberInit: - return AreEqual((MemberInitExpression)x, (MemberInitExpression)y); - - case ExpressionType.New: - return AreEqual((NewExpression)x, (NewExpression)y); - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - return AreEqual((NewArrayExpression)x, (NewArrayExpression)y); - - case ExpressionType.Parameter: - return AreEqual((ParameterExpression)x, (ParameterExpression)y); - - case ExpressionType.Quote: - x = ((UnaryExpression)x).Operand; - y = ((UnaryExpression)y).Operand; - continue; - - case ExpressionType.TypeIs: - return AreEqual((TypeBinaryExpression)x, (TypeBinaryExpression)y); - } - - throw new NotImplementedException("Unable to equate Expressions of type " + x.NodeType); - } - } - - private bool AllEqual(IList xItems, IList yItems) - { - if (xItems.Count != yItems.Count) - { - return false; - } - - for (var i = 0; i < xItems.Count; i++) - { - if (!Equals(xItems[i], yItems[i])) - { - return false; - } - } - - return true; - } - - private bool AreEqual(MethodCallExpression x, MethodCallExpression y) - { - return ReferenceEquals(x.Method, y.Method) && - (((x.Object == null) && (y.Object == null)) || ((x.Object != null) && Equals(x.Object, y.Object))) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(ConditionalExpression x, ConditionalExpression y) - { - return (x.Type == y.Type) && Equals(x.Test, y.Test) && - Equals(x.IfTrue, y.IfTrue) && Equals(x.IfFalse, y.IfFalse); - } - - private static bool AreEqual(ConstantExpression x, ConstantExpression y) - { - return ReferenceEquals(x.Value, y.Value) || x.Value.Equals(y.Value); - } - - private bool AreEqual(UnaryExpression x, UnaryExpression y) - { - return (x.Type == y.Type) && Equals(x.Operand, y.Operand); - } - - private bool AreEqual(IndexExpression x, IndexExpression y) - { - return ReferenceEquals(x.Indexer, y.Indexer) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(ListInitExpression x, ListInitExpression y) - { - return (x.Type == y.Type) && Equals(x.NewExpression, y.NewExpression) && - AllEqual(x.Initializers, y.Initializers); - } - - private bool AllEqual(IList xInitializers, IList yInitializers) - { - if (xInitializers.Count != yInitializers.Count) - { - return false; - } - - for (var i = 0; i < xInitializers.Count; i++) - { - var x = xInitializers[i]; - var y = yInitializers[i]; - - if (!ReferenceEquals(x.AddMethod, y.AddMethod) || - !AllEqual(x.Arguments, y.Arguments)) - { - return false; - } - } - - return true; - } - - private static bool AreEqual(MemberExpression x, MemberExpression y) - { - if (ReferenceEquals(x.Member, y.Member)) - { - return true; - } - - // ReSharper disable once PossibleNullReferenceException - return (x.Member.Name == y.Member.Name) && - y.Member.DeclaringType.IsAssignableFrom(x.Member.DeclaringType); - } - - private bool AreEqual(BinaryExpression x, BinaryExpression y) - { - return ReferenceEquals(x.Method, y.Method) && - Equals(x.Left, y.Left) && Equals(x.Right, y.Right); - } - - private bool AreEqual(MemberInitExpression x, MemberInitExpression y) - { - return Equals(x.NewExpression, y.NewExpression) && - AllEqual(x.Bindings, y.Bindings); - } - - private bool AllEqual(IList xBindings, IList yBindings) - { - if (xBindings.Count != yBindings.Count) - { - return false; - } - - for (var i = 0; i < xBindings.Count; i++) - { - var x = xBindings[i]; - var y = yBindings[i]; - - if ((x.BindingType != y.BindingType) || !ReferenceEquals(x.Member, y.Member)) - { - return false; - } - - switch (x.BindingType) - { - case MemberBindingType.Assignment: - if (Equals(((MemberAssignment)x).Expression, ((MemberAssignment)y).Expression)) - { - break; - } - return false; - - case MemberBindingType.MemberBinding: - if (AllEqual(((MemberMemberBinding)x).Bindings, ((MemberMemberBinding)y).Bindings)) - { - break; - } - return false; - - case MemberBindingType.ListBinding: - if (AreEqual((MemberListBinding)x, (MemberListBinding)y)) - { - break; - } - return false; - } - } - - return true; - } - - private bool AreEqual(MemberListBinding x, MemberListBinding y) - { - if (x.Initializers.Count != y.Initializers.Count) - { - return false; - } - - for (var i = 0; i < x.Initializers.Count; i++) - { - var xInitialiser = x.Initializers[i]; - var yInitialiser = y.Initializers[i]; - - if (!ReferenceEquals(xInitialiser.AddMethod, yInitialiser.AddMethod) || - !AllEqual(xInitialiser.Arguments, yInitialiser.Arguments)) - { - return false; - } - } - - return true; - } - - private bool AreEqual(NewExpression x, NewExpression y) - { - return (x.Type == y.Type) && ReferenceEquals(x.Constructor, y.Constructor) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(NewArrayExpression x, NewArrayExpression y) - { - return (x.Type == y.Type) && AllEqual(x.Expressions, y.Expressions); - } - - private static bool AreEqual(ParameterExpression x, ParameterExpression y) - { - return (x.Type == y.Type) && (x.Name == y.Name); - } - - private bool AreEqual(TypeBinaryExpression x, TypeBinaryExpression y) - => (x.TypeOperand == y.TypeOperand) && Equals(x.Expression, y.Expression); - - public int GetHashCode(Expression obj) => 0; - } -} \ No newline at end of file diff --git a/AgileMapper/Extensions/ExpressionEvaluation.cs b/AgileMapper/Extensions/ExpressionEvaluation.cs new file mode 100644 index 000000000..181462d92 --- /dev/null +++ b/AgileMapper/Extensions/ExpressionEvaluation.cs @@ -0,0 +1,324 @@ +namespace AgileObjects.AgileMapper.Extensions +{ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; +#if NET_STANDARD + using System.Reflection; +#endif + + internal static class ExpressionEvaluation + { + private static readonly IEqualityComparer _equator = new ExpressionEquator(MemberAccessesAreEqual); + + public static readonly IEqualityComparer Equivalator = new ExpressionEquator(MemberAccessesAreEquivalent); + + public static bool AreEqual(Expression x, Expression y) => _equator.Equals(x, y); + + public static bool AreEquivalent(Expression x, Expression y) => Equivalator.Equals(x, y); + + #region Member Access Evaluation + + private static bool MemberAccessesAreEqual(ExpressionEquator equator, MemberExpression x, MemberExpression y) + { + return MemberAccessesAreEquivalent(equator, x, y) && equator.Equals(x.Expression, y.Expression); + } + + private static bool MemberAccessesAreEquivalent(ExpressionEquator equator, MemberExpression x, MemberExpression y) + { + if (ReferenceEquals(x.Member, y.Member)) + { + return true; + } + + // ReSharper disable once PossibleNullReferenceException + return (x.Member.Name == y.Member.Name) && + y.Member.DeclaringType.IsAssignableFrom(x.Member.DeclaringType); + } + + #endregion + + private class ExpressionEquator : IEqualityComparer + { + private readonly Func _memberAccessComparer; + + public ExpressionEquator( + Func memberAccessComparer) + { + _memberAccessComparer = memberAccessComparer; + } + + public bool Equals(Expression x, Expression y) + { + while (true) + { + if (x.NodeType != y.NodeType) + { + return false; + } + + switch (x.NodeType) + { + case ExpressionType.Call: + return AreEqual((MethodCallExpression)x, (MethodCallExpression)y); + + + case ExpressionType.Conditional: + return AreEqual((ConditionalExpression)x, (ConditionalExpression)y); + + case ExpressionType.Constant: + return AreEqual((ConstantExpression)x, (ConstantExpression)y); + + case ExpressionType.ArrayLength: + case ExpressionType.Convert: + case ExpressionType.Negate: + case ExpressionType.NegateChecked: + case ExpressionType.Not: + case ExpressionType.TypeAs: + return AreEqual((UnaryExpression)x, (UnaryExpression)y); + + case ExpressionType.Default: + return ((DefaultExpression)x).Type == ((DefaultExpression)y).Type; + + case ExpressionType.Index: + return AreEqual((IndexExpression)x, (IndexExpression)y); + + case ExpressionType.Lambda: + x = ((LambdaExpression)x).Body; + y = ((LambdaExpression)y).Body; + continue; + + case ExpressionType.ListInit: + return AreEqual((ListInitExpression)x, (ListInitExpression)y); + + case ExpressionType.MemberAccess: + return _memberAccessComparer.Invoke(this, (MemberExpression)x, (MemberExpression)y); + + case ExpressionType.Add: + case ExpressionType.AddChecked: + case ExpressionType.And: + case ExpressionType.AndAlso: + case ExpressionType.ArrayIndex: + case ExpressionType.Coalesce: + case ExpressionType.Divide: + case ExpressionType.Equal: + case ExpressionType.ExclusiveOr: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + case ExpressionType.LeftShift: + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.Modulo: + case ExpressionType.Multiply: + case ExpressionType.MultiplyChecked: + case ExpressionType.NotEqual: + case ExpressionType.Or: + case ExpressionType.OrElse: + case ExpressionType.RightShift: + case ExpressionType.Subtract: + case ExpressionType.SubtractChecked: + return AreEqual((BinaryExpression)x, (BinaryExpression)y); + + case ExpressionType.MemberInit: + return AreEqual((MemberInitExpression)x, (MemberInitExpression)y); + + case ExpressionType.New: + return AreEqual((NewExpression)x, (NewExpression)y); + + case ExpressionType.NewArrayBounds: + case ExpressionType.NewArrayInit: + return AreEqual((NewArrayExpression)x, (NewArrayExpression)y); + + case ExpressionType.Parameter: + return AreEqual((ParameterExpression)x, (ParameterExpression)y); + + case ExpressionType.Quote: + x = ((UnaryExpression)x).Operand; + y = ((UnaryExpression)y).Operand; + continue; + + case ExpressionType.TypeIs: + return AreEqual((TypeBinaryExpression)x, (TypeBinaryExpression)y); + } + + throw new NotImplementedException("Unable to equate Expressions of type " + x.NodeType); + } + } + + private bool AllEqual(IList xItems, IList yItems) + { + if (xItems.Count != yItems.Count) + { + return false; + } + + for (var i = 0; i < xItems.Count; i++) + { + if (!Equals(xItems[i], yItems[i])) + { + return false; + } + } + + return true; + } + + private bool AreEqual(MethodCallExpression x, MethodCallExpression y) + { + return ReferenceEquals(x.Method, y.Method) && + (((x.Object == null) && (y.Object == null)) || ((x.Object != null) && Equals(x.Object, y.Object))) && + AllEqual(x.Arguments, y.Arguments); + } + + private bool AreEqual(ConditionalExpression x, ConditionalExpression y) + { + return (x.Type == y.Type) && Equals(x.Test, y.Test) && + Equals(x.IfTrue, y.IfTrue) && Equals(x.IfFalse, y.IfFalse); + } + + private static bool AreEqual(ConstantExpression x, ConstantExpression y) + { + return ReferenceEquals(x.Value, y.Value) || x.Value.Equals(y.Value); + } + + private bool AreEqual(UnaryExpression x, UnaryExpression y) + { + return (x.Type == y.Type) && Equals(x.Operand, y.Operand); + } + + private bool AreEqual(IndexExpression x, IndexExpression y) + { + return ReferenceEquals(x.Indexer, y.Indexer) && + AllEqual(x.Arguments, y.Arguments); + } + + private bool AreEqual(ListInitExpression x, ListInitExpression y) + { + return (x.Type == y.Type) && Equals(x.NewExpression, y.NewExpression) && + AllEqual(x.Initializers, y.Initializers); + } + + private bool AllEqual(IList xInitializers, IList yInitializers) + { + if (xInitializers.Count != yInitializers.Count) + { + return false; + } + + for (var i = 0; i < xInitializers.Count; i++) + { + var x = xInitializers[i]; + var y = yInitializers[i]; + + if (!ReferenceEquals(x.AddMethod, y.AddMethod) || + !AllEqual(x.Arguments, y.Arguments)) + { + return false; + } + } + + return true; + } + + private bool AreEqual(BinaryExpression x, BinaryExpression y) + { + return ReferenceEquals(x.Method, y.Method) && + Equals(x.Left, y.Left) && Equals(x.Right, y.Right); + } + + private bool AreEqual(MemberInitExpression x, MemberInitExpression y) + { + return Equals(x.NewExpression, y.NewExpression) && + AllEqual(x.Bindings, y.Bindings); + } + + private bool AllEqual(IList xBindings, IList yBindings) + { + if (xBindings.Count != yBindings.Count) + { + return false; + } + + for (var i = 0; i < xBindings.Count; i++) + { + var x = xBindings[i]; + var y = yBindings[i]; + + if ((x.BindingType != y.BindingType) || !ReferenceEquals(x.Member, y.Member)) + { + return false; + } + + switch (x.BindingType) + { + case MemberBindingType.Assignment: + if (Equals(((MemberAssignment)x).Expression, ((MemberAssignment)y).Expression)) + { + break; + } + return false; + + case MemberBindingType.MemberBinding: + if (AllEqual(((MemberMemberBinding)x).Bindings, ((MemberMemberBinding)y).Bindings)) + { + break; + } + return false; + + case MemberBindingType.ListBinding: + if (AreEqual((MemberListBinding)x, (MemberListBinding)y)) + { + break; + } + return false; + } + } + + return true; + } + + private bool AreEqual(MemberListBinding x, MemberListBinding y) + { + if (x.Initializers.Count != y.Initializers.Count) + { + return false; + } + + for (var i = 0; i < x.Initializers.Count; i++) + { + var xInitialiser = x.Initializers[i]; + var yInitialiser = y.Initializers[i]; + + if (!ReferenceEquals(xInitialiser.AddMethod, yInitialiser.AddMethod) || + !AllEqual(xInitialiser.Arguments, yInitialiser.Arguments)) + { + return false; + } + } + + return true; + } + + private bool AreEqual(NewExpression x, NewExpression y) + { + return (x.Type == y.Type) && ReferenceEquals(x.Constructor, y.Constructor) && + AllEqual(x.Arguments, y.Arguments); + } + + private bool AreEqual(NewArrayExpression x, NewArrayExpression y) + { + return (x.Type == y.Type) && AllEqual(x.Expressions, y.Expressions); + } + + private static bool AreEqual(ParameterExpression x, ParameterExpression y) + { + return (x.Type == y.Type) && (x.Name == y.Name); + } + + private bool AreEqual(TypeBinaryExpression x, TypeBinaryExpression y) + => (x.TypeOperand == y.TypeOperand) && Equals(x.Expression, y.Expression); + + public int GetHashCode(Expression obj) => 0; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Extensions/ExpressionReplacementDictionary.cs b/AgileMapper/Extensions/ExpressionReplacementDictionary.cs index 1fc2d97b0..ff54d5b63 100644 --- a/AgileMapper/Extensions/ExpressionReplacementDictionary.cs +++ b/AgileMapper/Extensions/ExpressionReplacementDictionary.cs @@ -6,7 +6,7 @@ namespace AgileObjects.AgileMapper.Extensions internal class ExpressionReplacementDictionary : Dictionary { public ExpressionReplacementDictionary(int capacity) - : base(capacity, ExpressionEquator.Instance) + : base(capacity, ExpressionEvaluation.Equivalator) { } } From 23c08cb183402742d2b22df9fc960e9b574e4983 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 4 Nov 2017 14:10:45 +0000 Subject: [PATCH 011/176] Reducing indirection in member population + size of IDataSource interface --- AgileMapper/DataSources/DataSourceBase.cs | 3 --- AgileMapper/DataSources/DataSourceSet.cs | 2 +- AgileMapper/DataSources/IDataSource.cs | 2 -- AgileMapper/Members/MemberMapperDataExtensions.cs | 3 +++ .../ComplexTypes/ComplexTypeMappingExpressionFactory.cs | 5 +---- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index aebb8a1a9..4a0c04efa 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -116,8 +116,5 @@ public Expression AddCondition(Expression value, Expression alternateBranch = nu ? Expression.IfThenElse(Condition, value, alternateBranch) : Expression.IfThen(Condition, value); } - - public Expression GetTargetMemberPopulation(IMemberMapperData mapperData) - => mapperData.GetTargetMemberPopulation(Value); } } \ No newline at end of file diff --git a/AgileMapper/DataSources/DataSourceSet.cs b/AgileMapper/DataSources/DataSourceSet.cs index c622702d2..c403b1cf2 100644 --- a/AgileMapper/DataSources/DataSourceSet.cs +++ b/AgileMapper/DataSources/DataSourceSet.cs @@ -79,7 +79,7 @@ public Expression GetPopulationExpression(IMemberMapperData mapperData) continue; } - var memberPopulation = dataSource.GetTargetMemberPopulation(mapperData); + var memberPopulation = mapperData.GetTargetMemberPopulation(dataSource.Value); population = dataSource.AddCondition(memberPopulation, population); population = dataSource.AddPreCondition(population); diff --git a/AgileMapper/DataSources/IDataSource.cs b/AgileMapper/DataSources/IDataSource.cs index 436525359..d1a03ca47 100644 --- a/AgileMapper/DataSources/IDataSource.cs +++ b/AgileMapper/DataSources/IDataSource.cs @@ -20,7 +20,5 @@ internal interface IDataSource : IConditionallyChainable Expression AddPreCondition(Expression population); Expression AddCondition(Expression value, Expression alternateBranch = null); - - Expression GetTargetMemberPopulation(IMemberMapperData mapperData); } } diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 02d86b4f9..4c1afa79d 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -18,6 +18,9 @@ public static bool IsStandalone(this IObjectMappingData mappingData) public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) => mapperData.IsRoot && mapperData.RuleSet.Settings.UseSingleRootMappingExpression; + public static bool UseMemberInitialisation(this IBasicMapperData mapperData) + => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.TargetMemberIsUserStruct(); + public static IMemberMapperData GetRootMapperData(this IMemberMapperData mapperData) { while (!mapperData.IsRoot) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index aea50885e..8c34dd4cb 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -136,16 +136,13 @@ protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingD protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { - var expressionFactory = UseMemberInitialisation(mappingData.MapperData) + var expressionFactory = mappingData.MapperData.UseMemberInitialisation() ? _memberInitPopulationFactory : _multiStatementPopulationFactory; return expressionFactory.GetPopulation(mappingData); } - private static bool UseMemberInitialisation(IBasicMapperData mapperData) - => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.TargetMemberIsUserStruct(); - public override void Reset() => _constructionFactory.Reset(); } } \ No newline at end of file From dfdf60444fef908a81d4e371ddf4efaa335fa543 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 4 Nov 2017 15:13:29 +0000 Subject: [PATCH 012/176] Moving MapperData property from MemberPopulation into DataSourceSet --- .../Configuration/UserConfigurationSet.cs | 2 +- AgileMapper/DataSources/DataSourceFinder.cs | 10 +++++----- AgileMapper/DataSources/DataSourceSet.cs | 19 ++++++++++++------- .../Members/Population/MemberPopulation.cs | 17 ++++++----------- .../DictionaryPopulationBuilder.cs | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/AgileMapper/Configuration/UserConfigurationSet.cs b/AgileMapper/Configuration/UserConfigurationSet.cs index c69a6d287..399dbd4b8 100644 --- a/AgileMapper/Configuration/UserConfigurationSet.cs +++ b/AgileMapper/Configuration/UserConfigurationSet.cs @@ -175,7 +175,7 @@ public void Add(ConfiguredDataSourceFactory dataSourceFactory) DataSourceFactories.AddSortFilter(dataSourceFactory); } - public IEnumerable GetDataSources(IMemberMapperData mapperData) + public IList GetDataSources(IMemberMapperData mapperData) => _dataSourceFactories.FindMatches(mapperData).Select(dsf => dsf.Create(mapperData)).ToArray(); #endregion diff --git a/AgileMapper/DataSources/DataSourceFinder.cs b/AgileMapper/DataSources/DataSourceFinder.cs index 45f84be4e..032dac59c 100644 --- a/AgileMapper/DataSources/DataSourceFinder.cs +++ b/AgileMapper/DataSources/DataSourceFinder.cs @@ -26,7 +26,7 @@ public DataSourceSet FindFor(IChildMemberMappingData childMappingData) .Where(ds => ds.IsValid) .ToArray(); - return new DataSourceSet(validDataSources); + return new DataSourceSet(childMappingData.MapperData, validDataSources); } private IEnumerable EnumerateDataSources(IChildMemberMappingData childMappingData) @@ -75,7 +75,7 @@ private IEnumerable EnumerateDataSources(IChildMemberMappingData ch private static bool DataSourcesAreConfigured( IMemberMapperData mapperData, - out IEnumerable configuredDataSources) + out IList configuredDataSources) { configuredDataSources = mapperData .MapperContext @@ -103,7 +103,7 @@ private bool UseMaptimeDataSources( } private static IEnumerable GetSourceMemberDataSources( - IEnumerable configuredDataSources, + IList configuredDataSources, int dataSourceIndex, IChildMemberMappingData mappingData) { @@ -116,7 +116,7 @@ private static IEnumerable GetSourceMemberDataSources( var matchingSourceMemberDataSource = GetSourceMemberDataSourceOrNull(bestMatchingSourceMember, mappingData); if ((matchingSourceMemberDataSource == null) || - configuredDataSources.Any(cds => cds.IsSameAs(matchingSourceMemberDataSource))) + configuredDataSources.Any(cds => cds.IsSameAs(matchingSourceMemberDataSource))) { if (dataSourceIndex == 0) { @@ -126,7 +126,7 @@ private static IEnumerable GetSourceMemberDataSources( yield return new ComplexTypeMappingDataSource(dataSourceIndex, mappingData); } } - else + else if (configuredDataSources.Any() && configuredDataSources.Last().IsConditional) { yield return GetFallbackDataSourceFor(mappingData); } diff --git a/AgileMapper/DataSources/DataSourceSet.cs b/AgileMapper/DataSources/DataSourceSet.cs index c403b1cf2..6a152d03f 100644 --- a/AgileMapper/DataSources/DataSourceSet.cs +++ b/AgileMapper/DataSources/DataSourceSet.cs @@ -11,8 +11,11 @@ internal class DataSourceSet : IEnumerable private readonly IList _dataSources; private readonly List _variables; - public DataSourceSet(params IDataSource[] dataSources) + public DataSourceSet( + IMemberMapperData mapperData, + params IDataSource[] dataSources) { + MapperData = mapperData; _dataSources = dataSources; _variables = new List(); None = dataSources.Length == 0; @@ -38,6 +41,8 @@ public DataSourceSet(params IDataSource[] dataSources) } } + public IMemberMapperData MapperData { get; } + public bool None { get; } public bool HasValue { get; } @@ -50,9 +55,9 @@ public DataSourceSet(params IDataSource[] dataSources) public Expression GetValueExpression() => _dataSources.ReverseChain(); - public Expression GetPopulationExpression(IMemberMapperData mapperData) + public Expression GetPopulationExpression() { - var fallbackValue = GetFallbackValueOrNull(mapperData); + var fallbackValue = GetFallbackValueOrNull(); var excludeFallback = fallbackValue == null; Expression population = null; @@ -68,7 +73,7 @@ public Expression GetPopulationExpression(IMemberMapperData mapperData) continue; } - population = mapperData.GetTargetMemberPopulation(fallbackValue); + population = MapperData.GetTargetMemberPopulation(fallbackValue); if (dataSource.IsConditional) { @@ -79,7 +84,7 @@ public Expression GetPopulationExpression(IMemberMapperData mapperData) continue; } - var memberPopulation = mapperData.GetTargetMemberPopulation(dataSource.Value); + var memberPopulation = MapperData.GetTargetMemberPopulation(dataSource.Value); population = dataSource.AddCondition(memberPopulation, population); population = dataSource.AddPreCondition(population); @@ -88,7 +93,7 @@ public Expression GetPopulationExpression(IMemberMapperData mapperData) return population; } - private Expression GetFallbackValueOrNull(IMemberMapperData mapperData) + private Expression GetFallbackValueOrNull() { var fallbackValue = _dataSources.Last().Value; @@ -102,7 +107,7 @@ private Expression GetFallbackValueOrNull(IMemberMapperData mapperData) return ((BinaryExpression)fallbackValue).Right; } - var targetMemberAccess = mapperData.GetTargetMemberAccess(); + var targetMemberAccess = MapperData.GetTargetMemberAccess(); if (ExpressionEvaluation.AreEqual(fallbackValue, targetMemberAccess)) { diff --git a/AgileMapper/Members/Population/MemberPopulation.cs b/AgileMapper/Members/Population/MemberPopulation.cs index 41b59df58..b25146b7e 100644 --- a/AgileMapper/Members/Population/MemberPopulation.cs +++ b/AgileMapper/Members/Population/MemberPopulation.cs @@ -13,12 +13,8 @@ internal class MemberPopulation : IMemberPopulation private readonly DataSourceSet _dataSources; private readonly Expression _populateCondition; - private MemberPopulation( - IMemberMapperData mapperData, - DataSourceSet dataSources, - Expression populateCondition = null) + private MemberPopulation(DataSourceSet dataSources, Expression populateCondition = null) { - MapperData = mapperData; _dataSources = dataSources; _populateCondition = populateCondition; } @@ -48,7 +44,7 @@ public static IMemberPopulation WithoutRegistration( populateCondition = GetPopulateCondition(populateCondition, mappingData); } - return new MemberPopulation(mappingData.MapperData, dataSources, populateCondition); + return new MemberPopulation(dataSources, populateCondition); } private static Expression GetPopulateCondition(Expression populateCondition, IChildMemberMappingData mappingData) @@ -88,16 +84,15 @@ private static IMemberPopulation CreateNullMemberPopulation( IMemberMapperData mapperData, Func commentFactory) { - return new MemberPopulation( - mapperData, - new DataSourceSet( + return new MemberPopulation(new DataSourceSet( + mapperData, new NullDataSource( ReadableExpression.Comment(commentFactory.Invoke(mapperData.TargetMember))))); } #endregion - public IMemberMapperData MapperData { get; } + public IMemberMapperData MapperData => _dataSources.MapperData; public bool IsSuccessful => _dataSources.HasValue; @@ -112,7 +107,7 @@ public Expression GetPopulation() ? GetBinding() : MapperData.TargetMember.IsReadOnly ? GetReadOnlyMemberPopulation() - : _dataSources.GetPopulationExpression(MapperData); + : _dataSources.GetPopulationExpression(); if (_dataSources.Variables.Any()) { diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs index 96661cb50..68088ceef 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs @@ -171,7 +171,7 @@ private Expression GetPopulation( var sourceMember = mappingData.MapperData.SourceMember; var mappingDataSource = new AdHocDataSource(sourceMember, elementMapping); - var mappingDataSources = new DataSourceSet(mappingDataSource); + var mappingDataSources = new DataSourceSet(elementMapperData, mappingDataSource); var memberPopulation = MemberPopulation.WithoutRegistration(elementMappingData, mappingDataSources); var populationExpression = memberPopulation.GetPopulation(); From 7cfcb23b4a26a7e3e1ee13610a29cfd206716895 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 4 Nov 2017 16:25:47 +0000 Subject: [PATCH 013/176] Support for string-to-bool projection with case-sensitive 'true' matching --- .../AgileMapper.UnitTests.Ef6.csproj | 1 + AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs | 3 +- .../WhenConvertingToBools.cs | 102 +++++++++++++++++- .../TestClasses/PublicStringProperty.cs | 13 +++ AgileMapper.UnitTests.Ef6/TestDbContext.cs | 4 +- AgileMapper/MappingRuleSetCollection.cs | 9 +- AgileMapper/MappingRuleSetSettings.cs | 2 + AgileMapper/Members/ExpressionInfoFinder.cs | 23 +++- .../Members/MemberMapperDataExtensions.cs | 9 +- .../Members/Population/MemberPopulation.cs | 2 +- ...ExistingOrDefaultValueDataSourceFactory.cs | 2 +- 11 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index 249c6203e..dfd9f2812 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -94,6 +94,7 @@ + diff --git a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs index 1268b7e84..d5821632f 100644 --- a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs +++ b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs @@ -23,8 +23,9 @@ protected void RunTest(Action testAction) private void EmptyDbContext() { _context.Products.RemoveRange(_context.Products); - _context.IntItems.RemoveRange(_context.IntItems); _context.BoolItems.RemoveRange(_context.BoolItems); + _context.IntItems.RemoveRange(_context.IntItems); + _context.StringItems.RemoveRange(_context.StringItems); _context.SaveChanges(); } } diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs index 68f44cb3c..6bc8511e0 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -13,7 +13,7 @@ public WhenConvertingToBools(TestContext context) } [Fact] - public void ShouldMapAnIntOneToTrue() + public void ShouldProjectAnIntOneToTrue() { RunTest(context => { @@ -27,7 +27,7 @@ public void ShouldMapAnIntOneToTrue() } [Fact] - public void ShouldMapAnIntZeroToFalse() + public void ShouldProjectAnIntZeroToFalse() { RunTest(context => { @@ -39,5 +39,103 @@ public void ShouldMapAnIntZeroToFalse() boolItem.Value.ShouldBeFalse(); }); } + + [Fact] + public void ShouldProjectAStringTrueToTrue() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "true" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringTrueToTrueIgnoringCase() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "tRuE" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringOneToTrue() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "1" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringFalseToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "false" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringZeroToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "0" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringNonBooleanValueToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "uokyujhygt" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringNullToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = null }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } } } diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs new file mode 100644 index 000000000..fdb91b4d9 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicStringProperty + { + [Key] + public int Id { get; set; } + + + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs index be5a74e3e..87810a274 100644 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -13,8 +13,10 @@ public TestDbContext() public DbSet Products { get; set; } + public DbSet BoolItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet BoolItems { get; set; } + public DbSet StringItems { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 9a9e3408f..8648f1ddd 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -15,7 +15,8 @@ internal class MappingRuleSetCollection new MappingRuleSetSettings { SourceElementsCouldBeNull = true, - UseTryCatch = true + UseTryCatch = true, + GuardStringAccesses = true }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, @@ -27,7 +28,8 @@ internal class MappingRuleSetCollection { RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, - UseTryCatch = true + UseTryCatch = true, + GuardStringAccesses = true }, MergeEnumerablePopulationStrategy.Instance, PreserveExistingValueMemberPopulationGuardFactory.Instance, @@ -39,7 +41,8 @@ internal class MappingRuleSetCollection { RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, - UseTryCatch = true + UseTryCatch = true, + GuardStringAccesses = true }, OverwriteEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index db14b1969..d09860dc1 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -11,5 +11,7 @@ internal class MappingRuleSetSettings public bool UseMemberInitialisation { get; set; } public bool UseTryCatch { get; set; } + + public bool GuardStringAccesses { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Members/ExpressionInfoFinder.cs b/AgileMapper/Members/ExpressionInfoFinder.cs index 0dba1e1cd..c4323073c 100644 --- a/AgileMapper/Members/ExpressionInfoFinder.cs +++ b/AgileMapper/Members/ExpressionInfoFinder.cs @@ -15,9 +15,12 @@ public ExpressionInfoFinder(Expression mappingDataObject) _mappingDataObject = mappingDataObject; } - public ExpressionInfo FindIn(Expression expression, bool targetCanBeNull) + public ExpressionInfo FindIn( + Expression expression, + bool targetCanBeNull, + bool guardStringAccesses) { - var finder = new ExpressionInfoFinderInstance(_mappingDataObject, targetCanBeNull); + var finder = new ExpressionInfoFinderInstance(_mappingDataObject, targetCanBeNull, guardStringAccesses); var info = finder.FindIn(expression); return info; @@ -32,18 +35,25 @@ private class ExpressionInfoFinderInstance : ExpressionVisitor private readonly ICollection _nullCheckSubjects; private readonly Dictionary _nestedAccessesByPath; private readonly bool _includeTargetNullChecking; + private readonly bool _guardStringAccesses; public ExpressionInfoFinderInstance( Expression mappingDataObject, - bool targetCanBeNull) + bool targetCanBeNull, + bool guardStringAccesses) { _mappingDataObject = mappingDataObject; - _stringMemberAccessSubjects = new List(); _allInvocations = new List(); _multiInvocations = new List(); _nullCheckSubjects = new List(); _nestedAccessesByPath = new Dictionary(); _includeTargetNullChecking = targetCanBeNull; + _guardStringAccesses = guardStringAccesses; + + //if (guardStringAccesses) + { + _stringMemberAccessSubjects = new List(); + } } public ExpressionInfo FindIn(Expression expression) @@ -166,7 +176,10 @@ private void AddExistingNullCheck(Expression checkedAccess) private void AddStringMemberAccessSubjectIfAppropriate(Expression member) { - if ((member != null) && (member.Type == typeof(string)) && AccessSubjectCouldBeNull(member)) + if (_guardStringAccesses && + (member != null) && + (member.Type == typeof(string)) && + AccessSubjectCouldBeNull(member)) { _stringMemberAccessSubjects.Add(member); } diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 4c1afa79d..48b7a9d05 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -18,8 +18,8 @@ public static bool IsStandalone(this IObjectMappingData mappingData) public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) => mapperData.IsRoot && mapperData.RuleSet.Settings.UseSingleRootMappingExpression; - public static bool UseMemberInitialisation(this IBasicMapperData mapperData) - => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.TargetMemberIsUserStruct(); + public static bool UseMemberInitialisation(this IMemberMapperData mapperData) + => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.Context.IsPartOfUserStructMapping; public static IMemberMapperData GetRootMapperData(this IMemberMapperData mapperData) { @@ -72,7 +72,10 @@ public static ExpressionInfoFinder.ExpressionInfo GetExpressionInfoFor( Expression value, bool targetCanBeNull) { - return mapperData.ExpressionInfoFinder.FindIn(value, targetCanBeNull); + return mapperData.ExpressionInfoFinder.FindIn( + value, + targetCanBeNull, + mapperData.RuleSet.Settings.GuardStringAccesses); } public static bool SourceIsNotFlatObject(this IMemberMapperData mapperData) diff --git a/AgileMapper/Members/Population/MemberPopulation.cs b/AgileMapper/Members/Population/MemberPopulation.cs index b25146b7e..e84aff09c 100644 --- a/AgileMapper/Members/Population/MemberPopulation.cs +++ b/AgileMapper/Members/Population/MemberPopulation.cs @@ -103,7 +103,7 @@ public Expression GetPopulation() return _dataSources.GetValueExpression(); } - var population = MapperData.Context.IsPartOfUserStructMapping + var population = MapperData.UseMemberInitialisation() ? GetBinding() : MapperData.TargetMember.IsReadOnly ? GetReadOnlyMemberPopulation() diff --git a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs index b3a675ec7..ffb388758 100644 --- a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs +++ b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs @@ -28,7 +28,7 @@ private static Expression GetValue(IMemberMapperData mapperData) : mapperData.GetFallbackCollectionValue(); } - if (mapperData.TargetMember.IsReadable && !mapperData.TargetMemberIsUserStruct()) + if (mapperData.TargetMember.IsReadable && !mapperData.UseMemberInitialisation()) { return mapperData.GetTargetMemberAccess(); } From 734c34df6625fea5935672cc460f2581d4bd4496 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 5 Nov 2017 17:17:53 +0000 Subject: [PATCH 014/176] Replacing case-insensitive string.Equals with stirng.ToLower == in query projections --- AgileMapper/Members/MappingInstanceData.cs | 4 +- .../QueryProjectionExpressionFactory.cs | 9 ++++ .../Queryables/QueryProjectionModifier.cs | 46 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 AgileMapper/Queryables/QueryProjectionModifier.cs diff --git a/AgileMapper/Members/MappingInstanceData.cs b/AgileMapper/Members/MappingInstanceData.cs index 4d06379ee..d2f94a9ef 100644 --- a/AgileMapper/Members/MappingInstanceData.cs +++ b/AgileMapper/Members/MappingInstanceData.cs @@ -35,7 +35,7 @@ protected MappingInstanceData( T IMappingData.GetSource() { - if (typeof(TSource).IsAssignableFrom(typeof(T))) + if (typeof(T).IsAssignableFrom(typeof(TSource))) { return (T)((object)Source); } @@ -45,7 +45,7 @@ T IMappingData.GetSource() T IMappingData.GetTarget() { - if (typeof(TTarget).IsAssignableFrom(typeof(T))) + if (typeof(T).IsAssignableFrom(typeof(TTarget))) { return (T)((object)Target); } diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index a30dd260f..ca7c0a53c 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -22,6 +22,13 @@ internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase #endregion + private readonly QueryProjectionModifier _queryProjectionModifier; + + private QueryProjectionExpressionFactory() + { + _queryProjectionModifier = new QueryProjectionModifier(); + } + public override bool IsFor(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; @@ -45,6 +52,8 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); + queryProjection = _queryProjectionModifier.Modify(queryProjection); + yield return queryProjection; } } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs new file mode 100644 index 000000000..6225e91f1 --- /dev/null +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -0,0 +1,46 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq; + using System.Linq.Expressions; + using Extensions; + using NetStandardPolyfills; + + internal class QueryProjectionModifier : ExpressionVisitor + { + public Expression Modify(Expression queryProjection) + { + return VisitAndConvert(queryProjection, "Modify"); + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCall) + { + if (IsStringEqualsIgnoreCase(methodCall)) + { + return GetStringToLowerEqualsComparison(methodCall); + } + + return base.VisitMethodCall(methodCall); + } + + private static bool IsStringEqualsIgnoreCase(MethodCallExpression methodCall) + { + return methodCall.Method.IsStatic && + (methodCall.Arguments.Count == 3) && + (methodCall.Method.DeclaringType == typeof(string)) && + (methodCall.Method.Name == "Equals"); + } + + private static Expression GetStringToLowerEqualsComparison(MethodCallExpression methodCall) + { + var subjectToLower = Expression.Call( + methodCall.Arguments[0], + typeof(string) + .GetPublicInstanceMethods() + .First(m => (m.Name == "ToLower") && (m.GetParameters().None()))); + + var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); + + return comparison; + } + } +} \ No newline at end of file From 6fce67d1877b848b5a52040f3c44eafc1dd3bd26 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 5 Nov 2017 17:28:01 +0000 Subject: [PATCH 015/176] Start of support for projecting to ints --- .../AgileMapper.UnitTests.Ef6.csproj | 3 ++ .../WhenConvertingToInts.cs | 29 +++++++++++++++++++ .../TestClasses/PublicIntPropertyDto.cs | 10 +++++++ .../TestClasses/PublicShortProperty.cs | 13 +++++++++ AgileMapper.UnitTests.Ef6/TestDbContext.cs | 2 ++ .../Queryables/QueryProjectionModifier.cs | 6 ++-- 6 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index dfd9f2812..18a805bc9 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -89,10 +89,13 @@ + + + diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs new file mode 100644 index 000000000..6dbb4b867 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion +{ + using System.Linq; + using Shouldly; + using TestClasses; + using Xunit; + + public class WhenConvertingToInts : Ef6TestClassBase + { + public WhenConvertingToInts(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldMapAShortToAnInt() + { + RunTest(context => + { + context.ShortItems.Add(new PublicShortProperty { Value = 123 }); + context.SaveChanges(); + + var intItem = context.ShortItems.ProjectTo().First(); + + intItem.Value.ShouldBe(123); + }); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs new file mode 100644 index 000000000..4ee05d58b --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + public class PublicIntPropertyDto + { + public int Id { get; set; } + + + public int Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs new file mode 100644 index 000000000..f85538499 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicShortProperty + { + [Key] + public int Id { get; set; } + + + public short Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs index 87810a274..25aefd7e9 100644 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -15,6 +15,8 @@ public TestDbContext() public DbSet BoolItems { get; set; } + public DbSet ShortItems { get; set; } + public DbSet IntItems { get; set; } public DbSet StringItems { get; set; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 6225e91f1..82ed30915 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -25,9 +25,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) private static bool IsStringEqualsIgnoreCase(MethodCallExpression methodCall) { return methodCall.Method.IsStatic && - (methodCall.Arguments.Count == 3) && - (methodCall.Method.DeclaringType == typeof(string)) && - (methodCall.Method.Name == "Equals"); + (methodCall.Arguments.Count == 3) && + (methodCall.Method.DeclaringType == typeof(string)) && + (methodCall.Method.Name == "Equals"); } private static Expression GetStringToLowerEqualsComparison(MethodCallExpression methodCall) From 23fab82e8fce75646a0791ba5cd70cf4832a0d30 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 5 Nov 2017 17:48:34 +0000 Subject: [PATCH 016/176] Test coverage for projecting longs to ints --- .../AgileMapper.UnitTests.Ef6.csproj | 1 + AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs | 2 + .../WhenConvertingToInts.cs | 44 ++++++++++++++++++- .../TestClasses/PublicLongProperty.cs | 13 ++++++ AgileMapper.UnitTests.Ef6/TestDbContext.cs | 2 + .../Queryables/QueryProjectionModifier.cs | 25 +++++++++++ 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index 18a805bc9..6370c70e8 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -94,6 +94,7 @@ + diff --git a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs index d5821632f..bf50e1037 100644 --- a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs +++ b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs @@ -24,7 +24,9 @@ private void EmptyDbContext() { _context.Products.RemoveRange(_context.Products); _context.BoolItems.RemoveRange(_context.BoolItems); + _context.ShortItems.RemoveRange(_context.ShortItems); _context.IntItems.RemoveRange(_context.IntItems); + _context.LongItems.RemoveRange(_context.LongItems); _context.StringItems.RemoveRange(_context.StringItems); _context.SaveChanges(); } diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 6dbb4b867..0c3c8de2a 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -13,7 +13,7 @@ public WhenConvertingToInts(TestContext context) } [Fact] - public void ShouldMapAShortToAnInt() + public void ShouldProjectAShortToAnInt() { RunTest(context => { @@ -25,5 +25,47 @@ public void ShouldMapAShortToAnInt() intItem.Value.ShouldBe(123); }); } + + [Fact] + public void ShouldProjectAnInRangeLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = 12345L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(12345); + }); + } + + [Fact] + public void ShouldProjectATooBigLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = long.MaxValue }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } + + [Fact] + public void ShouldProjectATooSmallLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = int.MinValue - 1L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs b/AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs new file mode 100644 index 000000000..9ce8a49d0 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicLongProperty + { + [Key] + public int Id { get; set; } + + + public long Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs index 25aefd7e9..27ce7ea0b 100644 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ b/AgileMapper.UnitTests.Ef6/TestDbContext.cs @@ -19,6 +19,8 @@ public TestDbContext() public DbSet IntItems { get; set; } + public DbSet LongItems { get; set; } + public DbSet StringItems { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 82ed30915..743b7b353 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables { + using System; using System.Linq; using System.Linq.Expressions; using Extensions; @@ -22,6 +23,30 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return base.VisitMethodCall(methodCall); } + protected override Expression VisitDefault(DefaultExpression defaultExpression) + => GetDefaultValueFor(defaultExpression.Type).ToConstantExpression(defaultExpression.Type); + + private static object GetDefaultValueFor(Type type) + { + var getDefaultValueCaller = GlobalContext.Instance.Cache.GetOrAdd(type, t => + { + var getDefaultValueCall = Expression + .Call(typeof(QueryProjectionModifier) + .GetNonPublicStaticMethod("GetDefaultValue") + .MakeGenericMethod(t)) + .GetConversionTo(typeof(object)); + + var getDefaultValueLambda = Expression.Lambda>(getDefaultValueCall); + + return getDefaultValueLambda.Compile(); + }); + + return getDefaultValueCaller.Invoke(); + } + + // ReSharper disable once UnusedMember.Local + private static T GetDefaultValue() => default(T); + private static bool IsStringEqualsIgnoreCase(MethodCallExpression methodCall) { return methodCall.Method.IsStatic && From 11d70838e1342158253ac99e42dfed5c5497ebca Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 9 Nov 2017 14:22:47 +0000 Subject: [PATCH 017/176] Adding Entity Framework 5 test project / Moving ORM tests to shared project --- .../AgileMapper.UnitTests.Ef5.csproj | 127 ++++++++++++++++ AgileMapper.UnitTests.Ef5/App.config | 17 +++ .../Infrastructure/Ef5DbSetWrapper.cs | 28 ++++ .../Infrastructure/Ef5TestDbContext.cs | 51 +++++++ .../Infrastructure/TestContextCollection.cs | 11 ++ .../Properties/AssemblyInfo.cs | 7 + .../WhenConvertingToBools.cs | 14 ++ .../WhenConvertingToInts.cs | 14 ++ .../WhenProjectingFlatTypes.cs | 14 ++ AgileMapper.UnitTests.Ef5/packages.config | 15 ++ .../AgileMapper.UnitTests.Ef6.csproj | 21 +-- AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs | 34 ----- .../Infrastructure/Ef6DbSetWrapper.cs | 21 +++ .../Infrastructure/Ef6TestDbContext.cs | 51 +++++++ .../Infrastructure/TestContextCollection.cs | 11 ++ .../WhenConvertingToBools.cs | 135 +---------------- .../WhenConvertingToInts.cs | 65 +------- AgileMapper.UnitTests.Ef6/TestContext.cs | 19 --- .../TestContextCollection.cs | 10 -- AgileMapper.UnitTests.Ef6/TestDbContext.cs | 26 ---- .../WhenProjectingFlatTypes.cs | 41 +---- .../AgileMapper.UnitTests.Orms.csproj | 117 ++++++++++++++ .../Infrastructure/DbSetWrapperBase.cs | 32 ++++ .../Infrastructure/IDbSetWrapper.cs | 11 ++ .../Infrastructure/ITestDbContext.cs | 22 +++ .../Infrastructure/OrmTestClassBase.cs | 35 +++++ .../Infrastructure/TestContext.cs | 20 +++ .../Properties/AssemblyInfo.cs | 7 + .../WhenConvertingToBools.cs | 143 ++++++++++++++++++ .../WhenConvertingToInts.cs | 87 +++++++++++ .../TestClasses/Product.cs | 2 +- .../TestClasses/ProductDto.cs | 2 +- .../TestClasses/PublicBoolProperty.cs | 2 +- .../TestClasses/PublicBoolPropertyDto.cs | 2 +- .../TestClasses/PublicIntProperty.cs | 2 +- .../TestClasses/PublicIntPropertyDto.cs | 2 +- .../TestClasses/PublicLongProperty.cs | 2 +- .../TestClasses/PublicShortProperty.cs | 2 +- .../TestClasses/PublicStringProperty.cs | 2 +- AgileMapper.UnitTests.Orms/TestConstants.cs | 7 + .../WhenProjectingFlatTypes.cs | 49 ++++++ AgileMapper.UnitTests.Orms/packages.config | 11 ++ AgileMapper.sln | 14 +- .../Queryables/DefaultExpressionConverter.cs | 34 +++++ .../Queryables/QueryProjectionModifier.cs | 58 ++----- .../StringEqualsIgnoreCaseConverter.cs | 43 ++++++ .../Queryables/TryParseAssignmentConverter.cs | 60 ++++++++ 47 files changed, 1112 insertions(+), 388 deletions(-) create mode 100644 AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj create mode 100644 AgileMapper.UnitTests.Ef5/App.config create mode 100644 AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs create mode 100644 AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs create mode 100644 AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs create mode 100644 AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs create mode 100644 AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs create mode 100644 AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Ef5/packages.config delete mode 100644 AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs create mode 100644 AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs create mode 100644 AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs create mode 100644 AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs delete mode 100644 AgileMapper.UnitTests.Ef6/TestContext.cs delete mode 100644 AgileMapper.UnitTests.Ef6/TestContextCollection.cs delete mode 100644 AgileMapper.UnitTests.Ef6/TestDbContext.cs create mode 100644 AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs create mode 100644 AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/Product.cs (74%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/ProductDto.cs (66%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicBoolProperty.cs (74%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicBoolPropertyDto.cs (66%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicIntProperty.cs (74%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicIntPropertyDto.cs (66%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicLongProperty.cs (74%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicShortProperty.cs (75%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms}/TestClasses/PublicStringProperty.cs (75%) create mode 100644 AgileMapper.UnitTests.Orms/TestConstants.cs create mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms/packages.config create mode 100644 AgileMapper/Queryables/DefaultExpressionConverter.cs create mode 100644 AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs create mode 100644 AgileMapper/Queryables/TryParseAssignmentConverter.cs diff --git a/AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj b/AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj new file mode 100644 index 000000000..84465347c --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj @@ -0,0 +1,127 @@ + + + + + + + Debug + AnyCPU + {D87103FD-3851-4724-BD8F-9CEF19C8F193} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Ef5 + AgileObjects.AgileMapper.UnitTests.Ef5 + v4.6.2 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\Effort.1.3.0\lib\net45\Effort.dll + + + ..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll + + + ..\packages\NMemory.1.1.2\lib\net45\NMemory.dll + + + ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + + + + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + CommonAssemblyInfo.cs + + + VersionInfo.cs + + + + + + + + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/App.config b/AgileMapper.UnitTests.Ef5/App.config new file mode 100644 index 000000000..62fb4da2a --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/App.config @@ -0,0 +1,17 @@ + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs new file mode 100644 index 000000000..1d951dc22 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -0,0 +1,28 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +{ + using System.Data.Entity; + using System.Linq; + using Orms.Infrastructure; + + public class Ef5DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public Ef5DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() + { + foreach (var entity in _dbSet.ToArray()) + { + _dbSet.Remove(entity); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs new file mode 100644 index 000000000..bf2483cc6 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -0,0 +1,51 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +{ + using System.Data.Entity; + using Effort; + using Orms.Infrastructure; + using Orms.TestClasses; + + public class Ef5TestDbContext : DbContext, ITestDbContext + { + public Ef5TestDbContext() + : base(DbConnectionFactory.CreateTransient(), true) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + IDbSetWrapper ITestDbContext.Products + => new Ef5DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new Ef5DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new Ef5DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new Ef5DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new Ef5DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new Ef5DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs new file mode 100644 index 000000000..d95b9e3f1 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +{ + using Orms; + using Orms.Infrastructure; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class TestContextCollection : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..34dc22ba0 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Ef6")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Ef6")] + +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs new file mode 100644 index 000000000..97c12f650 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToBools : WhenConvertingToBools + { + public WhenConvertingToBools(TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs new file mode 100644 index 000000000..c7474283d --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToInts : WhenConvertingToInts + { + public WhenConvertingToInts(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs new file mode 100644 index 000000000..45482cb26 --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef5 +{ + using Infrastructure; + using Orms; + using Orms.Infrastructure; + + public class WhenProjectingFlatTypes : WhenProjectingFlatTypes + { + public WhenProjectingFlatTypes(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/packages.config b/AgileMapper.UnitTests.Ef5/packages.config new file mode 100644 index 000000000..e8a4493ec --- /dev/null +++ b/AgileMapper.UnitTests.Ef5/packages.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj index 6370c70e8..fbbea3d8e 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj @@ -87,25 +87,19 @@ VersionInfo.cs - + + - - - - - - - - - - - - + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} AgileMapper @@ -121,6 +115,7 @@ + diff --git a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs b/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs deleted file mode 100644 index bf50e1037..000000000 --- a/AgileMapper.UnitTests.Ef6/Ef6TestClassBase.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 -{ - using System; - using Xunit; - - [Collection("EF6 collection")] - public abstract class Ef6TestClassBase - { - private readonly TestDbContext _context; - - protected Ef6TestClassBase(TestContext context) - { - _context = context.DbContext; - } - - protected void RunTest(Action testAction) - { - testAction.Invoke(_context); - - EmptyDbContext(); - } - - private void EmptyDbContext() - { - _context.Products.RemoveRange(_context.Products); - _context.BoolItems.RemoveRange(_context.BoolItems); - _context.ShortItems.RemoveRange(_context.ShortItems); - _context.IntItems.RemoveRange(_context.IntItems); - _context.LongItems.RemoveRange(_context.LongItems); - _context.StringItems.RemoveRange(_context.StringItems); - _context.SaveChanges(); - } - } -} diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs new file mode 100644 index 000000000..faf3633c3 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +{ + using System.Data.Entity; + using Orms.Infrastructure; + + public class Ef6DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public Ef6DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() => _dbSet.RemoveRange(_dbSet); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs new file mode 100644 index 000000000..9214099b3 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -0,0 +1,51 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +{ + using System.Data.Entity; + using Effort; + using Orms.Infrastructure; + using Orms.TestClasses; + + public class Ef6TestDbContext : DbContext, ITestDbContext + { + public Ef6TestDbContext() + : base(DbConnectionFactory.CreateTransient(), true) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + IDbSetWrapper ITestDbContext.Products + => new Ef6DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new Ef6DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new Ef6DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new Ef6DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new Ef6DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new Ef6DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs new file mode 100644 index 000000000..4ace795a2 --- /dev/null +++ b/AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +{ + using Orms; + using Orms.Infrastructure; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class TestContextCollection : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs index 6bc8511e0..438c0dfd6 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,141 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion { - using System.Linq; - using Shouldly; - using TestClasses; - using Xunit; + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; - public class WhenConvertingToBools : Ef6TestClassBase + public class WhenConvertingToBools : WhenConvertingToBools { public WhenConvertingToBools(TestContext context) : base(context) { } - - [Fact] - public void ShouldProjectAnIntOneToTrue() - { - RunTest(context => - { - context.IntItems.Add(new PublicIntProperty { Value = 1 }); - context.SaveChanges(); - - var boolItem = context.IntItems.ProjectTo().First(); - - boolItem.Value.ShouldBeTrue(); - }); - } - - [Fact] - public void ShouldProjectAnIntZeroToFalse() - { - RunTest(context => - { - context.IntItems.Add(new PublicIntProperty { Value = 0 }); - context.SaveChanges(); - - var boolItem = context.IntItems.ProjectTo().First(); - - boolItem.Value.ShouldBeFalse(); - }); - } - - [Fact] - public void ShouldProjectAStringTrueToTrue() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "true" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeTrue(); - }); - } - - [Fact] - public void ShouldProjectAStringTrueToTrueIgnoringCase() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "tRuE" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeTrue(); - }); - } - - [Fact] - public void ShouldProjectAStringOneToTrue() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "1" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeTrue(); - }); - } - - [Fact] - public void ShouldProjectAStringFalseToFalse() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "false" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeFalse(); - }); - } - - [Fact] - public void ShouldProjectAStringZeroToFalse() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "0" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeFalse(); - }); - } - - [Fact] - public void ShouldProjectAStringNonBooleanValueToFalse() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = "uokyujhygt" }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeFalse(); - }); - } - - [Fact] - public void ShouldProjectAStringNullToFalse() - { - RunTest(context => - { - context.StringItems.Add(new PublicStringProperty { Value = null }); - context.SaveChanges(); - - var boolItem = context.StringItems.ProjectTo().First(); - - boolItem.Value.ShouldBeFalse(); - }); - } } } diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 0c3c8de2a..c853ee4b4 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,71 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion { - using System.Linq; - using Shouldly; - using TestClasses; - using Xunit; + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; - public class WhenConvertingToInts : Ef6TestClassBase + public class WhenConvertingToInts : WhenConvertingToInts { public WhenConvertingToInts(TestContext context) : base(context) { } - - [Fact] - public void ShouldProjectAShortToAnInt() - { - RunTest(context => - { - context.ShortItems.Add(new PublicShortProperty { Value = 123 }); - context.SaveChanges(); - - var intItem = context.ShortItems.ProjectTo().First(); - - intItem.Value.ShouldBe(123); - }); - } - - [Fact] - public void ShouldProjectAnInRangeLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLongProperty { Value = 12345L }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(12345); - }); - } - - [Fact] - public void ShouldProjectATooBigLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLongProperty { Value = long.MaxValue }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(0); - }); - } - - [Fact] - public void ShouldProjectATooSmallLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLongProperty { Value = int.MinValue - 1L }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(0); - }); - } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestContext.cs b/AgileMapper.UnitTests.Ef6/TestContext.cs deleted file mode 100644 index 03188243d..000000000 --- a/AgileMapper.UnitTests.Ef6/TestContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 -{ - using System; - - public class TestContext : IDisposable - { - public TestContext() - { - DbContext = new TestDbContext(); - } - - public TestDbContext DbContext { get; } - - public void Dispose() - { - DbContext?.Dispose(); - } - } -} diff --git a/AgileMapper.UnitTests.Ef6/TestContextCollection.cs b/AgileMapper.UnitTests.Ef6/TestContextCollection.cs deleted file mode 100644 index 93f8fe4ca..000000000 --- a/AgileMapper.UnitTests.Ef6/TestContextCollection.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 -{ - using Xunit; - - [CollectionDefinition(Name)] - public class TestContextCollection : ICollectionFixture - { - public const string Name = "EF6 collection"; - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestDbContext.cs b/AgileMapper.UnitTests.Ef6/TestDbContext.cs deleted file mode 100644 index 27ce7ea0b..000000000 --- a/AgileMapper.UnitTests.Ef6/TestDbContext.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 -{ - using System.Data.Entity; - using Effort; - using TestClasses; - - public class TestDbContext : DbContext - { - public TestDbContext() - : base(DbConnectionFactory.CreateTransient(), true) - { - } - - public DbSet Products { get; set; } - - public DbSet BoolItems { get; set; } - - public DbSet ShortItems { get; set; } - - public DbSet IntItems { get; set; } - - public DbSet LongItems { get; set; } - - public DbSet StringItems { get; set; } - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs index 72b0b49b4..193ec80f3 100644 --- a/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs @@ -1,47 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Ef6 { - using System.Linq; - using Shouldly; - using TestClasses; - using Xunit; + using Infrastructure; + using Orms; + using Orms.Infrastructure; - public class WhenProjectingFlatTypes : Ef6TestClassBase + public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { public WhenProjectingFlatTypes(TestContext context) : base(context) { } - - [Fact] - public void ShouldProjectAFlatTypeToAnArray() - { - RunTest(context => - { - context.Products.Add(new Product - { - ProductId = 1, - Name = "Product One" - }); - - context.Products.Add(new Product - { - ProductId = 2, - Name = "Product Two" - }); - - context.SaveChanges(); - - var products = context.Products.ToArray(); - var productDtos = context.Products.ProjectTo().ToArray(); - - productDtos.Length.ShouldBe(2); - - productDtos[0].ProductId.ShouldBe(products[0].ProductId); - productDtos[0].Name.ShouldBe(products[0].Name); - - productDtos[1].ProductId.ShouldBe(products[1].ProductId); - productDtos[1].Name.ShouldBe(products[1].Name); - }); - } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj new file mode 100644 index 000000000..7393f8499 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -0,0 +1,117 @@ + + + + + + Debug + AnyCPU + {66522D44-19F5-4AF5-9D43-483A3CD6F958} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Orms + AgileObjects.AgileMapper.UnitTests.Orms + v4.6.2 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + CommonAssemblyInfo.cs + + + VersionInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs new file mode 100644 index 000000000..28ce6ba5e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs @@ -0,0 +1,32 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + + public abstract class DbSetWrapperBase : IDbSetWrapper + { + private readonly IQueryable _dbSet; + + protected DbSetWrapperBase(IQueryable dbSet) + { + _dbSet = dbSet; + } + + public IEnumerator GetEnumerator() => _dbSet.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public Expression Expression => _dbSet.Expression; + + public Type ElementType => _dbSet.ElementType; + + public IQueryProvider Provider => _dbSet.Provider; + + public abstract void Add(TEntity itemToAdd); + + public abstract void Clear(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs new file mode 100644 index 000000000..d082682ad --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System.Linq; + + public interface IDbSetWrapper : IQueryable + { + void Add(TEntity itemToAdd); + + void Clear(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs new file mode 100644 index 000000000..12cda8fd1 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -0,0 +1,22 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System; + using TestClasses; + + public interface ITestDbContext : IDisposable + { + IDbSetWrapper Products { get; } + + IDbSetWrapper BoolItems { get; } + + IDbSetWrapper ShortItems { get; } + + IDbSetWrapper IntItems { get; } + + IDbSetWrapper LongItems { get; } + + IDbSetWrapper StringItems { get; } + + void SaveChanges(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs new file mode 100644 index 000000000..41967e533 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -0,0 +1,35 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System; + using Xunit; + + [Collection(TestConstants.OrmCollectionName)] + public abstract class OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + private readonly TOrmContext _context; + + protected OrmTestClassBase(TestContext context) + { + _context = context.GetDbContext(); + } + + protected void RunTest(Action testAction) + { + testAction.Invoke(_context); + + EmptyDbContext(); + } + + private void EmptyDbContext() + { + _context.Products.Clear(); + _context.BoolItems.Clear(); + _context.ShortItems.Clear(); + _context.IntItems.Clear(); + _context.LongItems.Clear(); + _context.StringItems.Clear(); + _context.SaveChanges(); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs new file mode 100644 index 000000000..9623ddb86 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System; + + public class TestContext : IDisposable + { + private IDisposable _dbContext; + + public TOrmContext GetDbContext() + where TOrmContext : ITestDbContext, new() + { + return (TOrmContext)(_dbContext ?? (_dbContext = new TOrmContext())); + } + + public void Dispose() + { + _dbContext?.Dispose(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a5324d950 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Orms")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Orms")] + +[assembly: ComVisible(false)] diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs new file mode 100644 index 000000000..2feae78a7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -0,0 +1,143 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToBools : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToBools(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAnIntOneToTrue() + { + RunTest(context => + { + context.IntItems.Add(new PublicIntProperty { Value = 1 }); + context.SaveChanges(); + + var boolItem = context.IntItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAnIntZeroToFalse() + { + RunTest(context => + { + context.IntItems.Add(new PublicIntProperty { Value = 0 }); + context.SaveChanges(); + + var boolItem = context.IntItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringTrueToTrue() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "true" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringTrueToTrueIgnoringCase() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "tRuE" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringOneToTrue() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "1" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeTrue(); + }); + } + + [Fact] + public void ShouldProjectAStringFalseToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "false" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringZeroToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "0" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringNonBooleanValueToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "uokyujhygt" }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + + [Fact] + public void ShouldProjectAStringNullToFalse() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = null }); + context.SaveChanges(); + + var boolItem = context.StringItems.ProjectTo().First(); + + boolItem.Value.ShouldBeFalse(); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs new file mode 100644 index 000000000..3368b1814 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -0,0 +1,87 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToInts : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToInts(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAShortToAnInt() + { + RunTest(context => + { + context.ShortItems.Add(new PublicShortProperty { Value = 123 }); + context.SaveChanges(); + + var intItem = context.ShortItems.ProjectTo().First(); + + intItem.Value.ShouldBe(123); + }); + } + + [Fact] + public void ShouldProjectAnInRangeLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = 12345L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(12345); + }); + } + + [Fact] + public void ShouldProjectATooBigLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = long.MaxValue }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } + + [Fact] + public void ShouldProjectATooSmallLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLongProperty { Value = int.MinValue - 1L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } + + [Fact] + public void ShouldProjectAParsableStringToAnInt() + { + RunTest(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "738" }); + context.SaveChanges(); + + var intItem = context.StringItems.ProjectTo().First(); + + intItem.Value.ShouldBe(738); + }); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs b/AgileMapper.UnitTests.Orms/TestClasses/Product.cs similarity index 74% rename from AgileMapper.UnitTests.Ef6/TestClasses/Product.cs rename to AgileMapper.UnitTests.Orms/TestClasses/Product.cs index ac8349419..fad662e16 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/Product.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Product.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/ProductDto.cs similarity index 66% rename from AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/ProductDto.cs index e1b06611b..613562db3 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/ProductDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/ProductDto.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { public class ProductDto { diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs similarity index 74% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs index 430d8384b..46a780075 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs similarity index 66% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs index dbd4e9aba..b4e18a515 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicBoolPropertyDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { public class PublicBoolPropertyDto { diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs similarity index 74% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs index 43188771e..c0fa59e64 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs similarity index 66% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs index 4ee05d58b..18b03e6ec 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicIntPropertyDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { public class PublicIntPropertyDto { diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs similarity index 74% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs index 9ce8a49d0..6f16f1ec6 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicLongProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs similarity index 75% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs index f85538499..73723ad9f 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicShortProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs similarity index 75% rename from AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs index fdb91b4d9..0d4125bcc 100644 --- a/AgileMapper.UnitTests.Ef6/TestClasses/PublicStringProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.TestClasses +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { using System.ComponentModel.DataAnnotations; diff --git a/AgileMapper.UnitTests.Orms/TestConstants.cs b/AgileMapper.UnitTests.Orms/TestConstants.cs new file mode 100644 index 000000000..d452f1a93 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestConstants.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + public static class TestConstants + { + public const string OrmCollectionName = "ORM Collection"; + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs new file mode 100644 index 000000000..03dfe0acc --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -0,0 +1,49 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenProjectingFlatTypes : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingFlatTypes(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAFlatTypeToAnArray() + { + RunTest(context => + { + context.Products.Add(new Product + { + ProductId = 1, + Name = "Product One" + }); + + context.Products.Add(new Product + { + ProductId = 2, + Name = "Product Two" + }); + + context.SaveChanges(); + + var products = context.Products.ToArray(); + var productDtos = context.Products.ProjectTo().ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos[0].ProductId.ShouldBe(products[0].ProductId); + productDtos[0].Name.ShouldBe(products[0].Name); + + productDtos[1].ProductId.ShouldBe(products[1].ProductId); + productDtos[1].Name.ShouldBe(products[1].Name); + }); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/packages.config b/AgileMapper.UnitTests.Orms/packages.config new file mode 100644 index 000000000..969ebba40 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/packages.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.sln b/AgileMapper.sln index dc051c1a6..edac5fa42 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2006 +VisualStudioVersion = 15.0.27004.2008 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05AB6D17-6066-41D5-8E79-31C342DFC2DC}" ProjectSection(SolutionItems) = preProject @@ -24,6 +24,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.MoreT EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Ef6", "AgileMapper.UnitTests.Ef6\AgileMapper.UnitTests.Ef6.csproj", "{63B8975D-0CDE-48F5-8CA9-8AF8FE729610}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms", "AgileMapper.UnitTests.Orms\AgileMapper.UnitTests.Orms.csproj", "{66522D44-19F5-4AF5-9D43-483A3CD6F958}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Ef5", "AgileMapper.UnitTests.Ef5\AgileMapper.UnitTests.Ef5.csproj", "{D87103FD-3851-4724-BD8F-9CEF19C8F193}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,6 +58,14 @@ Global {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Debug|Any CPU.Build.0 = Debug|Any CPU {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Release|Any CPU.ActiveCfg = Release|Any CPU {63B8975D-0CDE-48F5-8CA9-8AF8FE729610}.Release|Any CPU.Build.0 = Release|Any CPU + {66522D44-19F5-4AF5-9D43-483A3CD6F958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66522D44-19F5-4AF5-9D43-483A3CD6F958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66522D44-19F5-4AF5-9D43-483A3CD6F958}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66522D44-19F5-4AF5-9D43-483A3CD6F958}.Release|Any CPU.Build.0 = Release|Any CPU + {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AgileMapper/Queryables/DefaultExpressionConverter.cs b/AgileMapper/Queryables/DefaultExpressionConverter.cs new file mode 100644 index 000000000..6749b6e67 --- /dev/null +++ b/AgileMapper/Queryables/DefaultExpressionConverter.cs @@ -0,0 +1,34 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System; + using System.Linq.Expressions; + using Extensions; + using NetStandardPolyfills; + + internal static class DefaultExpressionConverter + { + public static Expression Convert(DefaultExpression defaultExpression) + => GetDefaultValueFor(defaultExpression.Type).ToConstantExpression(defaultExpression.Type); + + private static object GetDefaultValueFor(Type type) + { + var getDefaultValueCaller = GlobalContext.Instance.Cache.GetOrAdd(type, t => + { + var getDefaultValueCall = Expression + .Call(typeof(DefaultExpressionConverter) + .GetNonPublicStaticMethod("GetDefaultValue") + .MakeGenericMethod(t)) + .GetConversionTo(typeof(object)); + + var getDefaultValueLambda = Expression.Lambda>(getDefaultValueCall); + + return getDefaultValueLambda.Compile(); + }); + + return getDefaultValueCaller.Invoke(); + } + + // ReSharper disable once UnusedMember.Local + private static T GetDefaultValue() => default(T); + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 743b7b353..abe5b694d 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,10 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables { - using System; - using System.Linq; using System.Linq.Expressions; - using Extensions; - using NetStandardPolyfills; internal class QueryProjectionModifier : ExpressionVisitor { @@ -13,59 +9,27 @@ public Expression Modify(Expression queryProjection) return VisitAndConvert(queryProjection, "Modify"); } - protected override Expression VisitMethodCall(MethodCallExpression methodCall) + protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { - if (IsStringEqualsIgnoreCase(methodCall)) + if (TryParseAssignmentConverter.TryConvert(assignment, out var converted)) { - return GetStringToLowerEqualsComparison(methodCall); + return converted; } - return base.VisitMethodCall(methodCall); + return base.VisitMemberAssignment(assignment); } - protected override Expression VisitDefault(DefaultExpression defaultExpression) - => GetDefaultValueFor(defaultExpression.Type).ToConstantExpression(defaultExpression.Type); - - private static object GetDefaultValueFor(Type type) + protected override Expression VisitMethodCall(MethodCallExpression methodCall) { - var getDefaultValueCaller = GlobalContext.Instance.Cache.GetOrAdd(type, t => + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, out var converted)) { - var getDefaultValueCall = Expression - .Call(typeof(QueryProjectionModifier) - .GetNonPublicStaticMethod("GetDefaultValue") - .MakeGenericMethod(t)) - .GetConversionTo(typeof(object)); - - var getDefaultValueLambda = Expression.Lambda>(getDefaultValueCall); - - return getDefaultValueLambda.Compile(); - }); - - return getDefaultValueCaller.Invoke(); - } - - // ReSharper disable once UnusedMember.Local - private static T GetDefaultValue() => default(T); + return converted; + } - private static bool IsStringEqualsIgnoreCase(MethodCallExpression methodCall) - { - return methodCall.Method.IsStatic && - (methodCall.Arguments.Count == 3) && - (methodCall.Method.DeclaringType == typeof(string)) && - (methodCall.Method.Name == "Equals"); + return base.VisitMethodCall(methodCall); } - private static Expression GetStringToLowerEqualsComparison(MethodCallExpression methodCall) - { - var subjectToLower = Expression.Call( - methodCall.Arguments[0], - typeof(string) - .GetPublicInstanceMethods() - .First(m => (m.Name == "ToLower") && (m.GetParameters().None()))); - - var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); - - return comparison; - } + protected override Expression VisitDefault(DefaultExpression defaultExpression) + => DefaultExpressionConverter.Convert(defaultExpression); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs new file mode 100644 index 000000000..29f8fc1ff --- /dev/null +++ b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq; + using System.Linq.Expressions; + using Extensions; + using NetStandardPolyfills; + + internal static class StringEqualsIgnoreCaseConverter + { + public static bool TryConvert(MethodCallExpression methodCall, out Expression converted) + { + if (IsEqualsIgnoreCaseCall(methodCall)) + { + converted = Convert(methodCall); + return true; + } + + converted = null; + return false; + } + + private static bool IsEqualsIgnoreCaseCall(MethodCallExpression methodCall) + { + return methodCall.Method.IsStatic && + (methodCall.Arguments.Count == 3) && + (methodCall.Method.DeclaringType == typeof(string)) && + (methodCall.Method.Name == "Equals"); + } + + private static Expression Convert(MethodCallExpression methodCall) + { + var subjectToLower = Expression.Call( + methodCall.Arguments[0], + typeof(string) + .GetPublicInstanceMethods() + .First(m => (m.Name == "ToLower") && (m.GetParameters().None()))); + + var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); + + return comparison; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs new file mode 100644 index 000000000..7aed4f2a1 --- /dev/null +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -0,0 +1,60 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using Extensions; + using NetStandardPolyfills; + + internal static class TryParseAssignmentConverter + { + public static bool TryConvert(MemberAssignment assignment, out MemberAssignment converted) + { + if (assignment.Expression.NodeType != ExpressionType.Block) + { + converted = null; + return false; + } + + var valueBlock = (BlockExpression)assignment.Expression; + var finalExpression = valueBlock.Expressions.Last(); + + if (finalExpression.NodeType != ExpressionType.Conditional) + { + converted = null; + return false; + } + + var tryParseOrDefault = (ConditionalExpression)finalExpression; + + if (tryParseOrDefault.Test.NodeType != ExpressionType.Call) + { + converted = null; + return false; + } + + var methodCall = (MethodCallExpression)tryParseOrDefault.Test; + + if (methodCall.Method.IsStatic && (methodCall.Method.Name == "TryParse")) + { + converted = assignment.Update(GetConvertCall(methodCall)); + return true; + } + + converted = null; + return false; + } + + private static MethodCallExpression GetConvertCall(MethodCallExpression tryParseCall) + { + var parseMethod = tryParseCall + .Method + .DeclaringType + .GetPublicStaticMethod("Parse"); + + var parseCall = Expression.Call(parseMethod, tryParseCall.Arguments.First()); + + return parseCall; + } + } +} \ No newline at end of file From 6ccd6b5acbea2105d35ffd04c94d9e6784a13d19 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 10 Nov 2017 09:00:38 +0000 Subject: [PATCH 018/176] Removing quoted lambda comments from mapping plans --- AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs | 2 +- AgileMapper/Plans/RootMapperMappingPlanFunction.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs b/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs index 1b25e0c8c..fabe317d9 100644 --- a/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs @@ -29,7 +29,7 @@ public string GetDescription() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{_mappingLambda.ToReadableString()}".TrimStart(); +{_mappingLambda.ToReadableString(_ => _.NoQuotedLambdaComments)}".TrimStart(); } } } \ No newline at end of file diff --git a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs index 0e0ecc277..9e62aca46 100644 --- a/AgileMapper/Plans/RootMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RootMapperMappingPlanFunction.cs @@ -31,7 +31,7 @@ public string GetDescription() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{lambda.ToReadableString()}".TrimStart(); +{lambda.ToReadableString(_ => _.NoQuotedLambdaComments)}".TrimStart(); } private Expression GetFinalMappingLambda() From 984f1df9b5f7cdfab053c3b14a0ec8184929a35c Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 10 Nov 2017 10:35:35 +0000 Subject: [PATCH 019/176] Giving up on attempting to get a working string -> int conversion for code-first EF5 and EF6 --- .../Infrastructure/OrmTestClassBase.cs | 10 ++++++++++ .../SimpleTypeConversion/WhenConvertingToInts.cs | 4 ++-- .../Queryables/TryParseAssignmentConverter.cs | 15 +++++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 41967e533..80c28cbd5 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; + using Shouldly; using Xunit; [Collection(TestConstants.OrmCollectionName)] @@ -14,6 +15,15 @@ protected OrmTestClassBase(TestContext context) _context = context.GetDbContext(); } + protected Exception RunTestAndExpectThrow(Action testAction) + => RunTestAndExpectThrow(testAction); + + protected TException RunTestAndExpectThrow(Action testAction) + where TException : Exception + { + return Should.Throw(() => RunTest(testAction)); + } + protected void RunTest(Action testAction) { testAction.Invoke(_context); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 3368b1814..f656a86f2 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -71,9 +71,9 @@ public void ShouldProjectATooSmallLongToAnInt() } [Fact] - public void ShouldProjectAParsableStringToAnInt() + public void ShouldErrorIfProjectingStringToAnInt() { - RunTest(context => + RunTestAndExpectThrow(context => { context.StringItems.Add(new PublicStringProperty { Value = "738" }); context.SaveChanges(); diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index 7aed4f2a1..0daa96bff 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -47,14 +47,17 @@ public static bool TryConvert(MemberAssignment assignment, out MemberAssignment private static MethodCallExpression GetConvertCall(MethodCallExpression tryParseCall) { - var parseMethod = tryParseCall - .Method - .DeclaringType - .GetPublicStaticMethod("Parse"); + // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, + // but it at least gives a decent error message: + var convertMethodName = "To" + tryParseCall.Method.DeclaringType.Name; - var parseCall = Expression.Call(parseMethod, tryParseCall.Arguments.First()); + var convertMethod = typeof(Convert) + .GetPublicStaticMethods(convertMethodName) + .First(m => m.GetParameters().HasOne() && (m.GetParameters()[0].ParameterType == typeof(string))); - return parseCall; + var convertCall = Expression.Call(convertMethod, tryParseCall.Arguments.First()); + + return convertCall; } } } \ No newline at end of file From 56016104d8e207b022b3993650b7534450b37112 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 10 Nov 2017 11:12:34 +0000 Subject: [PATCH 020/176] Adding EFCore2 tests project / Moving test projects into 'Orms' namespace --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 4 +- .../App.config | 0 .../Infrastructure/Ef5DbSetWrapper.cs | 2 +- .../Infrastructure/Ef5TestDbContext.cs | 4 +- .../Infrastructure/TestContextCollection.cs | 2 +- .../Properties/AssemblyInfo.cs | 0 .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenProjectingFlatTypes.cs | 2 +- .../packages.config | 0 .../AgileMapper.UnitTests.Orms.Ef6.csproj | 4 +- .../App.config | 0 .../Infrastructure/Ef6DbSetWrapper.cs | 2 +- .../Infrastructure/Ef6TestDbContext.cs | 4 +- .../Infrastructure/TestContextCollection.cs | 11 ++ .../Properties/AssemblyInfo.cs | 0 .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenProjectingFlatTypes.cs | 2 +- .../packages.config | 0 .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 183 ++++++++++++++++++ .../Infrastructure/EfCore2DbSetWrapper.cs | 21 ++ .../Infrastructure/EfCore2TestDbContext.cs | 55 ++++++ .../Infrastructure/TestContextCollection.cs | 11 ++ .../Properties/AssemblyInfo.cs | 36 ++++ .../WhenProjectingFlatTypes.cs | 14 ++ .../packages.config | 38 ++++ AgileMapper.sln | 10 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 120 ++++++++++++ .../Infrastructure/EfCore2DbSetWrapper.cs | 21 ++ .../Infrastructure/EfCore2TestDbContext.cs | 55 ++++++ .../Infrastructure/TestContextCollection.cs | 2 +- .../WhenProjectingFlatTypes.cs | 14 ++ .../packages.config | 12 ++ 34 files changed, 617 insertions(+), 20 deletions(-) rename AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj => AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj (97%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/App.config (100%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/Infrastructure/Ef5DbSetWrapper.cs (89%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/Infrastructure/Ef5TestDbContext.cs (94%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef5}/Infrastructure/TestContextCollection.cs (74%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/Properties/AssemblyInfo.cs (100%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/SimpleTypeConversion/WhenConvertingToBools.cs (79%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/SimpleTypeConversion/WhenConvertingToInts.cs (79%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/WhenProjectingFlatTypes.cs (83%) rename {AgileMapper.UnitTests.Ef5 => AgileMapper.UnitTests.Orms.Ef5}/packages.config (100%) rename AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj => AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj (97%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/App.config (100%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/Infrastructure/Ef6DbSetWrapper.cs (87%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/Infrastructure/Ef6TestDbContext.cs (94%) create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/Properties/AssemblyInfo.cs (100%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/SimpleTypeConversion/WhenConvertingToBools.cs (79%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/SimpleTypeConversion/WhenConvertingToInts.cs (79%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/WhenProjectingFlatTypes.cs (83%) rename {AgileMapper.UnitTests.Ef6 => AgileMapper.UnitTests.Orms.Ef6}/packages.config (100%) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/packages.config create mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj create mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs create mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs rename {AgileMapper.UnitTests.Ef5 => _AgileMapper.UnitTests.Orms.EfCore2}/Infrastructure/TestContextCollection.cs (75%) create mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs create mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/packages.config diff --git a/AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj similarity index 97% rename from AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj rename to AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 84465347c..27c430ec9 100644 --- a/AgileMapper.UnitTests.Ef5/AgileMapper.UnitTests.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -9,8 +9,8 @@ {D87103FD-3851-4724-BD8F-9CEF19C8F193} Library Properties - AgileObjects.AgileMapper.UnitTests.Ef5 - AgileObjects.AgileMapper.UnitTests.Ef5 + AgileObjects.AgileMapper.UnitTests.Orms.Ef5 + AgileObjects.AgileMapper.UnitTests.Orms.Ef5 v4.6.2 512 diff --git a/AgileMapper.UnitTests.Ef5/App.config b/AgileMapper.UnitTests.Orms.Ef5/App.config similarity index 100% rename from AgileMapper.UnitTests.Ef5/App.config rename to AgileMapper.UnitTests.Orms.Ef5/App.config diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs similarity index 89% rename from AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs rename to AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs index 1d951dc22..b8aa288fe 100644 --- a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { using System.Data.Entity; using System.Linq; diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs similarity index 94% rename from AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs rename to AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index bf2483cc6..550c587c2 100644 --- a/AgileMapper.UnitTests.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -1,9 +1,9 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { using System.Data.Entity; using Effort; using Orms.Infrastructure; - using Orms.TestClasses; + using TestClasses; public class Ef5TestDbContext : DbContext, ITestDbContext { diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs similarity index 74% rename from AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs rename to AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs index 4ace795a2..13c276025 100644 --- a/AgileMapper.UnitTests.Ef6/Infrastructure/TestContextCollection.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { using Orms; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.Ef5/Properties/AssemblyInfo.cs similarity index 100% rename from AgileMapper.UnitTests.Ef5/Properties/AssemblyInfo.cs rename to AgileMapper.UnitTests.Orms.Ef5/Properties/AssemblyInfo.cs diff --git a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs similarity index 79% rename from AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs rename to AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs index 97c12f650..c941a3bca 100644 --- a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs similarity index 79% rename from AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs rename to AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index c7474283d..d55e749ab 100644 --- a/AgileMapper.UnitTests.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs similarity index 83% rename from AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs rename to AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs index 45482cb26..9c0311569 100644 --- a/AgileMapper.UnitTests.Ef5/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { using Infrastructure; using Orms; diff --git a/AgileMapper.UnitTests.Ef5/packages.config b/AgileMapper.UnitTests.Orms.Ef5/packages.config similarity index 100% rename from AgileMapper.UnitTests.Ef5/packages.config rename to AgileMapper.UnitTests.Orms.Ef5/packages.config diff --git a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj similarity index 97% rename from AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj rename to AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index fbbea3d8e..57bb3b570 100644 --- a/AgileMapper.UnitTests.Ef6/AgileMapper.UnitTests.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -9,8 +9,8 @@ {63B8975D-0CDE-48F5-8CA9-8AF8FE729610} Library Properties - AgileObjects.AgileMapper.UnitTests.Ef6 - AgileObjects.AgileMapper.UnitTests.Ef6 + AgileObjects.AgileMapper.UnitTests.Orms.Ef6 + AgileObjects.AgileMapper.UnitTests.Orms.Ef6 v4.6.2 512 diff --git a/AgileMapper.UnitTests.Ef6/App.config b/AgileMapper.UnitTests.Orms.Ef6/App.config similarity index 100% rename from AgileMapper.UnitTests.Ef6/App.config rename to AgileMapper.UnitTests.Orms.Ef6/App.config diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs similarity index 87% rename from AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs rename to AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs index faf3633c3..e89c5f946 100644 --- a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { using System.Data.Entity; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs similarity index 94% rename from AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs rename to AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 9214099b3..5ffaa12a3 100644 --- a/AgileMapper.UnitTests.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -1,9 +1,9 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { using System.Data.Entity; using Effort; using Orms.Infrastructure; - using Orms.TestClasses; + using TestClasses; public class Ef6TestDbContext : DbContext, ITestDbContext { diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs new file mode 100644 index 000000000..7be9557cf --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure +{ + using Orms; + using Orms.Infrastructure; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class TestContextCollection : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.Ef6/Properties/AssemblyInfo.cs similarity index 100% rename from AgileMapper.UnitTests.Ef6/Properties/AssemblyInfo.cs rename to AgileMapper.UnitTests.Orms.Ef6/Properties/AssemblyInfo.cs diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs similarity index 79% rename from AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs rename to AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs index 438c0dfd6..cfc43404b 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs similarity index 79% rename from AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs rename to AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index c853ee4b4..dbdd7427c 100644 --- a/AgileMapper.UnitTests.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; using Orms.Infrastructure; diff --git a/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs similarity index 83% rename from AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs rename to AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs index 193ec80f3..90e935068 100644 --- a/AgileMapper.UnitTests.Ef6/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef6 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { using Infrastructure; using Orms; diff --git a/AgileMapper.UnitTests.Ef6/packages.config b/AgileMapper.UnitTests.Orms.Ef6/packages.config similarity index 100% rename from AgileMapper.UnitTests.Ef6/packages.config rename to AgileMapper.UnitTests.Orms.Ef6/packages.config diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj new file mode 100644 index 000000000..05c280944 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -0,0 +1,183 @@ + + + + + + Debug + AnyCPU + {2E3DF5C2-8A38-4A03-86D7-8D463C917E47} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 + v4.6.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + + + ..\packages\Microsoft.EntityFrameworkCore.InMemory.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.InMemory.dll + + + ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll + + + ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + + + ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + + + + ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll + + + + + ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll + + + ..\packages\System.Reflection.4.1.0\lib\net462\System.Reflection.dll + True + + + ..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll + True + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Runtime.Extensions.4.1.0\lib\net462\System.Runtime.Extensions.dll + True + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + True + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + + + + + + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + + + + + False + + + False + + + False + + + False + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs new file mode 100644 index 000000000..fceb2c023 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure +{ + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + + public class EfCore2DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public EfCore2DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() => _dbSet.RemoveRange(_dbSet); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs new file mode 100644 index 000000000..f3a4cc65c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -0,0 +1,55 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure +{ + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + + public class EfCore2TestDbContext : DbContext, ITestDbContext + { + private static readonly DbContextOptions _inMemoryOptions = + new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "Ef6TestDbContext") + .Options; + + public EfCore2TestDbContext() + : base(_inMemoryOptions) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + IDbSetWrapper ITestDbContext.Products + => new EfCore2DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new EfCore2DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new EfCore2DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new EfCore2DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new EfCore2DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new EfCore2DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs new file mode 100644 index 000000000..791f97c28 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure +{ + using Orms; + using Orms.Infrastructure; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class TestContextCollection : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..4c2df25a4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AgileMapper.UnitTests.Orms.EfCore2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AgileMapper.UnitTests.Orms.EfCore2")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs new file mode 100644 index 000000000..37c879b69 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + using Orms; + using Orms.Infrastructure; + + public class WhenProjectingFlatTypes : WhenProjectingFlatTypes + { + public WhenProjectingFlatTypes(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config new file mode 100644 index 000000000..059cafd34 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.sln b/AgileMapper.sln index edac5fa42..4d6f2f82a 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -22,11 +22,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper", "AgileMapper\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.MoreTestClasses", "AgileMapper.UnitTests.MoreTestClasses\AgileMapper.UnitTests.MoreTestClasses.csproj", "{049E1EE5-48CE-441A-B166-3CF6BEC17957}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Ef6", "AgileMapper.UnitTests.Ef6\AgileMapper.UnitTests.Ef6.csproj", "{63B8975D-0CDE-48F5-8CA9-8AF8FE729610}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.Ef6", "AgileMapper.UnitTests.Orms.Ef6\AgileMapper.UnitTests.Orms.Ef6.csproj", "{63B8975D-0CDE-48F5-8CA9-8AF8FE729610}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms", "AgileMapper.UnitTests.Orms\AgileMapper.UnitTests.Orms.csproj", "{66522D44-19F5-4AF5-9D43-483A3CD6F958}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Ef5", "AgileMapper.UnitTests.Ef5\AgileMapper.UnitTests.Ef5.csproj", "{D87103FD-3851-4724-BD8F-9CEF19C8F193}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.Ef5", "AgileMapper.UnitTests.Orms.Ef5\AgileMapper.UnitTests.Orms.Ef5.csproj", "{D87103FD-3851-4724-BD8F-9CEF19C8F193}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore2", "AgileMapper.UnitTests.Orms.EfCore2\AgileMapper.UnitTests.Orms.EfCore2.csproj", "{2E3DF5C2-8A38-4A03-86D7-8D463C917E47}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -66,6 +68,10 @@ Global {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Debug|Any CPU.Build.0 = Debug|Any CPU {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Release|Any CPU.ActiveCfg = Release|Any CPU {D87103FD-3851-4724-BD8F-9CEF19C8F193}.Release|Any CPU.Build.0 = Release|Any CPU + {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj new file mode 100644 index 000000000..2ccccb0d3 --- /dev/null +++ b/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -0,0 +1,120 @@ + + + + + + + Debug + AnyCPU + {9B9C6E6B-6C7C-414F-A162-816BBD6F0521} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 + v4.6.2 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + + + + + + ..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll + True + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + CommonAssemblyInfo.cs + + + VersionInfo.cs + + + + + + + + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs new file mode 100644 index 000000000..3558e4c45 --- /dev/null +++ b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure +{ + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + + public class EfCore2DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public EfCore2DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() => _dbSet.RemoveRange(_dbSet); + } +} \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs new file mode 100644 index 000000000..70b57a224 --- /dev/null +++ b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -0,0 +1,55 @@ +namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure +{ + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using Orms.TestClasses; + + public class EfCore2TestDbContext : DbContext, ITestDbContext + { + private static readonly DbContextOptions _inMemoryDbOptions = + new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "EfCore2TestDb") + .Options; + + public EfCore2TestDbContext() + : base(_inMemoryDbOptions) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + IDbSetWrapper ITestDbContext.Products + => new EfCore2DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new EfCore2DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new EfCore2DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new EfCore2DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new EfCore2DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new EfCore2DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs similarity index 75% rename from AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs rename to _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs index d95b9e3f1..92ab1fd6d 100644 --- a/AgileMapper.UnitTests.Ef5/Infrastructure/TestContextCollection.cs +++ b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Ef5.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure { using Orms; using Orms.Infrastructure; diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs new file mode 100644 index 000000000..c5f514911 --- /dev/null +++ b/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.EfCore2 +{ + using Infrastructure; + using Orms; + using Orms.Infrastructure; + + public class WhenProjectingFlatTypes : WhenProjectingFlatTypes + { + public WhenProjectingFlatTypes(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/packages.config b/_AgileMapper.UnitTests.Orms.EfCore2/packages.config new file mode 100644 index 000000000..3240e2e11 --- /dev/null +++ b/_AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file From 35ae21acaa75ccf8146f6e255062dbd342538a2e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 10 Nov 2017 11:37:30 +0000 Subject: [PATCH 021/176] Extending EFCore2 test coverage --- .../Infrastructure/Ef5TestDbContext.cs | 2 ++ .../Infrastructure/Ef6TestDbContext.cs | 2 ++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 ++ .../Infrastructure/EfCore2TestDbContext.cs | 2 ++ .../WhenConvertingToBools.cs | 14 +++++++++ .../WhenConvertingToInts.cs | 14 +++++++++ .../Infrastructure/ITestDbContext.cs | 2 ++ .../Infrastructure/OrmTestClassBase.cs | 22 +++++++------- .../WhenConvertingToInts.cs | 15 ++++++++-- .../QueryProjectionExpressionFactory.cs | 10 ++----- .../Queryables/QueryProjectionModifier.cs | 17 +++++++++-- .../StringEqualsIgnoreCaseConverter.cs | 30 ++++++++++++++----- 12 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 550c587c2..a2f33d111 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -26,6 +26,8 @@ public Ef5TestDbContext() #region ITestDbContext Members + public bool StringParsingSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 5ffaa12a3..a45898b88 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -26,6 +26,8 @@ public Ef6TestDbContext() #region ITestDbContext Members + public bool StringParsingSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 05c280944..1cc0d0aa6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -130,6 +130,8 @@ + + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index f3a4cc65c..5342007b9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -30,6 +30,8 @@ public EfCore2TestDbContext() #region ITestDbContext Members + public bool StringParsingSupported => true; + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs new file mode 100644 index 000000000..e8dcc6ee0 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToBools : WhenConvertingToBools + { + public WhenConvertingToBools(TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs new file mode 100644 index 000000000..196400fe2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToInts : WhenConvertingToInts + { + public WhenConvertingToInts(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 12cda8fd1..e1b0220bc 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -5,6 +5,8 @@ public interface ITestDbContext : IDisposable { + bool StringParsingSupported { get; } + IDbSetWrapper Products { get; } IDbSetWrapper BoolItems { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 80c28cbd5..baab4bd5f 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -8,13 +8,13 @@ public abstract class OrmTestClassBase where TOrmContext : ITestDbContext, new() { - private readonly TOrmContext _context; - protected OrmTestClassBase(TestContext context) { - _context = context.GetDbContext(); + Context = context.GetDbContext(); } + protected TOrmContext Context { get; } + protected Exception RunTestAndExpectThrow(Action testAction) => RunTestAndExpectThrow(testAction); @@ -26,20 +26,20 @@ protected TException RunTestAndExpectThrow(Action testA protected void RunTest(Action testAction) { - testAction.Invoke(_context); + testAction.Invoke(Context); EmptyDbContext(); } private void EmptyDbContext() { - _context.Products.Clear(); - _context.BoolItems.Clear(); - _context.ShortItems.Clear(); - _context.IntItems.Clear(); - _context.LongItems.Clear(); - _context.StringItems.Clear(); - _context.SaveChanges(); + Context.Products.Clear(); + Context.BoolItems.Clear(); + Context.ShortItems.Clear(); + Context.IntItems.Clear(); + Context.LongItems.Clear(); + Context.StringItems.Clear(); + Context.SaveChanges(); } } } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index f656a86f2..7c054c015 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -71,9 +71,9 @@ public void ShouldProjectATooSmallLongToAnInt() } [Fact] - public void ShouldErrorIfProjectingStringToAnInt() + public void ShouldProjectAParseableStringToAnIntAsExpected() { - RunTestAndExpectThrow(context => + void Test(TOrmContext context) { context.StringItems.Add(new PublicStringProperty { Value = "738" }); context.SaveChanges(); @@ -81,7 +81,16 @@ public void ShouldErrorIfProjectingStringToAnInt() var intItem = context.StringItems.ProjectTo().First(); intItem.Value.ShouldBe(738); - }); + } + + if (Context.StringParsingSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index ca7c0a53c..f76658fca 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -22,13 +22,6 @@ internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase #endregion - private readonly QueryProjectionModifier _queryProjectionModifier; - - private QueryProjectionExpressionFactory() - { - _queryProjectionModifier = new QueryProjectionModifier(); - } - public override bool IsFor(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; @@ -41,6 +34,7 @@ public override bool IsFor(IObjectMappingData mappingData) protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; + var queryable = mappingData.GetSource(); var queryProjection = mapperData .EnumerablePopulationBuilder @@ -52,7 +46,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); - queryProjection = _queryProjectionModifier.Modify(queryProjection); + queryProjection = QueryProjectionModifier.Modify(queryProjection, queryable); yield return queryProjection; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index abe5b694d..7e452a84d 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,10 +1,23 @@ namespace AgileObjects.AgileMapper.Queryables { + using System.Linq; using System.Linq.Expressions; internal class QueryProjectionModifier : ExpressionVisitor { - public Expression Modify(Expression queryProjection) + private readonly IQueryable _queryable; + + private QueryProjectionModifier(IQueryable queryable) + { + _queryable = queryable; + } + + public static Expression Modify(Expression queryProjection, IQueryable queryable) + { + return new QueryProjectionModifier(queryable).Modify(queryProjection); + } + + private Expression Modify(Expression queryProjection) { return VisitAndConvert(queryProjection, "Modify"); } @@ -21,7 +34,7 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig protected override Expression VisitMethodCall(MethodCallExpression methodCall) { - if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, out var converted)) + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _queryable, out var converted)) { return converted; } diff --git a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs index 29f8fc1ff..4d5c7de5a 100644 --- a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs @@ -7,11 +7,11 @@ internal static class StringEqualsIgnoreCaseConverter { - public static bool TryConvert(MethodCallExpression methodCall, out Expression converted) + public static bool TryConvert(MethodCallExpression methodCall, IQueryable queryable, out Expression converted) { if (IsEqualsIgnoreCaseCall(methodCall)) { - converted = Convert(methodCall); + converted = Convert(methodCall, queryable); return true; } @@ -27,17 +27,31 @@ private static bool IsEqualsIgnoreCaseCall(MethodCallExpression methodCall) (methodCall.Method.Name == "Equals"); } - private static Expression Convert(MethodCallExpression methodCall) + private static Expression Convert(MethodCallExpression methodCall, IQueryable queryable) { + var subject = methodCall.Arguments[0]; + var subjectToLower = Expression.Call( - methodCall.Arguments[0], - typeof(string) - .GetPublicInstanceMethods() - .First(m => (m.Name == "ToLower") && (m.GetParameters().None()))); + subject, + typeof(string).GetPublicInstanceMethod("ToLower", parameterCount: 0)); var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); - return comparison; + if (NullCheckNotRequired(queryable)) + { + return comparison; + } + + var subjectNotNull = subject.GetIsNotDefaultComparison(); + + return Expression.AndAlso(subjectNotNull, comparison); + } + + private static bool NullCheckNotRequired(IQueryable queryable) + { + var providerName = queryable.Provider.GetType().FullName; + + return !providerName.Contains("EntityFrameworkCore"); } } } \ No newline at end of file From d78977078059755b7cc3fb79b942aa3f9ca9838e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 10 Nov 2017 15:33:26 +0000 Subject: [PATCH 022/176] Using ORM provider-specific settings to tweak query projections --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 15 ++- .../Infrastructure/EfCore2TestDbContext.cs | 7 +- .../packages.config | 23 ++-- .../Infrastructure/OrmTestClassBase.cs | 11 +- .../WhenConvertingToInts.cs | 13 +++ .../QueryProjectionExpressionFactory.cs | 3 +- .../Queryables/QueryProjectionModifier.cs | 15 ++- .../Queryables/QueryProviderSettings.cs | 106 ++++++++++++++++++ .../StringEqualsIgnoreCaseConverter.cs | 46 +++----- .../Queryables/TryParseAssignmentConverter.cs | 5 +- 10 files changed, 181 insertions(+), 63 deletions(-) create mode 100644 AgileMapper/Queryables/QueryProviderSettings.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 1cc0d0aa6..1ec767ab8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -64,6 +64,9 @@ ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Debug.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Debug.dll + ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll @@ -71,7 +74,7 @@ ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll - ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll @@ -89,18 +92,18 @@ ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll - ..\packages\System.Reflection.4.1.0\lib\net462\System.Reflection.dll + ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll True - ..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll + ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll True ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - ..\packages\System.Runtime.Extensions.4.1.0\lib\net462\System.Runtime.Extensions.dll + ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll True @@ -135,7 +138,9 @@ - + + Designer + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 5342007b9..7b9fca957 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure { using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Logging.Debug; using Orms.Infrastructure; using TestClasses; @@ -8,8 +10,9 @@ public class EfCore2TestDbContext : DbContext, ITestDbContext { private static readonly DbContextOptions _inMemoryOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "Ef6TestDbContext") - .Options; + .UseLoggerFactory(new LoggerFactory(new[] { new DebugLoggerProvider() })) + .UseInMemoryDatabase(databaseName: "Ef6TestDbContext") + .Options; public EfCore2TestDbContext() : base(_inMemoryOptions) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config index 059cafd34..3e4d8e4de 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -8,25 +8,26 @@ + - - + + - + - - + + - - - - + + + + - - + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index baab4bd5f..71af9112d 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -26,9 +26,14 @@ protected TException RunTestAndExpectThrow(Action testA protected void RunTest(Action testAction) { - testAction.Invoke(Context); - - EmptyDbContext(); + try + { + testAction.Invoke(Context); + } + finally + { + EmptyDbContext(); + } } private void EmptyDbContext() diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 7c054c015..7b72a7c81 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -92,5 +92,18 @@ void Test(TOrmContext context) RunTestAndExpectThrow(Test); } } + + [Fact] + public void ShouldProjectAnUnparseableStringToAnIntAsExpected() + { + RunTestAndExpectThrow(context => + { + context.StringItems.Add(new PublicStringProperty { Value = "hsejk" }); + context.SaveChanges(); + + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + context.StringItems.ProjectTo().First(); + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index f76658fca..2f1ca0ec0 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -35,6 +35,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat { var mapperData = mappingData.MapperData; var queryable = mappingData.GetSource(); + var providerSettings = QueryProviderSettings.For(queryable); var queryProjection = mapperData .EnumerablePopulationBuilder @@ -46,7 +47,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); - queryProjection = QueryProjectionModifier.Modify(queryProjection, queryable); + queryProjection = QueryProjectionModifier.Modify(queryProjection, providerSettings); yield return queryProjection; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 7e452a84d..c6cf76673 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,20 +1,19 @@ namespace AgileObjects.AgileMapper.Queryables { - using System.Linq; using System.Linq.Expressions; internal class QueryProjectionModifier : ExpressionVisitor { - private readonly IQueryable _queryable; + private readonly QueryProviderSettings _settings; - private QueryProjectionModifier(IQueryable queryable) + private QueryProjectionModifier(QueryProviderSettings settings) { - _queryable = queryable; + _settings = settings; } - public static Expression Modify(Expression queryProjection, IQueryable queryable) + public static Expression Modify(Expression queryProjection, QueryProviderSettings settings) { - return new QueryProjectionModifier(queryable).Modify(queryProjection); + return new QueryProjectionModifier(settings).Modify(queryProjection); } private Expression Modify(Expression queryProjection) @@ -24,7 +23,7 @@ private Expression Modify(Expression queryProjection) protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { - if (TryParseAssignmentConverter.TryConvert(assignment, out var converted)) + if (TryParseAssignmentConverter.TryConvert(assignment, _settings, out var converted)) { return converted; } @@ -34,7 +33,7 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig protected override Expression VisitMethodCall(MethodCallExpression methodCall) { - if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _queryable, out var converted)) + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _settings, out var converted)) { return converted; } diff --git a/AgileMapper/Queryables/QueryProviderSettings.cs b/AgileMapper/Queryables/QueryProviderSettings.cs new file mode 100644 index 000000000..690d80fd6 --- /dev/null +++ b/AgileMapper/Queryables/QueryProviderSettings.cs @@ -0,0 +1,106 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System; + using System.Linq; + using NetStandardPolyfills; + + internal class QueryProviderSettings + { + #region Instances + + #region EF5 + + private static readonly QueryProviderSettings _ef5Settings = new QueryProviderSettings(InitEf5Settings) + { + }; + + private static void InitEf5Settings(QueryProviderSettings settings, IQueryProvider provider) + { + } + + #endregion + + #region EF6 + + private static readonly QueryProviderSettings _ef6Settings = new QueryProviderSettings(InitEf6Settings) + { + }; + + private static void InitEf6Settings(QueryProviderSettings settings, IQueryProvider provider) + { + } + + #endregion + + #region EFCore2 + + private static readonly QueryProviderSettings _efCore2Settings = new QueryProviderSettings(InitEfCore2Settings) + { + SupportsStringEqualsIgnoreCase = true + }; + + private static void InitEfCore2Settings(QueryProviderSettings settings, IQueryProvider provider) + { + } + + #endregion + + private static readonly QueryProviderSettings _fallbackSettings = new QueryProviderSettings(null) + { + }; + + #endregion + + private readonly object _initLock = new object(); + private readonly Action _initAction; + private bool _isInitialised; + + private QueryProviderSettings(Action initAction) + { + _initAction = initAction; + } + + public static QueryProviderSettings For(IQueryable queryable) + { + var queryableProviderAssemblyName = queryable.Provider.GetType().GetAssembly().GetName(); + var queryableProviderName = queryableProviderAssemblyName.FullName; + + if (queryableProviderName.Contains("EntityFrameworkCore")) + { + return _efCore2Settings.Initialised(queryable); + } + + if (queryableProviderName.Contains("EntityFramework")) + { + switch (queryableProviderAssemblyName.Version.Major) + { + case 5: + return _ef5Settings.Initialised(queryable); + + case 6: + return _ef6Settings.Initialised(queryable); + } + } + + return _fallbackSettings; + } + + private QueryProviderSettings Initialised(IQueryable queryable) + { + if (_isInitialised || (_initAction == null)) + { + return this; + } + + lock (_initLock) + { + _isInitialised = true; + _initAction.Invoke(this, queryable.Provider); + } + + return this; + } + + public bool SupportsStringEqualsIgnoreCase { get; private set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs index 4d5c7de5a..a290a76c9 100644 --- a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs @@ -1,57 +1,39 @@ namespace AgileObjects.AgileMapper.Queryables { - using System.Linq; using System.Linq.Expressions; - using Extensions; using NetStandardPolyfills; internal static class StringEqualsIgnoreCaseConverter { - public static bool TryConvert(MethodCallExpression methodCall, IQueryable queryable, out Expression converted) + public static bool TryConvert(MethodCallExpression methodCall, QueryProviderSettings settings, out Expression converted) { - if (IsEqualsIgnoreCaseCall(methodCall)) + if (settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) { - converted = Convert(methodCall, queryable); - return true; + converted = null; + return false; } - converted = null; - return false; + converted = Convert(methodCall); + return true; } - private static bool IsEqualsIgnoreCaseCall(MethodCallExpression methodCall) + private static bool IsNotEqualsIgnoreCaseCall(MethodCallExpression methodCall) { - return methodCall.Method.IsStatic && - (methodCall.Arguments.Count == 3) && - (methodCall.Method.DeclaringType == typeof(string)) && - (methodCall.Method.Name == "Equals"); + return !methodCall.Method.IsStatic || + (methodCall.Arguments.Count != 3) || + (methodCall.Method.DeclaringType != typeof(string)) || + (methodCall.Method.Name != "Equals"); } - private static Expression Convert(MethodCallExpression methodCall, IQueryable queryable) + private static Expression Convert(MethodCallExpression methodCall) { - var subject = methodCall.Arguments[0]; - var subjectToLower = Expression.Call( - subject, + methodCall.Arguments[0], typeof(string).GetPublicInstanceMethod("ToLower", parameterCount: 0)); var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); - if (NullCheckNotRequired(queryable)) - { - return comparison; - } - - var subjectNotNull = subject.GetIsNotDefaultComparison(); - - return Expression.AndAlso(subjectNotNull, comparison); - } - - private static bool NullCheckNotRequired(IQueryable queryable) - { - var providerName = queryable.Provider.GetType().FullName; - - return !providerName.Contains("EntityFrameworkCore"); + return comparison; } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index 0daa96bff..c6657357f 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -8,7 +8,10 @@ internal static class TryParseAssignmentConverter { - public static bool TryConvert(MemberAssignment assignment, out MemberAssignment converted) + public static bool TryConvert( + MemberAssignment assignment, + QueryProviderSettings settings, + out MemberAssignment converted) { if (assignment.Expression.NodeType != ExpressionType.Block) { From 31de56d5165424af92a064e082d29d345c872ec6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 09:32:02 +0000 Subject: [PATCH 023/176] Organising QueryProvider settings classes / Start of test coverage for to-string conversions --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConvertingToStrings.cs | 14 +++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConvertingToStrings.cs | 14 +++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConvertingToStrings.cs | 14 +++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../WhenConvertingToStrings.cs | 31 +++++ .../TestClasses/PublicStringPropertyDto.cs | 10 ++ .../QueryProjectionExpressionFactory.cs | 1 + .../Queryables/QueryProjectionModifier.cs | 14 ++- .../Queryables/QueryProviderSettings.cs | 106 ------------------ .../Settings/DefaultQueryProviderSettings.cs | 13 +++ .../Settings/Ef5QueryProviderSettings.cs | 51 +++++++++ .../Settings/Ef6QueryProviderSettings.cs | 7 ++ .../Settings/EfCore2QueryProviderSettings.cs | 9 ++ .../Settings/IQueryProviderSettings.cs | 13 +++ .../Settings/QueryProviderSettings.cs | 38 +++++++ .../StringEqualsIgnoreCaseConverter.cs | 6 +- AgileMapper/Queryables/ToStringConverter.cs | 31 +++++ .../Queryables/TryParseAssignmentConverter.cs | 4 +- 21 files changed, 269 insertions(+), 112 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs delete mode 100644 AgileMapper/Queryables/QueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/IQueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/Settings/QueryProviderSettings.cs create mode 100644 AgileMapper/Queryables/ToStringConverter.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 27c430ec9..dd37b6a3b 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -88,6 +88,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..4d3f60638 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToStrings : WhenConvertingToStrings + { + public WhenConvertingToStrings(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 57bb3b570..20dc361c7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -93,6 +93,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..806d2f5f4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToStrings : WhenConvertingToStrings + { + public WhenConvertingToStrings(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 1ec767ab8..0e2415090 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -135,6 +135,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..395231aa2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToStrings : WhenConvertingToStrings + { + public WhenConvertingToStrings(TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 7393f8499..a1285bfbe 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -77,6 +77,8 @@ + + diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..039ed3bda --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,31 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToStrings : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToStrings(TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAnIntToAString() + { + RunTest(context => + { + context.IntItems.Add(new PublicIntProperty { Value = 763483 }); + context.SaveChanges(); + + var stringItem = context.IntItems.ProjectTo().First(); + + stringItem.Value.ShouldBe("763483"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs new file mode 100644 index 000000000..a53f8be41 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class PublicStringPropertyDto + { + public int Id { get; set; } + + + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 2f1ca0ec0..457dde631 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -7,6 +7,7 @@ using Extensions; using NetStandardPolyfills; using ObjectPopulation; + using Settings; internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase { diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index c6cf76673..73dfe795d 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,17 +1,18 @@ namespace AgileObjects.AgileMapper.Queryables { using System.Linq.Expressions; + using Settings; internal class QueryProjectionModifier : ExpressionVisitor { - private readonly QueryProviderSettings _settings; + private readonly IQueryProviderSettings _settings; - private QueryProjectionModifier(QueryProviderSettings settings) + private QueryProjectionModifier(IQueryProviderSettings settings) { _settings = settings; } - public static Expression Modify(Expression queryProjection, QueryProviderSettings settings) + public static Expression Modify(Expression queryProjection, IQueryProviderSettings settings) { return new QueryProjectionModifier(settings).Modify(queryProjection); } @@ -33,7 +34,12 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig protected override Expression VisitMethodCall(MethodCallExpression methodCall) { - if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _settings, out var converted)) + if (ToStringConverter.TryConvert(methodCall, _settings, out var converted)) + { + return converted; + } + + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _settings, out converted)) { return converted; } diff --git a/AgileMapper/Queryables/QueryProviderSettings.cs b/AgileMapper/Queryables/QueryProviderSettings.cs deleted file mode 100644 index 690d80fd6..000000000 --- a/AgileMapper/Queryables/QueryProviderSettings.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace AgileObjects.AgileMapper.Queryables -{ - using System; - using System.Linq; - using NetStandardPolyfills; - - internal class QueryProviderSettings - { - #region Instances - - #region EF5 - - private static readonly QueryProviderSettings _ef5Settings = new QueryProviderSettings(InitEf5Settings) - { - }; - - private static void InitEf5Settings(QueryProviderSettings settings, IQueryProvider provider) - { - } - - #endregion - - #region EF6 - - private static readonly QueryProviderSettings _ef6Settings = new QueryProviderSettings(InitEf6Settings) - { - }; - - private static void InitEf6Settings(QueryProviderSettings settings, IQueryProvider provider) - { - } - - #endregion - - #region EFCore2 - - private static readonly QueryProviderSettings _efCore2Settings = new QueryProviderSettings(InitEfCore2Settings) - { - SupportsStringEqualsIgnoreCase = true - }; - - private static void InitEfCore2Settings(QueryProviderSettings settings, IQueryProvider provider) - { - } - - #endregion - - private static readonly QueryProviderSettings _fallbackSettings = new QueryProviderSettings(null) - { - }; - - #endregion - - private readonly object _initLock = new object(); - private readonly Action _initAction; - private bool _isInitialised; - - private QueryProviderSettings(Action initAction) - { - _initAction = initAction; - } - - public static QueryProviderSettings For(IQueryable queryable) - { - var queryableProviderAssemblyName = queryable.Provider.GetType().GetAssembly().GetName(); - var queryableProviderName = queryableProviderAssemblyName.FullName; - - if (queryableProviderName.Contains("EntityFrameworkCore")) - { - return _efCore2Settings.Initialised(queryable); - } - - if (queryableProviderName.Contains("EntityFramework")) - { - switch (queryableProviderAssemblyName.Version.Major) - { - case 5: - return _ef5Settings.Initialised(queryable); - - case 6: - return _ef6Settings.Initialised(queryable); - } - } - - return _fallbackSettings; - } - - private QueryProviderSettings Initialised(IQueryable queryable) - { - if (_isInitialised || (_initAction == null)) - { - return this; - } - - lock (_initLock) - { - _isInitialised = true; - _initAction.Invoke(this, queryable.Provider); - } - - return this; - } - - public bool SupportsStringEqualsIgnoreCase { get; private set; } - } -} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs new file mode 100644 index 000000000..160472e20 --- /dev/null +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + using System.Linq.Expressions; + + internal class DefaultQueryProviderSettings : IQueryProviderSettings + { + public virtual bool SupportsStringEqualsIgnoreCase => false; + + public virtual bool SupportsToString => false; + + public virtual Expression ConvertToString(MethodCallExpression toStringCall) => toStringCall; + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs new file mode 100644 index 000000000..ee023166e --- /dev/null +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -0,0 +1,51 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ +#if !NET_STANDARD + using System; + using System.Linq; + using System.Linq.Expressions; + using Extensions; + using NetStandardPolyfills; +#endif + + internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings + { +#if !NET_STANDARD + public override Expression ConvertToString(MethodCallExpression toStringCall) + { + var sqlFunctionsType = AppDomain.CurrentDomain + .GetAssemblies() + .FirstOrDefault(assembly => assembly.GetName().Name == "System.Data.Entity")? + .GetType("System.Data.Objects.SqlClient.SqlFunctions"); + + if (sqlFunctionsType == null) + { + return base.ConvertToString(toStringCall); + } + + var subject = toStringCall.Object; + + // ReSharper disable once PossibleNullReferenceException + var subjectType = subject.Type.GetNonNullableType(); + + if (subjectType == typeof(decimal)) + { + return Expression.Call( + sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(decimal?)), + subject); + } + + if (subjectType != typeof(double)) + { + subject = Expression.Convert(subject, typeof(double?)); + } + + var subjectToString = Expression.Call( + sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(double?)), + subject); + + return subjectToString; + } +#endif + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs new file mode 100644 index 000000000..a22d70b54 --- /dev/null +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings + { + public override bool SupportsToString => true; + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs new file mode 100644 index 000000000..ac8a2591c --- /dev/null +++ b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings + { + public override bool SupportsStringEqualsIgnoreCase => true; + + public override bool SupportsToString => true; + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs new file mode 100644 index 000000000..399a1ae99 --- /dev/null +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + using System.Linq.Expressions; + + internal interface IQueryProviderSettings + { + bool SupportsStringEqualsIgnoreCase { get; } + + bool SupportsToString { get; } + + Expression ConvertToString(MethodCallExpression toStringCall); + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs new file mode 100644 index 000000000..fc45a07f4 --- /dev/null +++ b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + using System.Linq; + using NetStandardPolyfills; + + internal static class QueryProviderSettings + { + private static readonly IQueryProviderSettings _ef5Settings = new Ef5QueryProviderSettings(); + private static readonly IQueryProviderSettings _ef6Settings = new Ef6QueryProviderSettings(); + private static readonly IQueryProviderSettings _efCore2Settings = new EfCore2QueryProviderSettings(); + private static readonly IQueryProviderSettings _defaultSettings = new DefaultQueryProviderSettings(); + + public static IQueryProviderSettings For(IQueryable queryable) + { + var queryableProviderAssemblyName = queryable.Provider.GetType().GetAssembly().GetName(); + var queryableProviderName = queryableProviderAssemblyName.FullName; + + if (queryableProviderName.Contains("EntityFrameworkCore")) + { + return _efCore2Settings; + } + + if (queryableProviderName.Contains("EntityFramework")) + { + switch (queryableProviderAssemblyName.Version.Major) + { + case 5: + return _ef5Settings; + + case 6: + return _ef6Settings; + } + } + + return _defaultSettings; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs index a290a76c9..6bdd75fc8 100644 --- a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs @@ -2,10 +2,14 @@ { using System.Linq.Expressions; using NetStandardPolyfills; + using Settings; internal static class StringEqualsIgnoreCaseConverter { - public static bool TryConvert(MethodCallExpression methodCall, QueryProviderSettings settings, out Expression converted) + public static bool TryConvert( + MethodCallExpression methodCall, + IQueryProviderSettings settings, + out Expression converted) { if (settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) { diff --git a/AgileMapper/Queryables/ToStringConverter.cs b/AgileMapper/Queryables/ToStringConverter.cs new file mode 100644 index 000000000..af05ce601 --- /dev/null +++ b/AgileMapper/Queryables/ToStringConverter.cs @@ -0,0 +1,31 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq.Expressions; + using Extensions; + using Settings; + + internal static class ToStringConverter + { + public static bool TryConvert( + MethodCallExpression methodCall, + IQueryProviderSettings settings, + out Expression converted) + { + if (settings.SupportsToString || IsNotToStringCall(methodCall)) + { + converted = null; + return false; + } + + converted = settings.ConvertToString(methodCall); + return true; + } + + private static bool IsNotToStringCall(MethodCallExpression methodCall) + { + return methodCall.Arguments.Any() || + methodCall.Method.IsStatic || + (methodCall.Method.Name != "ToString"); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index c6657357f..17c613224 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -5,12 +5,13 @@ using System.Linq.Expressions; using Extensions; using NetStandardPolyfills; + using Settings; internal static class TryParseAssignmentConverter { public static bool TryConvert( MemberAssignment assignment, - QueryProviderSettings settings, + IQueryProviderSettings settings, out MemberAssignment converted) { if (assignment.Expression.NodeType != ExpressionType.Block) @@ -50,6 +51,7 @@ public static bool TryConvert( private static MethodCallExpression GetConvertCall(MethodCallExpression tryParseCall) { + // ReSharper disable once PossibleNullReferenceException // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, // but it at least gives a decent error message: var convertMethodName = "To" + tryParseCall.Method.DeclaringType.Name; From 5d0bc48f5d3efe136f8b6540fbbf1ed75538bf1a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 09:54:37 +0000 Subject: [PATCH 024/176] Switching test projects to unit test project types - nice icons :) --- .../AgileMapper.UnitTests.NonParallel.csproj | 5 ++++- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 3 +++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 3 +++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 3 ++- AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj | 3 +++ AgileMapper.UnitTests/AgileMapper.UnitTests.csproj | 5 ++++- 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj index c9d68cfc7..a10745013 100644 --- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj +++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj @@ -1,5 +1,5 @@  - + @@ -13,6 +13,9 @@ AgileObjects.AgileMapper.UnitTests.NonParallel v4.6.1 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index dd37b6a3b..3f2e46979 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -13,6 +13,9 @@ AgileObjects.AgileMapper.UnitTests.Orms.Ef5 v4.6.2 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 20dc361c7..f44995e3c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -13,6 +13,9 @@ AgileObjects.AgileMapper.UnitTests.Orms.Ef6 v4.6.2 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 0e2415090..41f7f204a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -1,7 +1,8 @@  - + + Debug AnyCPU diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index a1285bfbe..6c97cb6a6 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -12,6 +12,9 @@ AgileObjects.AgileMapper.UnitTests.Orms v4.6.2 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index aa0536f4d..ee936d5f6 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -1,5 +1,5 @@  - + @@ -13,6 +13,9 @@ AgileObjects.AgileMapper.UnitTests v4.6.1 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest From 0d16d2f3a11e0ca058e82d56706ecd83a0303ea4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 10:34:14 +0000 Subject: [PATCH 025/176] Deferentiating between in-memory and local db ORM test contexts / EF5 integration tests project added to enable testing of SqlFunctions.StringConvert calls --- ...ileMapper.IntegrationTests.Orms.Ef5.csproj | 107 ++++++++++++++++++ .../App.config | 14 +++ .../Infrastructure/Ef5DbSetWrapper.cs | 28 +++++ .../Infrastructure/Ef5LocalDbTestContext.cs | 34 ++++++ .../Ef5LocalDbTestDefinition.cs | 10 ++ .../Infrastructure/Ef5TestDbContext.cs | 57 ++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ .../WhenConvertingToStrings.cs | 13 +++ .../packages.config | 12 ++ .../AgileMapper.UnitTests.Orms.Ef5.csproj | 3 +- ...ection.cs => InMemoryOrmTestDefinition.cs} | 2 +- .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenConvertingToStrings.cs | 14 --- .../WhenProjectingFlatTypes.cs | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- ...ection.cs => InMemoryOrmTestDefinition.cs} | 2 +- .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenConvertingToStrings.cs | 2 +- .../WhenProjectingFlatTypes.cs | 2 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- ...ection.cs => InMemoryOrmTestDefinition.cs} | 2 +- .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenConvertingToStrings.cs | 2 +- .../WhenProjectingFlatTypes.cs | 2 +- .../AgileMapper.UnitTests.Orms.csproj | 3 +- .../Infrastructure/ITestContext.cs | 10 ++ ...stContext.cs => InMemoryOrmTestContext.cs} | 2 +- .../Infrastructure/OrmTestClassBase.cs | 2 +- .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenConvertingToStrings.cs | 2 +- .../WhenProjectingFlatTypes.cs | 2 +- AgileMapper.sln | 6 + .../Settings/Ef5QueryProviderSettings.cs | 14 ++- 37 files changed, 361 insertions(+), 44 deletions(-) create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/App.config create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/packages.config rename AgileMapper.UnitTests.Orms.Ef5/Infrastructure/{TestContextCollection.cs => InMemoryOrmTestDefinition.cs} (70%) delete mode 100644 AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs rename AgileMapper.UnitTests.Orms.Ef6/Infrastructure/{TestContextCollection.cs => InMemoryOrmTestDefinition.cs} (70%) rename AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/{TestContextCollection.cs => InMemoryOrmTestDefinition.cs} (71%) create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs rename AgileMapper.UnitTests.Orms/Infrastructure/{TestContext.cs => InMemoryOrmTestContext.cs} (88%) diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj new file mode 100644 index 000000000..fd592feea --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj @@ -0,0 +1,107 @@ + + + + + + Debug + AnyCPU + {48B855A9-F42C-4D2A-9549-97ED27D59336} + Library + Properties + AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5 + AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5 + v4.6.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\AgileObjects.NetStandardPolyfills.1.0.0\lib\net40\AgileObjects.NetStandardPolyfills.dll + + + ..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll + + + + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + + + + + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/App.config b/AgileMapper.IntegrationTests.Orms.Ef5/App.config new file mode 100644 index 000000000..e5af0d11e --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/App.config @@ -0,0 +1,14 @@ + + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs new file mode 100644 index 000000000..139a9a64f --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -0,0 +1,28 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +{ + using System.Data.Entity; + using System.Linq; + using UnitTests.Orms.Infrastructure; + + public class Ef5DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public Ef5DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() + { + foreach (var entity in _dbSet.ToArray()) + { + _dbSet.Remove(entity); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs new file mode 100644 index 000000000..79f044122 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs @@ -0,0 +1,34 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +{ + using System.Data.Entity.Infrastructure; + using UnitTests.Orms.Infrastructure; + + public class Ef5LocalDbTestContext : ITestContext + { + private readonly Ef5TestDbContext _dbContext; + + public Ef5LocalDbTestContext() + { + var localDbConnectionFactory = + new SqlConnectionFactory(@"Data Source=(local);Integrated Security=True;MultipleActiveResultSets=True"); + + var localDbConnection = localDbConnectionFactory.CreateConnection("Ef5TestDb"); + + _dbContext = new Ef5TestDbContext(localDbConnection); + + _dbContext.Database.Create(); + } + + public TOrmContext GetDbContext() + where TOrmContext : ITestDbContext, new() + { + return (TOrmContext)(object)_dbContext; + } + + public void Dispose() + { + _dbContext.Database.Delete(); + _dbContext.Dispose(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs new file mode 100644 index 000000000..74e8e46d4 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +{ + using UnitTests.Orms; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class Ef5LocalDbTestDefinition : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs new file mode 100644 index 000000000..c9dc949f1 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -0,0 +1,57 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +{ + using System.Data.Common; + using System.Data.Entity; + using UnitTests.Orms.Infrastructure; + using UnitTests.Orms.TestClasses; + + public class Ef5TestDbContext : DbContext, ITestDbContext + { + public Ef5TestDbContext() + { + } + + public Ef5TestDbContext(DbConnection localDbConnection) + : base(localDbConnection, false) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + public bool StringParsingSupported => false; + + IDbSetWrapper ITestDbContext.Products + => new Ef5DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new Ef5DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new Ef5DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new Ef5DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new Ef5DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new Ef5DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0b952189e --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AgileMapper.IntegrationTests.Orms.Ef5")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AgileMapper.IntegrationTests.Orms.Ef5")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("48b855a9-f42c-4d2a-9549-97ed27d59336")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..29e038718 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using UnitTests.Orms.SimpleTypeConversion; + + public class WhenConvertingToStrings : WhenConvertingToStrings + { + public WhenConvertingToStrings(Ef5LocalDbTestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/packages.config b/AgileMapper.IntegrationTests.Orms.Ef5/packages.config new file mode 100644 index 000000000..b38cae77f --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 3f2e46979..feb495bec 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,9 +89,8 @@ VersionInfo.cs - + - diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs similarity index 70% rename from AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs rename to AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs index 13c276025..bf8256be1 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/TestContextCollection.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs @@ -5,7 +5,7 @@ using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class TestContextCollection : ICollectionFixture + public class InMemoryOrmTestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs index c941a3bca..0db240e61 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs @@ -6,7 +6,7 @@ public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(TestContext context) + public WhenConvertingToBools(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index d55e749ab..72946d0c3 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,7 +6,7 @@ public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(TestContext context) + public WhenConvertingToInts(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs deleted file mode 100644 index 4d3f60638..000000000 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion -{ - using Infrastructure; - using Orms.Infrastructure; - using Orms.SimpleTypeConversion; - - public class WhenConvertingToStrings : WhenConvertingToStrings - { - public WhenConvertingToStrings(TestContext context) - : base(context) - { - } - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs index 9c0311569..9b711ea90 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs @@ -6,7 +6,7 @@ public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(TestContext context) + public WhenProjectingFlatTypes(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index f44995e3c..2ec157e98 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,7 +91,7 @@ VersionInfo.cs - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs similarity index 70% rename from AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs rename to AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs index 7be9557cf..eb4eb0845 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/TestContextCollection.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs @@ -5,7 +5,7 @@ using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class TestContextCollection : ICollectionFixture + public class InMemoryOrmTestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs index cfc43404b..73f1cdc38 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -6,7 +6,7 @@ public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(TestContext context) + public WhenConvertingToBools(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index dbdd7427c..4f2c8f47f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,7 +6,7 @@ public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(TestContext context) + public WhenConvertingToInts(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs index 806d2f5f4..778668943 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -6,7 +6,7 @@ public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(TestContext context) + public WhenConvertingToStrings(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs index 90e935068..cd340e8f0 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs @@ -6,7 +6,7 @@ public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(TestContext context) + public WhenProjectingFlatTypes(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 41f7f204a..5a365a561 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -132,7 +132,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs similarity index 71% rename from AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs rename to AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs index 791f97c28..11b5de7bd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs @@ -5,7 +5,7 @@ using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class TestContextCollection : ICollectionFixture + public class InMemoryOrmTestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs index e8dcc6ee0..f08d7e500 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs @@ -6,7 +6,7 @@ public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(TestContext context) + public WhenConvertingToBools(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 196400fe2..545a5d1ce 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,7 +6,7 @@ public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(TestContext context) + public WhenConvertingToInts(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs index 395231aa2..ee3828f9e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -6,7 +6,7 @@ public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(TestContext context) + public WhenConvertingToStrings(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs index 37c879b69..0bc43f815 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs @@ -6,7 +6,7 @@ public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(TestContext context) + public WhenProjectingFlatTypes(InMemoryOrmTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 6c97cb6a6..52c4bfaa9 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -78,12 +78,13 @@ + - + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs new file mode 100644 index 000000000..73b864460 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + using System; + + public interface ITestContext : IDisposable + { + TOrmContext GetDbContext() + where TOrmContext : ITestDbContext, new(); + } +} diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs similarity index 88% rename from AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs rename to AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs index 9623ddb86..32624d73a 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/TestContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs @@ -2,7 +2,7 @@ { using System; - public class TestContext : IDisposable + public class InMemoryOrmTestContext : ITestContext { private IDisposable _dbContext; diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 71af9112d..8ec7ed3dd 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -8,7 +8,7 @@ public abstract class OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected OrmTestClassBase(TestContext context) + protected OrmTestClassBase(ITestContext context) { Context = context.GetDbContext(); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index 2feae78a7..cecd00a11 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToBools : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToBools(TestContext context) + protected WhenConvertingToBools(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 7b72a7c81..c196b4069 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToInts : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToInts(TestContext context) + protected WhenConvertingToInts(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index 039ed3bda..a3a7a9201 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToStrings : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToStrings(TestContext context) + protected WhenConvertingToStrings(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 03dfe0acc..45cda7cfa 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -9,7 +9,7 @@ public abstract class WhenProjectingFlatTypes : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenProjectingFlatTypes(TestContext context) + protected WhenProjectingFlatTypes(ITestContext context) : base(context) { } diff --git a/AgileMapper.sln b/AgileMapper.sln index 4d6f2f82a..038de98f7 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -30,6 +30,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore2", "AgileMapper.UnitTests.Orms.EfCore2\AgileMapper.UnitTests.Orms.EfCore2.csproj", "{2E3DF5C2-8A38-4A03-86D7-8D463C917E47}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTests.Orms.Ef5", "AgileMapper.IntegrationTests.Orms.Ef5\AgileMapper.IntegrationTests.Orms.Ef5.csproj", "{48B855A9-F42C-4D2A-9549-97ED27D59336}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +74,10 @@ Global {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.Build.0 = Release|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index ee023166e..66a316e73 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -23,9 +23,15 @@ public override Expression ConvertToString(MethodCallExpression toStringCall) return base.ConvertToString(toStringCall); } - var subject = toStringCall.Object; + var stringConvertCall = GetStringConvertCall(toStringCall.Object, sqlFunctionsType); + var trimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); + var trimCall = Expression.Call(stringConvertCall, trimMethod); - // ReSharper disable once PossibleNullReferenceException + return trimCall; + } + + private static Expression GetStringConvertCall(Expression subject, Type sqlFunctionsType) + { var subjectType = subject.Type.GetNonNullableType(); if (subjectType == typeof(decimal)) @@ -40,11 +46,9 @@ public override Expression ConvertToString(MethodCallExpression toStringCall) subject = Expression.Convert(subject, typeof(double?)); } - var subjectToString = Expression.Call( + return Expression.Call( sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(double?)), subject); - - return subjectToString; } #endif } From 482f859f68281c01ffbccb51e89b11de69646e01 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 11:32:03 +0000 Subject: [PATCH 026/176] Genericising local db context / Switching to typed test contexts --- ...ileMapper.IntegrationTests.Orms.Ef5.csproj | 1 - .../Infrastructure/Ef5LocalDbTestContext.cs | 34 ------------------- .../Ef5LocalDbTestDefinition.cs | 3 +- .../Infrastructure/Ef5TestDbContext.cs | 21 ++++++++---- .../WhenConvertingToStrings.cs | 3 +- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 3 +- .../Infrastructure/InMemoryEf5TestContext.cs | 8 +++++ ...nition.cs => InMemoryEf5TestDefinition.cs} | 3 +- .../WhenConvertingToBools.cs | 3 +- .../WhenConvertingToInts.cs | 3 +- .../WhenProjectingFlatTypes.cs | 3 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 3 +- .../Infrastructure/InMemoryEf6TestContext.cs | 8 +++++ ...nition.cs => InMemoryEf6TestDefinition.cs} | 3 +- .../WhenConvertingToBools.cs | 3 +- .../WhenConvertingToInts.cs | 3 +- .../WhenConvertingToStrings.cs | 3 +- .../WhenProjectingFlatTypes.cs | 3 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 3 +- .../InMemoryEfCore2TestContext.cs | 8 +++++ ...on.cs => InMemoryEfCore2TestDefinition.cs} | 3 +- .../WhenConvertingToBools.cs | 3 +- .../WhenConvertingToInts.cs | 3 +- .../WhenConvertingToStrings.cs | 3 +- .../WhenProjectingFlatTypes.cs | 3 +- .../AgileMapper.UnitTests.Orms.csproj | 2 ++ .../Infrastructure/ITestContext.cs | 6 ++-- .../Infrastructure/ITestLocalDbContext.cs | 9 +++++ .../Infrastructure/InMemoryOrmTestContext.cs | 16 ++++----- .../Infrastructure/LocalDbTestContext.cs | 20 +++++++++++ .../Infrastructure/OrmTestClassBase.cs | 4 +-- .../WhenConvertingToBools.cs | 2 +- .../WhenConvertingToInts.cs | 2 +- .../WhenConvertingToStrings.cs | 2 +- AgileMapper.UnitTests.Orms/TestConstants.cs | 17 ++++++++++ .../WhenProjectingFlatTypes.cs | 2 +- 36 files changed, 126 insertions(+), 93 deletions(-) delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestContext.cs rename AgileMapper.UnitTests.Orms.Ef5/Infrastructure/{InMemoryOrmTestDefinition.cs => InMemoryEf5TestDefinition.cs} (60%) create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs rename AgileMapper.UnitTests.Orms.Ef6/Infrastructure/{InMemoryOrmTestDefinition.cs => InMemoryEf6TestDefinition.cs} (60%) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestContext.cs rename AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/{InMemoryOrmTestDefinition.cs => InMemoryEfCore2TestDefinition.cs} (60%) create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/ITestLocalDbContext.cs create mode 100644 AgileMapper.UnitTests.Orms/Infrastructure/LocalDbTestContext.cs diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj index fd592feea..fdd054a2a 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj +++ b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj @@ -76,7 +76,6 @@ - diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs deleted file mode 100644 index 79f044122..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestContext.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure -{ - using System.Data.Entity.Infrastructure; - using UnitTests.Orms.Infrastructure; - - public class Ef5LocalDbTestContext : ITestContext - { - private readonly Ef5TestDbContext _dbContext; - - public Ef5LocalDbTestContext() - { - var localDbConnectionFactory = - new SqlConnectionFactory(@"Data Source=(local);Integrated Security=True;MultipleActiveResultSets=True"); - - var localDbConnection = localDbConnectionFactory.CreateConnection("Ef5TestDb"); - - _dbContext = new Ef5TestDbContext(localDbConnection); - - _dbContext.Database.Create(); - } - - public TOrmContext GetDbContext() - where TOrmContext : ITestDbContext, new() - { - return (TOrmContext)(object)_dbContext; - } - - public void Dispose() - { - _dbContext.Database.Delete(); - _dbContext.Dispose(); - } - } -} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs index 74e8e46d4..c75112858 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs @@ -1,10 +1,11 @@ namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure { using UnitTests.Orms; + using UnitTests.Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class Ef5LocalDbTestDefinition : ICollectionFixture + public class Ef5LocalDbTestDefinition : ICollectionFixture> { } } \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index c9dc949f1..2cd2dff24 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -1,18 +1,17 @@ namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure { - using System.Data.Common; using System.Data.Entity; + using System.Data.SqlClient; + using UnitTests.Orms; using UnitTests.Orms.Infrastructure; using UnitTests.Orms.TestClasses; - public class Ef5TestDbContext : DbContext, ITestDbContext + public class Ef5TestDbContext : DbContext, ITestLocalDbContext { public Ef5TestDbContext() - { - } - - public Ef5TestDbContext(DbConnection localDbConnection) - : base(localDbConnection, false) + : base( + new SqlConnection(TestConstants.GetLocalDbConnectionString()), + true) { } @@ -53,5 +52,13 @@ IDbSetWrapper ITestDbContext.StringItems void ITestDbContext.SaveChanges() => SaveChanges(); #endregion + + #region ITestLocalDbContext Members + + void ITestLocalDbContext.CreateDatabase() => Database.Create(); + + void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); + + #endregion } } \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs index 29e038718..7527ce8f7 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,11 +1,12 @@ namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; + using UnitTests.Orms.Infrastructure; using UnitTests.Orms.SimpleTypeConversion; public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(Ef5LocalDbTestContext context) + public WhenConvertingToStrings(LocalDbTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index feb495bec..1bedf550c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,7 +89,8 @@ VersionInfo.cs - + + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestContext.cs new file mode 100644 index 000000000..fa834fc6b --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestContext.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure +{ + using Orms.Infrastructure; + + public class InMemoryEf5TestContext : InMemoryOrmTestContext + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestDefinition.cs similarity index 60% rename from AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs rename to AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestDefinition.cs index bf8256be1..cc28a6963 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryOrmTestDefinition.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/InMemoryEf5TestDefinition.cs @@ -1,11 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { using Orms; - using Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class InMemoryOrmTestDefinition : ICollectionFixture + public class InMemoryEf5TestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs index 0db240e61..ca106f8c6 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(InMemoryOrmTestContext context) + public WhenConvertingToBools(InMemoryEf5TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index 72946d0c3..6511a14ef 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(InMemoryOrmTestContext context) + public WhenConvertingToInts(InMemoryEf5TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs index 9b711ea90..60a239b9f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs @@ -2,11 +2,10 @@ { using Infrastructure; using Orms; - using Orms.Infrastructure; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(InMemoryOrmTestContext context) + public WhenProjectingFlatTypes(InMemoryEf5TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 2ec157e98..489162080 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,7 +91,8 @@ VersionInfo.cs - + + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs new file mode 100644 index 000000000..8a5b605a3 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure +{ + using Orms.Infrastructure; + + public class InMemoryEf6TestContext : InMemoryOrmTestContext + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestDefinition.cs similarity index 60% rename from AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs rename to AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestDefinition.cs index eb4eb0845..4093205ca 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryOrmTestDefinition.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestDefinition.cs @@ -1,11 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { using Orms; - using Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class InMemoryOrmTestDefinition : ICollectionFixture + public class InMemoryEf6TestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs index 73f1cdc38..fe128a71f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(InMemoryOrmTestContext context) + public WhenConvertingToBools(InMemoryEf6TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 4f2c8f47f..072a55630 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(InMemoryOrmTestContext context) + public WhenConvertingToInts(InMemoryEf6TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs index 778668943..327f8d648 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(InMemoryOrmTestContext context) + public WhenConvertingToStrings(InMemoryEf6TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs index cd340e8f0..9d39b1d0a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs @@ -2,11 +2,10 @@ { using Infrastructure; using Orms; - using Orms.Infrastructure; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(InMemoryOrmTestContext context) + public WhenProjectingFlatTypes(InMemoryEf6TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 5a365a561..e25c38f83 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -132,7 +132,8 @@ - + + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestContext.cs new file mode 100644 index 000000000..4e2ce39c7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestContext.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure +{ + using Orms.Infrastructure; + + public class InMemoryEfCore2TestContext : InMemoryOrmTestContext + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestDefinition.cs similarity index 60% rename from AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs rename to AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestDefinition.cs index 11b5de7bd..5c2125fce 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryOrmTestDefinition.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/InMemoryEfCore2TestDefinition.cs @@ -1,11 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure { using Orms; - using Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class InMemoryOrmTestDefinition : ICollectionFixture + public class InMemoryEfCore2TestDefinition : ICollectionFixture { } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs index f08d7e500..16dd78e09 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToBools : WhenConvertingToBools { - public WhenConvertingToBools(InMemoryOrmTestContext context) + public WhenConvertingToBools(InMemoryEfCore2TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 545a5d1ce..06ce28653 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToInts : WhenConvertingToInts { - public WhenConvertingToInts(InMemoryOrmTestContext context) + public WhenConvertingToInts(InMemoryEfCore2TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs index ee3828f9e..f9b77d730 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,12 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.Infrastructure; using Orms.SimpleTypeConversion; public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(InMemoryOrmTestContext context) + public WhenConvertingToStrings(InMemoryEfCore2TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs index 0bc43f815..e1e36a384 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs @@ -2,11 +2,10 @@ { using Infrastructure; using Orms; - using Orms.Infrastructure; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { - public WhenProjectingFlatTypes(InMemoryOrmTestContext context) + public WhenProjectingFlatTypes(InMemoryEfCore2TestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 52c4bfaa9..712eba7ba 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -80,6 +80,8 @@ + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs index 73b864460..81d20d626 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestContext.cs @@ -2,9 +2,9 @@ { using System; - public interface ITestContext : IDisposable + public interface ITestContext : IDisposable + where TOrmContext : ITestDbContext, new() { - TOrmContext GetDbContext() - where TOrmContext : ITestDbContext, new(); + TOrmContext DbContext { get; } } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestLocalDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestLocalDbContext.cs new file mode 100644 index 000000000..129f8b294 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestLocalDbContext.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + public interface ITestLocalDbContext : ITestDbContext + { + void CreateDatabase(); + + void DeleteDatabase(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs index 32624d73a..f17e70563 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/InMemoryOrmTestContext.cs @@ -1,20 +1,18 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { - using System; - - public class InMemoryOrmTestContext : ITestContext + public class InMemoryOrmTestContext : ITestContext + where TOrmContext : ITestDbContext, new() { - private IDisposable _dbContext; - - public TOrmContext GetDbContext() - where TOrmContext : ITestDbContext, new() + public InMemoryOrmTestContext() { - return (TOrmContext)(_dbContext ?? (_dbContext = new TOrmContext())); + DbContext = new TOrmContext(); } + public TOrmContext DbContext { get; } + public void Dispose() { - _dbContext?.Dispose(); + DbContext?.Dispose(); } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/LocalDbTestContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/LocalDbTestContext.cs new file mode 100644 index 000000000..026edba0c --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Infrastructure/LocalDbTestContext.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure +{ + public class LocalDbTestContext : ITestContext + where TOrmContext : ITestLocalDbContext, new() + { + public LocalDbTestContext() + { + DbContext = new TOrmContext(); + DbContext.CreateDatabase(); + } + + public TOrmContext DbContext { get; } + + public void Dispose() + { + DbContext.DeleteDatabase(); + DbContext.Dispose(); + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 8ec7ed3dd..626075f53 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -8,9 +8,9 @@ public abstract class OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected OrmTestClassBase(ITestContext context) + protected OrmTestClassBase(ITestContext context) { - Context = context.GetDbContext(); + Context = context.DbContext; } protected TOrmContext Context { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index cecd00a11..aae0cf3b0 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToBools : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToBools(ITestContext context) + protected WhenConvertingToBools(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index c196b4069..c9d1e3a33 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToInts : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToInts(ITestContext context) + protected WhenConvertingToInts(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index a3a7a9201..d4453d711 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -9,7 +9,7 @@ public abstract class WhenConvertingToStrings : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenConvertingToStrings(ITestContext context) + protected WhenConvertingToStrings(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms/TestConstants.cs b/AgileMapper.UnitTests.Orms/TestConstants.cs index d452f1a93..b36b0000c 100644 --- a/AgileMapper.UnitTests.Orms/TestConstants.cs +++ b/AgileMapper.UnitTests.Orms/TestConstants.cs @@ -1,7 +1,24 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { + using System; + public static class TestConstants { public const string OrmCollectionName = "ORM Collection"; + + public static string GetLocalDbConnectionString() + { + var dbName = typeof(TDbContext).Name; + + if (dbName.EndsWith("Context", StringComparison.Ordinal)) + { + dbName = dbName.Substring(0, dbName.Length - "Context".Length); + } + + return "Data Source=(local);" + + "Initial Catalog=" + dbName + ";" + + "Integrated Security=True;" + + "MultipleActiveResultSets=True"; + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 45cda7cfa..31e63a543 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -9,7 +9,7 @@ public abstract class WhenProjectingFlatTypes : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenProjectingFlatTypes(ITestContext context) + protected WhenProjectingFlatTypes(ITestContext context) : base(context) { } From 3b3a3975c37be991c19ee22fcd2b2cfef1dc7a34 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 11:44:21 +0000 Subject: [PATCH 027/176] Simplifying bool -> string conversion / Test coverage for bool -> string projection --- .../WhenConvertingToStrings.cs | 14 ++++++++++ .../TypeConversion/ToStringConverter.cs | 28 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index d4453d711..56c71d30c 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -27,5 +27,19 @@ public void ShouldProjectAnIntToAString() stringItem.Value.ShouldBe("763483"); }); } + + [Fact] + public void ShouldProjectABoolToAString() + { + RunTest(context => + { + context.BoolItems.Add(new PublicBoolProperty { Value = true }); + context.SaveChanges(); + + var stringItem = context.BoolItems.ProjectTo().First(); + + stringItem.Value.ShouldBe("true"); + }); + } } } diff --git a/AgileMapper/TypeConversion/ToStringConverter.cs b/AgileMapper/TypeConversion/ToStringConverter.cs index e76f1ebd7..1a1b3b4e2 100644 --- a/AgileMapper/TypeConversion/ToStringConverter.cs +++ b/AgileMapper/TypeConversion/ToStringConverter.cs @@ -32,6 +32,11 @@ public Expression GetConversion(Expression sourceValue) return GetDateTimeToStringConversion(sourceValue, nonNullableSourceType); } + if (nonNullableSourceType == typeof(bool)) + { + return GetBoolToStringConversion(sourceValue, nonNullableSourceType); + } + var toStringMethod = sourceValue.Type .GetPublicInstanceMethod("ToString", parameterCount: 0); @@ -82,5 +87,28 @@ public static MethodInfo GetToStringMethodOrNull(Type sourceType, Type argumentT return toStringMethod; } + + private static Expression GetBoolToStringConversion(Expression sourceValue, Type nonNullableSourceType) + { + if (sourceValue.Type == nonNullableSourceType) + { + return GetTrueOrFalseTernary(sourceValue); + } + + var nullTrueOrFalse = Expression.Condition( + Expression.Property(sourceValue, "HasValue"), + GetTrueOrFalseTernary(Expression.Property(sourceValue, "Value")), + typeof(string).ToDefaultExpression()); + + return nullTrueOrFalse; + } + + private static Expression GetTrueOrFalseTernary(Expression sourceValue) + { + return Expression.Condition( + sourceValue, + "true".ToConstantExpression(), + "false".ToConstantExpression()); + } } } \ No newline at end of file From 78a22b01ec6caf2226aa9de8687471f3e3681963 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 12:49:16 +0000 Subject: [PATCH 028/176] Updating EF5 integration tests project assembly info --- ...ileMapper.IntegrationTests.Orms.Ef5.csproj | 6 ++++ .../Properties/AssemblyInfo.cs | 35 ++----------------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj index fdd054a2a..42da15aff 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj +++ b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj @@ -74,6 +74,12 @@ + + CommonAssemblyInfo.cs + + + VersionInfo.cs + diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs index 0b952189e..7b1da0d46 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs @@ -1,36 +1,7 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AgileMapper.IntegrationTests.Orms.Ef5")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AgileMapper.IntegrationTests.Orms.Ef5")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] +[assembly: AssemblyTitle("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("48b855a9-f42c-4d2a-9549-97ed27d59336")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file From 8fbf4d2eb1169acc560f7165adf8da0ea68b5610 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 13:17:09 +0000 Subject: [PATCH 029/176] Adding EfCore1 unit tests project --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 227 ++++++++++++++++++ .../Infrastructure/EfCore1DbSetWrapper.cs | 6 +- .../Infrastructure/EfCore1TestDbContext.cs | 30 +-- .../InMemoryEfCore1TestContext.cs | 8 + .../InMemoryEfCore1TestDefinition.cs | 10 + .../Properties/AssemblyInfo.cs | 36 +++ .../WhenConvertingToBools.cs | 13 + .../WhenConvertingToInts.cs | 13 + .../WhenConvertingToStrings.cs | 13 + .../WhenProjectingFlatTypes.cs | 13 + AgileMapper.UnitTests.Orms.EfCore1/app.config | 31 +++ .../packages.config | 73 ++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 6 + .../Infrastructure/EfCore2TestDbContext.cs | 2 +- AgileMapper.sln | 6 + .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 120 --------- .../Infrastructure/TestContextCollection.cs | 11 - .../WhenProjectingFlatTypes.cs | 14 -- .../packages.config | 12 - 19 files changed, 469 insertions(+), 175 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj rename _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs => AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs (66%) rename _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs => AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs (55%) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestContext.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestDefinition.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToBools.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/app.config create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/packages.config delete mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj delete mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs delete mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs delete mode 100644 _AgileMapper.UnitTests.Orms.EfCore2/packages.config diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj new file mode 100644 index 000000000..05dbff232 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -0,0 +1,227 @@ + + + + + + + Debug + AnyCPU + {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B} + Library + Properties + AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 + AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 + v4.6.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + ..\AgileMapper.snk + + + + ..\packages\Microsoft.EntityFrameworkCore.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.dll + + + ..\packages\Microsoft.EntityFrameworkCore.InMemory.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.InMemory.dll + + + ..\packages\Microsoft.Extensions.Caching.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Caching.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Caching.Memory.1.1.1\lib\net451\Microsoft.Extensions.Caching.Memory.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.1.1.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Options.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Options.dll + + + ..\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + + + ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll + True + + + ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + + + + ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll + True + + + ..\packages\System.Collections.Immutable.1.3.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + True + + + ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll + + + + + ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll + + + ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll + + + ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll + + + ..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll + + + ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll + + + ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll + True + + + + ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll + + + ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll + + + ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll + + + ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + + ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll + + + + ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll + + + ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll + + + ..\packages\System.Runtime.InteropServices.4.3.0\lib\net462\System.Runtime.InteropServices.dll + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + True + + + ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll + + + ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll + + + ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll + + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + + + + ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + + Designer + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs similarity index 66% rename from _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs rename to AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs index 3558e4c45..966519102 100644 --- a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs @@ -1,14 +1,14 @@ -namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure { using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; - public class EfCore2DbSetWrapper : DbSetWrapperBase + public class EfCore1DbSetWrapper : DbSetWrapperBase where TEntity : class { private readonly DbSet _dbSet; - public EfCore2DbSetWrapper(DbSet dbSet) + public EfCore1DbSetWrapper(DbSet dbSet) : base(dbSet) { _dbSet = dbSet; diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs similarity index 55% rename from _AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs rename to AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 70b57a224..0be5648e7 100644 --- a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -1,18 +1,18 @@ -namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure { using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; - using Orms.TestClasses; + using TestClasses; - public class EfCore2TestDbContext : DbContext, ITestDbContext + public class EfCore1TestDbContext : DbContext, ITestDbContext { - private static readonly DbContextOptions _inMemoryDbOptions = - new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "EfCore2TestDb") + private static readonly DbContextOptions _inMemoryOptions = + new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "EfCore1TestDb") .Options; - public EfCore2TestDbContext() - : base(_inMemoryDbOptions) + public EfCore1TestDbContext() + : base(_inMemoryOptions) { } @@ -30,23 +30,25 @@ public EfCore2TestDbContext() #region ITestDbContext Members + public bool StringParsingSupported => true; + IDbSetWrapper ITestDbContext.Products - => new EfCore2DbSetWrapper(Products); + => new EfCore1DbSetWrapper(Products); IDbSetWrapper ITestDbContext.BoolItems - => new EfCore2DbSetWrapper(BoolItems); + => new EfCore1DbSetWrapper(BoolItems); IDbSetWrapper ITestDbContext.ShortItems - => new EfCore2DbSetWrapper(ShortItems); + => new EfCore1DbSetWrapper(ShortItems); IDbSetWrapper ITestDbContext.IntItems - => new EfCore2DbSetWrapper(IntItems); + => new EfCore1DbSetWrapper(IntItems); IDbSetWrapper ITestDbContext.LongItems - => new EfCore2DbSetWrapper(LongItems); + => new EfCore1DbSetWrapper(LongItems); IDbSetWrapper ITestDbContext.StringItems - => new EfCore2DbSetWrapper(StringItems); + => new EfCore1DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestContext.cs new file mode 100644 index 000000000..e7b66ca98 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestContext.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure +{ + using Orms.Infrastructure; + + public class InMemoryEfCore1TestContext : InMemoryOrmTestContext + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestDefinition.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestDefinition.cs new file mode 100644 index 000000000..d2ba98aad --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/InMemoryEfCore1TestDefinition.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure +{ + using Orms; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class InMemoryEfCore1TestDefinition : ICollectionFixture + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fa9cb5e39 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AgileMapper.UnitTests.Orms.EfCore1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AgileMapper.UnitTests.Orms.EfCore1")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToBools.cs new file mode 100644 index 000000000..1ab6d378d --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToBools.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToBools : WhenConvertingToBools + { + public WhenConvertingToBools(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs new file mode 100644 index 000000000..67a5355f7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToInts : WhenConvertingToInts + { + public WhenConvertingToInts(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs new file mode 100644 index 000000000..9a05e7ca8 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToStrings : WhenConvertingToStrings + { + public WhenConvertingToStrings(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs new file mode 100644 index 000000000..3a5b17561 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingFlatTypes : WhenProjectingFlatTypes + { + public WhenProjectingFlatTypes(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config new file mode 100644 index 000000000..b0cf98482 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config new file mode 100644 index 000000000..a591dbc22 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index e25c38f83..61cb44958 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -40,6 +40,12 @@ prompt 4 + + true + + + ..\AgileMapper.snk + ..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 7b9fca957..bcf1fc2d0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -11,7 +11,7 @@ public class EfCore2TestDbContext : DbContext, ITestDbContext private static readonly DbContextOptions _inMemoryOptions = new DbContextOptionsBuilder() .UseLoggerFactory(new LoggerFactory(new[] { new DebugLoggerProvider() })) - .UseInMemoryDatabase(databaseName: "Ef6TestDbContext") + .UseInMemoryDatabase(databaseName: "EfCore2TestDb") .Options; public EfCore2TestDbContext() diff --git a/AgileMapper.sln b/AgileMapper.sln index 038de98f7..65e22f256 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTests.Orms.Ef5", "AgileMapper.IntegrationTests.Orms.Ef5\AgileMapper.IntegrationTests.Orms.Ef5.csproj", "{48B855A9-F42C-4D2A-9549-97ED27D59336}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore1", "AgileMapper.UnitTests.Orms.EfCore1\AgileMapper.UnitTests.Orms.EfCore1.csproj", "{FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +80,10 @@ Global {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.Build.0 = Debug|Any CPU {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.ActiveCfg = Release|Any CPU {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.Build.0 = Release|Any CPU + {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj deleted file mode 100644 index 2ccccb0d3..000000000 --- a/_AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - Debug - AnyCPU - {9B9C6E6B-6C7C-414F-A162-816BBD6F0521} - Library - Properties - AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 - AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 - v4.6.2 - 512 - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - true - - - ..\AgileMapper.snk - - - - ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll - - - - - - ..\packages\System.Runtime.4.1.0\lib\net462\System.Runtime.dll - True - - - - - - - - - ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll - - - ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll - - - ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll - - - ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll - - - - - CommonAssemblyInfo.cs - - - VersionInfo.cs - - - - - - - - - - - - {66522d44-19f5-4af5-9d43-483a3cd6f958} - AgileMapper.UnitTests.Orms - - - {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} - AgileMapper - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs b/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs deleted file mode 100644 index 92ab1fd6d..000000000 --- a/_AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/TestContextCollection.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.EfCore2.Infrastructure -{ - using Orms; - using Orms.Infrastructure; - using Xunit; - - [CollectionDefinition(TestConstants.OrmCollectionName)] - public class TestContextCollection : ICollectionFixture - { - } -} \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs deleted file mode 100644 index c5f514911..000000000 --- a/_AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.EfCore2 -{ - using Infrastructure; - using Orms; - using Orms.Infrastructure; - - public class WhenProjectingFlatTypes : WhenProjectingFlatTypes - { - public WhenProjectingFlatTypes(TestContext context) - : base(context) - { - } - } -} \ No newline at end of file diff --git a/_AgileMapper.UnitTests.Orms.EfCore2/packages.config b/_AgileMapper.UnitTests.Orms.EfCore2/packages.config deleted file mode 100644 index 3240e2e11..000000000 --- a/_AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file From 915a3f4366133ad9ed7bbcd89b8caf55c7f5dc1e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 13:20:24 +0000 Subject: [PATCH 030/176] Updating NuGet package versions --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 61 ++++++++++--------- AgileMapper.UnitTests.Orms.EfCore1/app.config | 32 ++++++++++ .../packages.config | 34 +++++------ 3 files changed, 80 insertions(+), 47 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 05dbff232..83fe47943 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -53,45 +53,44 @@ ..\packages\Microsoft.EntityFrameworkCore.InMemory.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.InMemory.dll - - ..\packages\Microsoft.Extensions.Caching.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Caching.Abstractions.dll + + ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll - - ..\packages\Microsoft.Extensions.Caching.Memory.1.1.1\lib\net451\Microsoft.Extensions.Caching.Memory.dll + + ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.1.1.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - ..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll + + ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll - - ..\packages\Microsoft.Extensions.Options.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Options.dll + + ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll - - ..\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + + ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll True - ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll True - - ..\packages\System.Collections.Immutable.1.3.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - True + + ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll @@ -101,8 +100,8 @@ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll - - ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll @@ -110,8 +109,8 @@ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll - - ..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll + + ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll @@ -130,8 +129,9 @@ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + ..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll + True ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll @@ -143,8 +143,8 @@ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll @@ -165,8 +165,9 @@ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.1\lib\net461\System.Security.Cryptography.X509Certificates.dll + True diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index b0cf98482..7a42cad2e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -26,6 +26,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index a591dbc22..59d8336ad 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -2,31 +2,31 @@ - - - - - - - - - + + + + + + + + + - + - + - + - + @@ -34,8 +34,8 @@ - - + + @@ -44,7 +44,7 @@ - + @@ -53,7 +53,7 @@ - + From 8f13b7ea9e3e1c13193bf7e0d1298a401a43f3c5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:11:35 +0000 Subject: [PATCH 031/176] Revert "Updating NuGet package versions" This reverts commit 915a3f4366133ad9ed7bbcd89b8caf55c7f5dc1e. --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 61 +++++++++---------- AgileMapper.UnitTests.Orms.EfCore1/app.config | 32 ---------- .../packages.config | 34 +++++------ 3 files changed, 47 insertions(+), 80 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 83fe47943..05dbff232 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -53,44 +53,45 @@ ..\packages\Microsoft.EntityFrameworkCore.InMemory.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.InMemory.dll - - ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll + + ..\packages\Microsoft.Extensions.Caching.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Caching.Abstractions.dll - - ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll + + ..\packages\Microsoft.Extensions.Caching.Memory.1.1.1\lib\net451\Microsoft.Extensions.Caching.Memory.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.1.1.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll + + ..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll - - ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll + + ..\packages\Microsoft.Extensions.Options.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Options.dll - - ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + + ..\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll True - ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll + ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll True - - ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll + + ..\packages\System.Collections.Immutable.1.3.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + True ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll @@ -100,8 +101,8 @@ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll - - ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll @@ -109,8 +110,8 @@ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll - - ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll + + ..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll @@ -129,9 +130,8 @@ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - ..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll - True + + ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll @@ -143,8 +143,8 @@ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll @@ -165,9 +165,8 @@ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.1\lib\net461\System.Security.Cryptography.X509Certificates.dll - True + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index 7a42cad2e..b0cf98482 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -26,38 +26,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 59d8336ad..a591dbc22 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -2,31 +2,31 @@ - - - - - - - - - + + + + + + + + + - + - + - + - + @@ -34,8 +34,8 @@ - - + + @@ -44,7 +44,7 @@ - + @@ -53,7 +53,7 @@ - + From 4993acc61773fd90b2796cd102589304f88785c0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:15:46 +0000 Subject: [PATCH 032/176] Updating NuGet package versions - 1 --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 28 +++++++++---------- AgileMapper.UnitTests.Orms.EfCore1/app.config | 20 +++++++++++++ .../packages.config | 14 +++++----- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 05dbff232..fb188f5c5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -53,29 +53,29 @@ ..\packages\Microsoft.EntityFrameworkCore.InMemory.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.InMemory.dll - - ..\packages\Microsoft.Extensions.Caching.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Caching.Abstractions.dll + + ..\packages\Microsoft.Extensions.Caching.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Abstractions.dll - - ..\packages\Microsoft.Extensions.Caching.Memory.1.1.1\lib\net451\Microsoft.Extensions.Caching.Memory.dll + + ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll ..\packages\Microsoft.Extensions.DependencyInjection.1.1.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.0\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll ..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll - - ..\packages\Microsoft.Extensions.Logging.Abstractions.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + + ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll - - ..\packages\Microsoft.Extensions.Options.1.1.1\lib\netstandard1.0\Microsoft.Extensions.Options.dll + + ..\packages\Microsoft.Extensions.Options.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll - - ..\packages\Microsoft.Extensions.Primitives.1.1.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + + ..\packages\Microsoft.Extensions.Primitives.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll @@ -143,8 +143,8 @@ ..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.4.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index b0cf98482..6f8f112cb 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -26,6 +26,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index a591dbc22..b5ebc3761 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -2,14 +2,14 @@ - - + + - + - - - + + + @@ -44,7 +44,7 @@ - + From 4cefef5e4df91f59fd5f1baae73374543bc6cc25 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:17:04 +0000 Subject: [PATCH 033/176] Updating NuGet package versions - 2 --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 8 ++++---- AgileMapper.UnitTests.Orms.EfCore1/app.config | 6 +++++- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index fb188f5c5..53e48a896 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -65,8 +65,8 @@ ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll - - ..\packages\Microsoft.Extensions.Logging.1.1.1\lib\netstandard1.1\Microsoft.Extensions.Logging.dll + + ..\packages\Microsoft.Extensions.Logging.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.dll ..\packages\Microsoft.Extensions.Logging.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll @@ -101,8 +101,8 @@ ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll - - ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + ..\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll ..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index 6f8f112cb..cc35cd5de 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -24,7 +24,7 @@ - + @@ -46,6 +46,10 @@ + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index b5ebc3761..3f443bcb2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -6,7 +6,7 @@ - + @@ -21,7 +21,7 @@ - + From af11d974a19ed83e3f357410a11cc183e0cdfe9b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:18:18 +0000 Subject: [PATCH 034/176] Updating NuGet package versions - 3 --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 9 ++++----- AgileMapper.UnitTests.Orms.EfCore1/app.config | 8 ++++++++ AgileMapper.UnitTests.Orms.EfCore1/packages.config | 4 ++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 53e48a896..7a6765043 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -89,9 +89,8 @@ ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll True - - ..\packages\System.Collections.Immutable.1.3.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - True + + ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll @@ -110,8 +109,8 @@ ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll - - ..\packages\System.Interactive.Async.3.0.0\lib\net45\System.Interactive.Async.dll + + ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index cc35cd5de..464944a2d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -50,6 +50,14 @@ + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 3f443bcb2..033f5c5e9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -17,7 +17,7 @@ - + @@ -26,7 +26,7 @@ - + From a17f0e8825f550ed18078f5b88edb0f0be2ad654 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:21:29 +0000 Subject: [PATCH 035/176] Updating NuGet package versions - 4 --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 6 +++--- AgileMapper.UnitTests.Orms.EfCore1/app.config | 4 ++++ AgileMapper.UnitTests.Orms.EfCore1/packages.config | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 7a6765043..b95bf7761 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -59,8 +59,8 @@ ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll - - ..\packages\Microsoft.Extensions.DependencyInjection.1.1.0\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll + + ..\packages\Microsoft.Extensions.DependencyInjection.1.1.1\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll @@ -82,7 +82,7 @@ True - ..\packages\Remotion.Linq.2.1.1\lib\net45\Remotion.Linq.dll + ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index 464944a2d..d1cf0bd97 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -58,6 +58,10 @@ + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 033f5c5e9..fe8d68cfd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -4,7 +4,7 @@ - + @@ -13,7 +13,7 @@ - + @@ -34,7 +34,7 @@ - + From a7e2c388dcb477eff1955b2ce4c1626c1c1a9f5f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 14:25:31 +0000 Subject: [PATCH 036/176] Consolidating NETStandard.Library package versions --- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index fe8d68cfd..2e7928658 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -12,7 +12,7 @@ - + From d2f2f15f86aa91f02d62a4bc42b273c10623edc9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 16:48:53 +0000 Subject: [PATCH 037/176] Support for string -> DateTime conversion / Adding EF6 integration tests project --- ...ileMapper.IntegrationTests.Orms.Ef5.csproj | 1 + .../Infrastructure/Ef5TestDbContext.cs | 32 ++--- .../WhenConvertingToDateTimes.cs | 14 +++ ...ileMapper.IntegrationTests.Orms.Ef6.csproj | 114 ++++++++++++++++++ .../App.config | 17 +++ .../Infrastructure/Ef6DbSetWrapper.cs | 21 ++++ .../Ef6LocalDbTestDefinition.cs | 11 ++ .../Infrastructure/Ef6TestDbContext.cs | 64 ++++++++++ .../Properties/AssemblyInfo.cs | 7 ++ .../WhenConvertingToDateTimes.cs | 14 +++ .../packages.config | 12 ++ .../Infrastructure/Ef5TestDbContext.cs | 32 ++--- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Infrastructure/Ef6TestDbContext.cs | 32 ++--- .../Infrastructure/InMemoryEf6TestContext.cs | 7 ++ .../WhenConvertingToDateTimes.cs | 13 ++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../Infrastructure/EfCore1TestDbContext.cs | 32 ++--- .../WhenConvertingToDateTimes.cs | 13 ++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 32 ++--- .../WhenConvertingToDateTimes.cs | 13 ++ .../AgileMapper.UnitTests.Orms.csproj | 18 +-- .../Infrastructure/ITestDbContext.cs | 12 +- .../WhenConvertingToBools.cs | 36 +++--- .../WhenConvertingToDateTimes.cs | 38 ++++++ .../WhenConvertingToInts.cs | 26 ++-- .../WhenConvertingToStrings.cs | 8 +- .../{PublicBoolProperty.cs => PublicBool.cs} | 2 +- ...licBoolPropertyDto.cs => PublicBoolDto.cs} | 2 +- .../TestClasses/PublicDateTimeDto.cs | 12 ++ .../{PublicIntProperty.cs => PublicInt.cs} | 2 +- ...ublicIntPropertyDto.cs => PublicIntDto.cs} | 2 +- .../{PublicLongProperty.cs => PublicLong.cs} | 2 +- ...{PublicShortProperty.cs => PublicShort.cs} | 2 +- ...ublicStringProperty.cs => PublicString.cs} | 2 +- ...tringPropertyDto.cs => PublicStringDto.cs} | 2 +- AgileMapper.sln | 6 + .../Settings/DefaultQueryProviderSettings.cs | 40 +++++- .../Settings/Ef5QueryProviderSettings.cs | 25 ++-- .../Settings/Ef6QueryProviderSettings.cs | 18 +++ .../Settings/IQueryProviderSettings.cs | 10 +- .../QueryProviderSettingsExtensions.cs | 89 ++++++++++++++ AgileMapper/Queryables/ToStringConverter.cs | 2 +- .../Queryables/TryParseAssignmentConverter.cs | 21 +--- 45 files changed, 693 insertions(+), 168 deletions(-) create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/App.config create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/packages.config create mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs rename AgileMapper.UnitTests.Orms/TestClasses/{PublicBoolProperty.cs => PublicBool.cs} (85%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicBoolPropertyDto.cs => PublicBoolDto.cs} (80%) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicDateTimeDto.cs rename AgileMapper.UnitTests.Orms/TestClasses/{PublicIntProperty.cs => PublicInt.cs} (86%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicIntPropertyDto.cs => PublicIntDto.cs} (80%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicLongProperty.cs => PublicLong.cs} (86%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicShortProperty.cs => PublicShort.cs} (85%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicStringProperty.cs => PublicString.cs} (85%) rename AgileMapper.UnitTests.Orms/TestClasses/{PublicStringPropertyDto.cs => PublicStringDto.cs} (79%) create mode 100644 AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj index 42da15aff..c28c4518d 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj +++ b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj @@ -84,6 +84,7 @@ + diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 2cd2dff24..5687c1402 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -17,37 +17,37 @@ public Ef5TestDbContext() public DbSet Products { get; set; } - public DbSet BoolItems { get; set; } + public DbSet BoolItems { get; set; } - public DbSet ShortItems { get; set; } + public DbSet ShortItems { get; set; } - public DbSet IntItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet LongItems { get; set; } + public DbSet LongItems { get; set; } - public DbSet StringItems { get; set; } + public DbSet StringItems { get; set; } #region ITestDbContext Members - public bool StringParsingSupported => false; + public bool StringToNumberConversionSupported => false; IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); - IDbSetWrapper ITestDbContext.BoolItems - => new Ef5DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems + => new Ef5DbSetWrapper(BoolItems); - IDbSetWrapper ITestDbContext.ShortItems - => new Ef5DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems + => new Ef5DbSetWrapper(ShortItems); - IDbSetWrapper ITestDbContext.IntItems - => new Ef5DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems + => new Ef5DbSetWrapper(IntItems); - IDbSetWrapper ITestDbContext.LongItems - => new Ef5DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems + => new Ef5DbSetWrapper(LongItems); - IDbSetWrapper ITestDbContext.StringItems - => new Ef5DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems + => new Ef5DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..240597272 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using UnitTests.Orms.Infrastructure; + using UnitTests.Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(LocalDbTestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj b/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj new file mode 100644 index 000000000..3e11e9f87 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj @@ -0,0 +1,114 @@ + + + + + + Debug + AnyCPU + {B75D1A61-006A-4951-82FE-A2943296A872} + Library + Properties + AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6 + AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6 + v4.6.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + + + ..\AgileMapper.snk + + + + ..\packages\AgileObjects.NetStandardPolyfills.1.0.0\lib\net40\AgileObjects.NetStandardPolyfills.dll + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + + + + + + + + + + + ..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll + + + + + CommonAssemblyInfo.cs + + + VersionInfo.cs + + + + + + + + + + {66522d44-19f5-4af5-9d43-483a3cd6f958} + AgileMapper.UnitTests.Orms + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/App.config b/AgileMapper.IntegrationTests.Orms.Ef6/App.config new file mode 100644 index 000000000..a653955ca --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/App.config @@ -0,0 +1,17 @@ + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs new file mode 100644 index 000000000..041b853d9 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +{ + using System.Data.Entity; + using UnitTests.Orms.Infrastructure; + + public class Ef6DbSetWrapper : DbSetWrapperBase + where TEntity : class + { + private readonly DbSet _dbSet; + + public Ef6DbSetWrapper(DbSet dbSet) + : base(dbSet) + { + _dbSet = dbSet; + } + + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + + public override void Clear() => _dbSet.RemoveRange(_dbSet); + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs new file mode 100644 index 000000000..302167d45 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +{ + using UnitTests.Orms; + using UnitTests.Orms.Infrastructure; + using Xunit; + + [CollectionDefinition(TestConstants.OrmCollectionName)] + public class Ef6LocalDbTestDefinition : ICollectionFixture> + { + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs new file mode 100644 index 000000000..62a104ca2 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -0,0 +1,64 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +{ + using System.Data.Entity; + using System.Data.SqlClient; + using UnitTests.Orms; + using UnitTests.Orms.Infrastructure; + using UnitTests.Orms.TestClasses; + + public class Ef6TestDbContext : DbContext, ITestLocalDbContext + { + public Ef6TestDbContext() + : base( + new SqlConnection(TestConstants.GetLocalDbConnectionString()), + true) + { + } + + public DbSet Products { get; set; } + + public DbSet BoolItems { get; set; } + + public DbSet ShortItems { get; set; } + + public DbSet IntItems { get; set; } + + public DbSet LongItems { get; set; } + + public DbSet StringItems { get; set; } + + #region ITestDbContext Members + + public bool StringToNumberConversionSupported => false; + + IDbSetWrapper ITestDbContext.Products + => new Ef6DbSetWrapper(Products); + + IDbSetWrapper ITestDbContext.BoolItems + => new Ef6DbSetWrapper(BoolItems); + + IDbSetWrapper ITestDbContext.ShortItems + => new Ef6DbSetWrapper(ShortItems); + + IDbSetWrapper ITestDbContext.IntItems + => new Ef6DbSetWrapper(IntItems); + + IDbSetWrapper ITestDbContext.LongItems + => new Ef6DbSetWrapper(LongItems); + + IDbSetWrapper ITestDbContext.StringItems + => new Ef6DbSetWrapper(StringItems); + + void ITestDbContext.SaveChanges() => SaveChanges(); + + #endregion + + #region ITestLocalDbContext Members + + void ITestLocalDbContext.CreateDatabase() => Database.Create(); + + void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7b1da0d46 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] + +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..c4fda4432 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using UnitTests.Orms.Infrastructure; + using UnitTests.Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(LocalDbTestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/packages.config b/AgileMapper.IntegrationTests.Orms.Ef6/packages.config new file mode 100644 index 000000000..3222059d0 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index a2f33d111..bc95cc6d7 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -14,37 +14,37 @@ public Ef5TestDbContext() public DbSet Products { get; set; } - public DbSet BoolItems { get; set; } + public DbSet BoolItems { get; set; } - public DbSet ShortItems { get; set; } + public DbSet ShortItems { get; set; } - public DbSet IntItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet LongItems { get; set; } + public DbSet LongItems { get; set; } - public DbSet StringItems { get; set; } + public DbSet StringItems { get; set; } #region ITestDbContext Members - public bool StringParsingSupported => false; + public bool StringToNumberConversionSupported => false; IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); - IDbSetWrapper ITestDbContext.BoolItems - => new Ef5DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems + => new Ef5DbSetWrapper(BoolItems); - IDbSetWrapper ITestDbContext.ShortItems - => new Ef5DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems + => new Ef5DbSetWrapper(ShortItems); - IDbSetWrapper ITestDbContext.IntItems - => new Ef5DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems + => new Ef5DbSetWrapper(IntItems); - IDbSetWrapper ITestDbContext.LongItems - => new Ef5DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems + => new Ef5DbSetWrapper(LongItems); - IDbSetWrapper ITestDbContext.StringItems - => new Ef5DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems + => new Ef5DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 489162080..24465c3f9 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -97,6 +97,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index a45898b88..3767772d9 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -14,37 +14,37 @@ public Ef6TestDbContext() public DbSet Products { get; set; } - public DbSet BoolItems { get; set; } + public DbSet BoolItems { get; set; } - public DbSet ShortItems { get; set; } + public DbSet ShortItems { get; set; } - public DbSet IntItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet LongItems { get; set; } + public DbSet LongItems { get; set; } - public DbSet StringItems { get; set; } + public DbSet StringItems { get; set; } #region ITestDbContext Members - public bool StringParsingSupported => false; + public bool StringToNumberConversionSupported => false; IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); - IDbSetWrapper ITestDbContext.BoolItems - => new Ef6DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems + => new Ef6DbSetWrapper(BoolItems); - IDbSetWrapper ITestDbContext.ShortItems - => new Ef6DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems + => new Ef6DbSetWrapper(ShortItems); - IDbSetWrapper ITestDbContext.IntItems - => new Ef6DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems + => new Ef6DbSetWrapper(IntItems); - IDbSetWrapper ITestDbContext.LongItems - => new Ef6DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems + => new Ef6DbSetWrapper(LongItems); - IDbSetWrapper ITestDbContext.StringItems - => new Ef6DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems + => new Ef6DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs index 8a5b605a3..25bbb61b7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs @@ -1,8 +1,15 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { + using System.Data.Entity.SqlServer; using Orms.Infrastructure; public class InMemoryEf6TestContext : InMemoryOrmTestContext { + public InMemoryEf6TestContext() + { + // ReSharper disable once UnusedVariable + // Touch SqlFunctions to load System.Data.Entity into the AppDomain: + //var functionsType = typeof(SqlFunctions); + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..343734bdb --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index b95bf7761..e06b31f5b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -208,6 +208,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 0be5648e7..c7a422c05 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -18,37 +18,37 @@ public EfCore1TestDbContext() public DbSet Products { get; set; } - public DbSet BoolItems { get; set; } + public DbSet BoolItems { get; set; } - public DbSet ShortItems { get; set; } + public DbSet ShortItems { get; set; } - public DbSet IntItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet LongItems { get; set; } + public DbSet LongItems { get; set; } - public DbSet StringItems { get; set; } + public DbSet StringItems { get; set; } #region ITestDbContext Members - public bool StringParsingSupported => true; + public bool StringToNumberConversionSupported => true; IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); - IDbSetWrapper ITestDbContext.BoolItems - => new EfCore1DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems + => new EfCore1DbSetWrapper(BoolItems); - IDbSetWrapper ITestDbContext.ShortItems - => new EfCore1DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems + => new EfCore1DbSetWrapper(ShortItems); - IDbSetWrapper ITestDbContext.IntItems - => new EfCore1DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems + => new EfCore1DbSetWrapper(IntItems); - IDbSetWrapper ITestDbContext.LongItems - => new EfCore1DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems + => new EfCore1DbSetWrapper(LongItems); - IDbSetWrapper ITestDbContext.StringItems - => new EfCore1DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems + => new EfCore1DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..dcfdfa687 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 61cb44958..72a568cc0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -143,6 +143,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index bcf1fc2d0..f5960e9c8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -21,37 +21,37 @@ public EfCore2TestDbContext() public DbSet Products { get; set; } - public DbSet BoolItems { get; set; } + public DbSet BoolItems { get; set; } - public DbSet ShortItems { get; set; } + public DbSet ShortItems { get; set; } - public DbSet IntItems { get; set; } + public DbSet IntItems { get; set; } - public DbSet LongItems { get; set; } + public DbSet LongItems { get; set; } - public DbSet StringItems { get; set; } + public DbSet StringItems { get; set; } #region ITestDbContext Members - public bool StringParsingSupported => true; + public bool StringToNumberConversionSupported => true; IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); - IDbSetWrapper ITestDbContext.BoolItems - => new EfCore2DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems + => new EfCore2DbSetWrapper(BoolItems); - IDbSetWrapper ITestDbContext.ShortItems - => new EfCore2DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems + => new EfCore2DbSetWrapper(ShortItems); - IDbSetWrapper ITestDbContext.IntItems - => new EfCore2DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems + => new EfCore2DbSetWrapper(IntItems); - IDbSetWrapper ITestDbContext.LongItems - => new EfCore2DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems + => new EfCore2DbSetWrapper(LongItems); - IDbSetWrapper ITestDbContext.StringItems - => new EfCore2DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems + => new EfCore2DbSetWrapper(StringItems); void ITestDbContext.SaveChanges() => SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..12a75c03c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 712eba7ba..24932cac9 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -83,8 +83,10 @@ + - + + @@ -92,13 +94,13 @@ - - - - - - - + + + + + + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index e1b0220bc..f23f165b5 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -5,19 +5,19 @@ public interface ITestDbContext : IDisposable { - bool StringParsingSupported { get; } + bool StringToNumberConversionSupported { get; } IDbSetWrapper Products { get; } - IDbSetWrapper BoolItems { get; } + IDbSetWrapper BoolItems { get; } - IDbSetWrapper ShortItems { get; } + IDbSetWrapper ShortItems { get; } - IDbSetWrapper IntItems { get; } + IDbSetWrapper IntItems { get; } - IDbSetWrapper LongItems { get; } + IDbSetWrapper LongItems { get; } - IDbSetWrapper StringItems { get; } + IDbSetWrapper StringItems { get; } void SaveChanges(); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index aae0cf3b0..dab12cd34 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -19,10 +19,10 @@ public void ShouldProjectAnIntOneToTrue() { RunTest(context => { - context.IntItems.Add(new PublicIntProperty { Value = 1 }); + context.IntItems.Add(new PublicInt { Value = 1 }); context.SaveChanges(); - var boolItem = context.IntItems.ProjectTo().First(); + var boolItem = context.IntItems.ProjectTo().First(); boolItem.Value.ShouldBeTrue(); }); @@ -33,10 +33,10 @@ public void ShouldProjectAnIntZeroToFalse() { RunTest(context => { - context.IntItems.Add(new PublicIntProperty { Value = 0 }); + context.IntItems.Add(new PublicInt { Value = 0 }); context.SaveChanges(); - var boolItem = context.IntItems.ProjectTo().First(); + var boolItem = context.IntItems.ProjectTo().First(); boolItem.Value.ShouldBeFalse(); }); @@ -47,10 +47,10 @@ public void ShouldProjectAStringTrueToTrue() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "true" }); + context.StringItems.Add(new PublicString { Value = "true" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeTrue(); }); @@ -61,10 +61,10 @@ public void ShouldProjectAStringTrueToTrueIgnoringCase() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "tRuE" }); + context.StringItems.Add(new PublicString { Value = "tRuE" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeTrue(); }); @@ -75,10 +75,10 @@ public void ShouldProjectAStringOneToTrue() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "1" }); + context.StringItems.Add(new PublicString { Value = "1" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeTrue(); }); @@ -89,10 +89,10 @@ public void ShouldProjectAStringFalseToFalse() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "false" }); + context.StringItems.Add(new PublicString { Value = "false" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeFalse(); }); @@ -103,10 +103,10 @@ public void ShouldProjectAStringZeroToFalse() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "0" }); + context.StringItems.Add(new PublicString { Value = "0" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeFalse(); }); @@ -117,10 +117,10 @@ public void ShouldProjectAStringNonBooleanValueToFalse() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = "uokyujhygt" }); + context.StringItems.Add(new PublicString { Value = "uokyujhygt" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeFalse(); }); @@ -131,10 +131,10 @@ public void ShouldProjectAStringNullToFalse() { RunTest(context => { - context.StringItems.Add(new PublicStringProperty { Value = null }); + context.StringItems.Add(new PublicString { Value = null }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.ProjectTo().First(); boolItem.Value.ShouldBeFalse(); }); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..913a94c77 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System; + using System.Globalization; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToDateTimes : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToDateTimes(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAParseableStringToADateTimeAsExpected() + { + void Test(TOrmContext context) + { + var now = DateTime.Now; + var nowString = now.ToString(CultureInfo.InvariantCulture); + + context.StringItems.Add(new PublicString { Value = nowString }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ToString(CultureInfo.InvariantCulture).ShouldBe(nowString); + } + + RunTest(Test); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index c9d1e3a33..ab4e5904c 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -19,10 +19,10 @@ public void ShouldProjectAShortToAnInt() { RunTest(context => { - context.ShortItems.Add(new PublicShortProperty { Value = 123 }); + context.ShortItems.Add(new PublicShort { Value = 123 }); context.SaveChanges(); - var intItem = context.ShortItems.ProjectTo().First(); + var intItem = context.ShortItems.ProjectTo().First(); intItem.Value.ShouldBe(123); }); @@ -33,10 +33,10 @@ public void ShouldProjectAnInRangeLongToAnInt() { RunTest(context => { - context.LongItems.Add(new PublicLongProperty { Value = 12345L }); + context.LongItems.Add(new PublicLong { Value = 12345L }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.ProjectTo().First(); intItem.Value.ShouldBe(12345); }); @@ -47,10 +47,10 @@ public void ShouldProjectATooBigLongToAnInt() { RunTest(context => { - context.LongItems.Add(new PublicLongProperty { Value = long.MaxValue }); + context.LongItems.Add(new PublicLong { Value = long.MaxValue }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.ProjectTo().First(); intItem.Value.ShouldBe(0); }); @@ -61,10 +61,10 @@ public void ShouldProjectATooSmallLongToAnInt() { RunTest(context => { - context.LongItems.Add(new PublicLongProperty { Value = int.MinValue - 1L }); + context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.ProjectTo().First(); intItem.Value.ShouldBe(0); }); @@ -75,15 +75,15 @@ public void ShouldProjectAParseableStringToAnIntAsExpected() { void Test(TOrmContext context) { - context.StringItems.Add(new PublicStringProperty { Value = "738" }); + context.StringItems.Add(new PublicString { Value = "738" }); context.SaveChanges(); - var intItem = context.StringItems.ProjectTo().First(); + var intItem = context.StringItems.ProjectTo().First(); intItem.Value.ShouldBe(738); } - if (Context.StringParsingSupported) + if (Context.StringToNumberConversionSupported) { RunTest(Test); } @@ -98,11 +98,11 @@ public void ShouldProjectAnUnparseableStringToAnIntAsExpected() { RunTestAndExpectThrow(context => { - context.StringItems.Add(new PublicStringProperty { Value = "hsejk" }); + context.StringItems.Add(new PublicString { Value = "hsejk" }); context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - context.StringItems.ProjectTo().First(); + context.StringItems.ProjectTo().First(); }); } } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index 56c71d30c..9279d3940 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -19,10 +19,10 @@ public void ShouldProjectAnIntToAString() { RunTest(context => { - context.IntItems.Add(new PublicIntProperty { Value = 763483 }); + context.IntItems.Add(new PublicInt { Value = 763483 }); context.SaveChanges(); - var stringItem = context.IntItems.ProjectTo().First(); + var stringItem = context.IntItems.ProjectTo().First(); stringItem.Value.ShouldBe("763483"); }); @@ -33,10 +33,10 @@ public void ShouldProjectABoolToAString() { RunTest(context => { - context.BoolItems.Add(new PublicBoolProperty { Value = true }); + context.BoolItems.Add(new PublicBool { Value = true }); context.SaveChanges(); - var stringItem = context.BoolItems.ProjectTo().First(); + var stringItem = context.BoolItems.ProjectTo().First(); stringItem.Value.ShouldBe("true"); }); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicBool.cs similarity index 85% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicBool.cs index 46a780075..6b985a377 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicBool.cs @@ -2,7 +2,7 @@ { using System.ComponentModel.DataAnnotations; - public class PublicBoolProperty + public class PublicBool { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolDto.cs similarity index 80% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicBoolDto.cs index b4e18a515..6b0d38076 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolPropertyDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicBoolDto.cs @@ -1,6 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { - public class PublicBoolPropertyDto + public class PublicBoolDto { public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTimeDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTimeDto.cs new file mode 100644 index 000000000..9b020665b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTimeDto.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + + public class PublicDateTimeDto + { + public int Id { get; set; } + + + public DateTime Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicInt.cs similarity index 86% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicInt.cs index c0fa59e64..9c1777790 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicIntProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicInt.cs @@ -2,7 +2,7 @@ { using System.ComponentModel.DataAnnotations; - public class PublicIntProperty + public class PublicInt { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntDto.cs similarity index 80% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicIntDto.cs index 18b03e6ec..1155a98ff 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicIntPropertyDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicIntDto.cs @@ -1,6 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { - public class PublicIntPropertyDto + public class PublicIntDto { public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicLong.cs similarity index 86% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicLong.cs index 6f16f1ec6..ab36a4c9f 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicLongProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicLong.cs @@ -2,7 +2,7 @@ { using System.ComponentModel.DataAnnotations; - public class PublicLongProperty + public class PublicLong { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicShort.cs similarity index 85% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicShort.cs index 73723ad9f..dba76641f 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicShortProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicShort.cs @@ -2,7 +2,7 @@ { using System.ComponentModel.DataAnnotations; - public class PublicShortProperty + public class PublicShort { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicString.cs similarity index 85% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicString.cs index 0d4125bcc..0da541930 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringProperty.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicString.cs @@ -2,7 +2,7 @@ { using System.ComponentModel.DataAnnotations; - public class PublicStringProperty + public class PublicString { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringDto.cs similarity index 79% rename from AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs rename to AgileMapper.UnitTests.Orms/TestClasses/PublicStringDto.cs index a53f8be41..50a71a348 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringPropertyDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringDto.cs @@ -1,6 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { - public class PublicStringPropertyDto + public class PublicStringDto { public int Id { get; set; } diff --git a/AgileMapper.sln b/AgileMapper.sln index 65e22f256..752d7bb27 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -34,6 +34,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTest EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore1", "AgileMapper.UnitTests.Orms.EfCore1\AgileMapper.UnitTests.Orms.EfCore1.csproj", "{FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTests.Orms.Ef6", "AgileMapper.IntegrationTests.Orms.Ef6\AgileMapper.IntegrationTests.Orms.Ef6.csproj", "{B75D1A61-006A-4951-82FE-A2943296A872}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,6 +86,10 @@ Global {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.Build.0 = Release|Any CPU + {B75D1A61-006A-4951-82FE-A2943296A872}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B75D1A61-006A-4951-82FE-A2943296A872}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B75D1A61-006A-4951-82FE-A2943296A872}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B75D1A61-006A-4951-82FE-A2943296A872}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 160472e20..5bb20d850 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -1,13 +1,51 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { +#if !NET_STANDARD + using System; + using System.Linq; +#endif using System.Linq.Expressions; +#if !NET_STANDARD + using System.Reflection; +#endif internal class DefaultQueryProviderSettings : IQueryProviderSettings { +#if !NET_STANDARD + private readonly Lazy _canonicalFunctionsTypeLoader; + private readonly Lazy _sqlFunctionsTypeLoader; + + public DefaultQueryProviderSettings() + { + _canonicalFunctionsTypeLoader = new Lazy(LoadCanonicalFunctionsType); + _sqlFunctionsTypeLoader = new Lazy(LoadSqlFunctionsType); + } + + protected virtual Type LoadCanonicalFunctionsType() => null; + + protected virtual Type LoadSqlFunctionsType() => null; + + public Type CanonicalFunctionsType => _canonicalFunctionsTypeLoader.Value; + + public Type SqlFunctionsType => _sqlFunctionsTypeLoader.Value; +#endif public virtual bool SupportsStringEqualsIgnoreCase => false; public virtual bool SupportsToString => false; - public virtual Expression ConvertToString(MethodCallExpression toStringCall) => toStringCall; + public virtual Expression ConvertToStringCall(MethodCallExpression call) => call; + + public virtual Expression ConvertTryParseCall(MethodCallExpression call) + => this.GetConvertToTypeCall(call); + +#if !NET_STANDARD + protected static Type GetTypeOrNull(string loadedAssemblyName, string typeName) + { + return AppDomain.CurrentDomain + .GetAssemblies() + .FirstOrDefault(assembly => assembly.GetName().Name == loadedAssemblyName)? + .GetType(typeName); + } +#endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 66a316e73..080704ff0 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -2,7 +2,6 @@ { #if !NET_STANDARD using System; - using System.Linq; using System.Linq.Expressions; using Extensions; using NetStandardPolyfills; @@ -11,19 +10,22 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings { #if !NET_STANDARD - public override Expression ConvertToString(MethodCallExpression toStringCall) + protected override Type LoadCanonicalFunctionsType() + => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.EntityFunctions"); + + protected override Type LoadSqlFunctionsType() + => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.SqlClient.SqlFunctions"); + + public override Expression ConvertToStringCall(MethodCallExpression call) { - var sqlFunctionsType = AppDomain.CurrentDomain - .GetAssemblies() - .FirstOrDefault(assembly => assembly.GetName().Name == "System.Data.Entity")? - .GetType("System.Data.Objects.SqlClient.SqlFunctions"); + var sqlFunctionsType = SqlFunctionsType; if (sqlFunctionsType == null) { - return base.ConvertToString(toStringCall); + return base.ConvertToStringCall(call); } - var stringConvertCall = GetStringConvertCall(toStringCall.Object, sqlFunctionsType); + var stringConvertCall = GetStringConvertCall(call.Object, sqlFunctionsType); var trimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); var trimCall = Expression.Call(stringConvertCall, trimMethod); @@ -50,6 +52,13 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(double?)), subject); } + + public override Expression ConvertTryParseCall(MethodCallExpression call) + { + return this.TryGetDateTimeFromStringCall(call, out var convertedCall) + ? convertedCall + : base.ConvertTryParseCall(call); + } #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index a22d70b54..1ea826d34 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -1,7 +1,25 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { + using System; + using System.Linq.Expressions; + internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsToString => true; + +#if !NET_STANDARD + protected override Type LoadCanonicalFunctionsType() + => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); + + protected override Type LoadSqlFunctionsType() + => GetTypeOrNull("EntityFramework.SqlServer", "System.Data.Entity.SqlServer.SqlFunctions"); + + public override Expression ConvertTryParseCall(MethodCallExpression call) + { + return this.TryGetDateTimeFromStringCall(call, out var convertedCall) + ? convertedCall + : base.ConvertTryParseCall(call); + } +#endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 399a1ae99..072eacf35 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -1,13 +1,21 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { + using System; using System.Linq.Expressions; internal interface IQueryProviderSettings { +#if !NET_STANDARD + Type CanonicalFunctionsType { get; } + + Type SqlFunctionsType { get; } +#endif bool SupportsStringEqualsIgnoreCase { get; } bool SupportsToString { get; } - Expression ConvertToString(MethodCallExpression toStringCall); + Expression ConvertToStringCall(MethodCallExpression call); + + Expression ConvertTryParseCall(MethodCallExpression call); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs new file mode 100644 index 000000000..78b608a9e --- /dev/null +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -0,0 +1,89 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Extensions; + using NetStandardPolyfills; + + internal static class QueryProviderSettingsExtensions + { + public static Expression GetConvertToTypeCall( + this IQueryProviderSettings settings, + MethodCallExpression tryParseCall) + { + // ReSharper disable once PossibleNullReferenceException + // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, + // but it at least gives a decent error message: + var convertMethodName = "To" + tryParseCall.Method.DeclaringType.Name; + + var convertMethod = typeof(Convert) + .GetPublicStaticMethods(convertMethodName) + .First(m => m.GetParameters().HasOne() && (m.GetParameters()[0].ParameterType == typeof(string))); + + var convertCall = Expression.Call(convertMethod, tryParseCall.Arguments.First()); + + return convertCall; + } + +#if !NET_STANDARD + public static bool TryGetDateTimeFromStringCall( + this IQueryProviderSettings settings, + MethodCallExpression tryParseCall, + out Expression convertedCall) + { + if ((tryParseCall.Method.DeclaringType != typeof(DateTime)) || + (settings.CanonicalFunctionsType) == null || + (settings.SqlFunctionsType == null)) + { + convertedCall = null; + return false; + } + + var createDateTimeMethod = settings + .CanonicalFunctionsType + .GetPublicStaticMethod("CreateDateTime"); + + if (createDateTimeMethod == null) + { + convertedCall = null; + return false; + } + + var datePartMethod = settings + .SqlFunctionsType + .GetPublicStaticMethods("DatePart") + .FirstOrDefault(m => m.GetParameters().All(p => p.ParameterType == typeof(string))); + + if (datePartMethod == null) + { + convertedCall = null; + return false; + } + + var sourceValue = tryParseCall.Arguments[0]; + + convertedCall = Expression.Call( + createDateTimeMethod, + GetDatePartCall(datePartMethod, "yy", sourceValue), + GetDatePartCall(datePartMethod, "mm", sourceValue), + GetDatePartCall(datePartMethod, "dd", sourceValue), + GetDatePartCall(datePartMethod, "hh", sourceValue), + GetDatePartCall(datePartMethod, "mi", sourceValue), + GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo(typeof(double?))); + + convertedCall = Expression.Property(convertedCall, "Value"); + return true; + } + + private static Expression GetDatePartCall( + MethodInfo datePartMethod, + string datePart, + Expression sourceValue) + { + return Expression.Call(datePartMethod, datePart.ToConstantExpression(), sourceValue); + } +#endif + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/ToStringConverter.cs b/AgileMapper/Queryables/ToStringConverter.cs index af05ce601..08004ef8a 100644 --- a/AgileMapper/Queryables/ToStringConverter.cs +++ b/AgileMapper/Queryables/ToStringConverter.cs @@ -17,7 +17,7 @@ public static bool TryConvert( return false; } - converted = settings.ConvertToString(methodCall); + converted = settings.ConvertToStringCall(methodCall); return true; } diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index 17c613224..154cc990c 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -1,10 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables { - using System; - using System.Linq; using System.Linq.Expressions; using Extensions; - using NetStandardPolyfills; using Settings; internal static class TryParseAssignmentConverter @@ -41,28 +38,12 @@ public static bool TryConvert( if (methodCall.Method.IsStatic && (methodCall.Method.Name == "TryParse")) { - converted = assignment.Update(GetConvertCall(methodCall)); + converted = assignment.Update(settings.ConvertTryParseCall(methodCall)); return true; } converted = null; return false; } - - private static MethodCallExpression GetConvertCall(MethodCallExpression tryParseCall) - { - // ReSharper disable once PossibleNullReferenceException - // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, - // but it at least gives a decent error message: - var convertMethodName = "To" + tryParseCall.Method.DeclaringType.Name; - - var convertMethod = typeof(Convert) - .GetPublicStaticMethods(convertMethodName) - .First(m => m.GetParameters().HasOne() && (m.GetParameters()[0].ParameterType == typeof(string))); - - var convertCall = Expression.Call(convertMethod, tryParseCall.Arguments.First()); - - return convertCall; - } } } \ No newline at end of file From 191cd388516ae1b04b20011fc07a8ca4f88793ff Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 11 Nov 2017 16:59:46 +0000 Subject: [PATCH 038/176] Reusing EF5 and EF6 unit test classes in corresponding integration test projects --- ...ileMapper.IntegrationTests.Orms.Ef5.csproj | 9 ++- .../Infrastructure/Ef5DbSetWrapper.cs | 28 -------- .../Infrastructure/Ef5TestDbContext.cs | 64 ------------------- .../Infrastructure/Ef5TestLocalDbContext.cs | 19 ++++++ ...inition.cs => Ef5TestLocalDbDefinition.cs} | 2 +- .../WhenConvertingToDateTimes.cs | 4 +- .../WhenConvertingToStrings.cs | 4 +- ...ileMapper.IntegrationTests.Orms.Ef6.csproj | 9 ++- .../Infrastructure/Ef6DbSetWrapper.cs | 21 ------ .../Infrastructure/Ef6TestDbContext.cs | 64 ------------------- .../Infrastructure/Ef6TestLocalDbContext.cs | 19 ++++++ ...inition.cs => Ef6TestLocalDbDefinition.cs} | 2 +- .../WhenConvertingToDateTimes.cs | 4 +- .../Infrastructure/Ef5TestDbContext.cs | 8 ++- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 - .../Infrastructure/Ef6TestDbContext.cs | 8 ++- .../WhenConvertingToDateTimes.cs | 13 ---- 17 files changed, 72 insertions(+), 207 deletions(-) delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs rename AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/{Ef5LocalDbTestDefinition.cs => Ef5TestLocalDbDefinition.cs} (69%) delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs create mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs rename AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/{Ef6LocalDbTestDefinition.cs => Ef6TestLocalDbDefinition.cs} (69%) delete mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj index c28c4518d..689126362 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj +++ b/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj @@ -80,14 +80,17 @@ VersionInfo.cs - - - + + + + {d87103fd-3851-4724-bd8f-9cef19c8f193} + AgileMapper.UnitTests.Orms.Ef5 + {66522d44-19f5-4af5-9d43-483a3cd6f958} AgileMapper.UnitTests.Orms diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs deleted file mode 100644 index 139a9a64f..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure -{ - using System.Data.Entity; - using System.Linq; - using UnitTests.Orms.Infrastructure; - - public class Ef5DbSetWrapper : DbSetWrapperBase - where TEntity : class - { - private readonly DbSet _dbSet; - - public Ef5DbSetWrapper(DbSet dbSet) - : base(dbSet) - { - _dbSet = dbSet; - } - - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); - - public override void Clear() - { - foreach (var entity in _dbSet.ToArray()) - { - _dbSet.Remove(entity); - } - } - } -} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs deleted file mode 100644 index 5687c1402..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure -{ - using System.Data.Entity; - using System.Data.SqlClient; - using UnitTests.Orms; - using UnitTests.Orms.Infrastructure; - using UnitTests.Orms.TestClasses; - - public class Ef5TestDbContext : DbContext, ITestLocalDbContext - { - public Ef5TestDbContext() - : base( - new SqlConnection(TestConstants.GetLocalDbConnectionString()), - true) - { - } - - public DbSet Products { get; set; } - - public DbSet BoolItems { get; set; } - - public DbSet ShortItems { get; set; } - - public DbSet IntItems { get; set; } - - public DbSet LongItems { get; set; } - - public DbSet StringItems { get; set; } - - #region ITestDbContext Members - - public bool StringToNumberConversionSupported => false; - - IDbSetWrapper ITestDbContext.Products - => new Ef5DbSetWrapper(Products); - - IDbSetWrapper ITestDbContext.BoolItems - => new Ef5DbSetWrapper(BoolItems); - - IDbSetWrapper ITestDbContext.ShortItems - => new Ef5DbSetWrapper(ShortItems); - - IDbSetWrapper ITestDbContext.IntItems - => new Ef5DbSetWrapper(IntItems); - - IDbSetWrapper ITestDbContext.LongItems - => new Ef5DbSetWrapper(LongItems); - - IDbSetWrapper ITestDbContext.StringItems - => new Ef5DbSetWrapper(StringItems); - - void ITestDbContext.SaveChanges() => SaveChanges(); - - #endregion - - #region ITestLocalDbContext Members - - void ITestLocalDbContext.CreateDatabase() => Database.Create(); - - void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); - - #endregion - } -} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs new file mode 100644 index 000000000..294b73e4b --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +{ + using System.Data.SqlClient; + using UnitTests.Orms; + using UnitTests.Orms.Ef5.Infrastructure; + using UnitTests.Orms.Infrastructure; + + public class Ef5TestLocalDbContext : Ef5TestDbContext, ITestLocalDbContext + { + public Ef5TestLocalDbContext() + : base(new SqlConnection(TestConstants.GetLocalDbConnectionString())) + { + } + + void ITestLocalDbContext.CreateDatabase() => Database.Create(); + + void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs similarity index 69% rename from AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs rename to AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs index c75112858..b0797d298 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5LocalDbTestDefinition.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs @@ -5,7 +5,7 @@ using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class Ef5LocalDbTestDefinition : ICollectionFixture> + public class Ef5TestLocalDbDefinition : ICollectionFixture> { } } \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 240597272..d6b890585 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -4,9 +4,9 @@ using UnitTests.Orms.Infrastructure; using UnitTests.Orms.SimpleTypeConversion; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes { - public WhenConvertingToDateTimes(LocalDbTestContext context) + public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) { } diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs index 7527ce8f7..cde97bbe9 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -4,9 +4,9 @@ using UnitTests.Orms.Infrastructure; using UnitTests.Orms.SimpleTypeConversion; - public class WhenConvertingToStrings : WhenConvertingToStrings + public class WhenConvertingToStrings : WhenConvertingToStrings { - public WhenConvertingToStrings(LocalDbTestContext context) + public WhenConvertingToStrings(LocalDbTestContext context) : base(context) { } diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj b/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj index 3e11e9f87..ea07538f0 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj +++ b/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj @@ -82,13 +82,16 @@ VersionInfo.cs - - - + + + + {63b8975d-0cde-48f5-8ca9-8af8fe729610} + AgileMapper.UnitTests.Orms.Ef6 + {66522d44-19f5-4af5-9d43-483a3cd6f958} AgileMapper.UnitTests.Orms diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs deleted file mode 100644 index 041b853d9..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure -{ - using System.Data.Entity; - using UnitTests.Orms.Infrastructure; - - public class Ef6DbSetWrapper : DbSetWrapperBase - where TEntity : class - { - private readonly DbSet _dbSet; - - public Ef6DbSetWrapper(DbSet dbSet) - : base(dbSet) - { - _dbSet = dbSet; - } - - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); - - public override void Clear() => _dbSet.RemoveRange(_dbSet); - } -} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs deleted file mode 100644 index 62a104ca2..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure -{ - using System.Data.Entity; - using System.Data.SqlClient; - using UnitTests.Orms; - using UnitTests.Orms.Infrastructure; - using UnitTests.Orms.TestClasses; - - public class Ef6TestDbContext : DbContext, ITestLocalDbContext - { - public Ef6TestDbContext() - : base( - new SqlConnection(TestConstants.GetLocalDbConnectionString()), - true) - { - } - - public DbSet Products { get; set; } - - public DbSet BoolItems { get; set; } - - public DbSet ShortItems { get; set; } - - public DbSet IntItems { get; set; } - - public DbSet LongItems { get; set; } - - public DbSet StringItems { get; set; } - - #region ITestDbContext Members - - public bool StringToNumberConversionSupported => false; - - IDbSetWrapper ITestDbContext.Products - => new Ef6DbSetWrapper(Products); - - IDbSetWrapper ITestDbContext.BoolItems - => new Ef6DbSetWrapper(BoolItems); - - IDbSetWrapper ITestDbContext.ShortItems - => new Ef6DbSetWrapper(ShortItems); - - IDbSetWrapper ITestDbContext.IntItems - => new Ef6DbSetWrapper(IntItems); - - IDbSetWrapper ITestDbContext.LongItems - => new Ef6DbSetWrapper(LongItems); - - IDbSetWrapper ITestDbContext.StringItems - => new Ef6DbSetWrapper(StringItems); - - void ITestDbContext.SaveChanges() => SaveChanges(); - - #endregion - - #region ITestLocalDbContext Members - - void ITestLocalDbContext.CreateDatabase() => Database.Create(); - - void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); - - #endregion - } -} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs new file mode 100644 index 000000000..c8ffc99e0 --- /dev/null +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +{ + using System.Data.SqlClient; + using UnitTests.Orms; + using UnitTests.Orms.Ef6.Infrastructure; + using UnitTests.Orms.Infrastructure; + + public class Ef6TestLocalDbContext : Ef6TestDbContext, ITestLocalDbContext + { + public Ef6TestLocalDbContext() + : base(new SqlConnection(TestConstants.GetLocalDbConnectionString())) + { + } + + void ITestLocalDbContext.CreateDatabase() => Database.Create(); + + void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); + } +} \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs similarity index 69% rename from AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs rename to AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs index 302167d45..4f31635bc 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6LocalDbTestDefinition.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs @@ -5,7 +5,7 @@ using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] - public class Ef6LocalDbTestDefinition : ICollectionFixture> + public class Ef6TestLocalDbDefinition : ICollectionFixture> { } } \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs index c4fda4432..5da72f670 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -4,9 +4,9 @@ using UnitTests.Orms.Infrastructure; using UnitTests.Orms.SimpleTypeConversion; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes { - public WhenConvertingToDateTimes(LocalDbTestContext context) + public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index bc95cc6d7..5b546ecb7 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { + using System.Data.Common; using System.Data.Entity; using Effort; using Orms.Infrastructure; @@ -8,7 +9,12 @@ public class Ef5TestDbContext : DbContext, ITestDbContext { public Ef5TestDbContext() - : base(DbConnectionFactory.CreateTransient(), true) + : this(DbConnectionFactory.CreateTransient()) + { + } + + protected Ef5TestDbContext(DbConnection dbConnection) + : base(dbConnection, true) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 24465c3f9..489162080 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -97,7 +97,6 @@ - diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 3767772d9..b4db86fd7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { + using System.Data.Common; using System.Data.Entity; using Effort; using Orms.Infrastructure; @@ -8,7 +9,12 @@ public class Ef6TestDbContext : DbContext, ITestDbContext { public Ef6TestDbContext() - : base(DbConnectionFactory.CreateTransient(), true) + : this(DbConnectionFactory.CreateTransient()) + { + } + + protected Ef6TestDbContext(DbConnection dbConnection) + : base(dbConnection, true) { } diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs deleted file mode 100644 index 343734bdb..000000000 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion -{ - using Infrastructure; - using Orms.SimpleTypeConversion; - - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes - { - public WhenConvertingToDateTimes(InMemoryEf6TestContext context) - : base(context) - { - } - } -} \ No newline at end of file From 5b9c98bf6479604605cc95b7d797d8d874876fa6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 09:03:31 +0000 Subject: [PATCH 039/176] Removing Shouldly package where not needed --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef5/packages.config | 1 - .../AgileMapper.UnitTests.Orms.Ef6.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef6/packages.config | 1 - 4 files changed, 8 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 1bedf550c..0c1d2fcac 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -55,9 +55,6 @@ ..\packages\NMemory.1.1.2\lib\net45\NMemory.dll - - ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll - diff --git a/AgileMapper.UnitTests.Orms.Ef5/packages.config b/AgileMapper.UnitTests.Orms.Ef5/packages.config index e8a4493ec..c36b96b5f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef5/packages.config @@ -3,7 +3,6 @@ - diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 489162080..ad09fe8a8 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -58,9 +58,6 @@ ..\packages\NMemory.1.1.2\lib\net45\NMemory.dll - - ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll - diff --git a/AgileMapper.UnitTests.Orms.Ef6/packages.config b/AgileMapper.UnitTests.Orms.Ef6/packages.config index 584fc2e86..84fd91cb1 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef6/packages.config @@ -3,7 +3,6 @@ - From f8dbbd7a900bfc52823645c09bd4ec6a5afa3766 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 10:58:41 +0000 Subject: [PATCH 040/176] Fixing date time integration tests --- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 913a94c77..1a0d9ea6b 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -22,14 +22,13 @@ public void ShouldProjectAParseableStringToADateTimeAsExpected() void Test(TOrmContext context) { var now = DateTime.Now; - var nowString = now.ToString(CultureInfo.InvariantCulture); - context.StringItems.Add(new PublicString { Value = nowString }); + context.StringItems.Add(new PublicString { Value = now.ToString("s") }); context.SaveChanges(); var dateTimeItem = context.StringItems.ProjectTo().First(); - dateTimeItem.Value.ToString(CultureInfo.InvariantCulture).ShouldBe(nowString); + dateTimeItem.Value.ShouldBe(now, TimeSpan.FromSeconds(1)); } RunTest(Test); From 426eb5fd1720df86c04e5ad2a9243f02576b83f7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 14:15:17 +0000 Subject: [PATCH 041/176] Test coverage for in-memory EF5 and EF6 string -> datetime conversion --- .../Infrastructure/Ef5TestLocalDbContext.cs | 2 ++ .../Infrastructure/Ef6TestLocalDbContext.cs | 2 ++ .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../Infrastructure/Ef5TestDbContext.cs | 2 ++ .../WhenConvertingToDateTimes.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Infrastructure/Ef6TestDbContext.cs | 2 ++ .../WhenConvertingToDateTimes.cs | 13 +++++++++++++ .../Infrastructure/EfCore1TestDbContext.cs | 2 ++ .../Infrastructure/EfCore2TestDbContext.cs | 2 ++ .../Infrastructure/ITestDbContext.cs | 2 ++ .../WhenConvertingToDateTimes.cs | 9 ++++++++- 12 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs index 294b73e4b..d560accae 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs @@ -12,6 +12,8 @@ public Ef5TestLocalDbContext() { } + public override bool StringToDateTimeConversionSupported => true; + void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs index c8ffc99e0..1c5a4a452 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs +++ b/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs @@ -12,6 +12,8 @@ public Ef6TestLocalDbContext() { } + public override bool StringToDateTimeConversionSupported => true; + void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 0c1d2fcac..5d54fc67f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 5b546ecb7..155c5cbf5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -34,6 +34,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; + public virtual bool StringToDateTimeConversionSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..e8019551c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index ad09fe8a8..240f69451 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,6 +91,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index b4db86fd7..1e6d12aac 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -34,6 +34,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; + public virtual bool StringToDateTimeConversionSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..343734bdb --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + { + public WhenConvertingToDateTimes(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index c7a422c05..440bce4d4 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -32,6 +32,8 @@ public EfCore1TestDbContext() public bool StringToNumberConversionSupported => true; + public bool StringToDateTimeConversionSupported => true; + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index f5960e9c8..c5b7d6439 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -35,6 +35,8 @@ public EfCore2TestDbContext() public bool StringToNumberConversionSupported => true; + public bool StringToDateTimeConversionSupported => true; + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index f23f165b5..75d3082ef 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -7,6 +7,8 @@ public interface ITestDbContext : IDisposable { bool StringToNumberConversionSupported { get; } + bool StringToDateTimeConversionSupported { get; } + IDbSetWrapper Products { get; } IDbSetWrapper BoolItems { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 1a0d9ea6b..5597b585c 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -31,7 +31,14 @@ void Test(TOrmContext context) dateTimeItem.Value.ShouldBe(now, TimeSpan.FromSeconds(1)); } - RunTest(Test); + if (Context.StringToDateTimeConversionSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } } } } From 46c31d76eb58fb0bc440c06177426f6f538e723a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 14:38:52 +0000 Subject: [PATCH 042/176] Handling null strings in string -> datetime conversions --- .../WhenConvertingToDateTimes.cs | 24 +++++++++++++++- .../QueryProviderSettingsExtensions.cs | 4 +-- .../Queryables/TryParseAssignmentConverter.cs | 28 +++++++++++++++---- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 5597b585c..d805046cb 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System; - using System.Globalization; using System.Linq; using Infrastructure; using Shouldly; @@ -40,5 +39,28 @@ void Test(TOrmContext context) RunTestAndExpectThrow(Test); } } + + [Fact] + public void ShouldProjectANullStringToADateTimeAsExpected() + { + void Test(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ShouldBe(default(DateTime)); + } + + if (Context.StringToDateTimeConversionSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } + } } } diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 78b608a9e..266b22c16 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -34,8 +34,8 @@ public static bool TryGetDateTimeFromStringCall( out Expression convertedCall) { if ((tryParseCall.Method.DeclaringType != typeof(DateTime)) || - (settings.CanonicalFunctionsType) == null || - (settings.SqlFunctionsType == null)) + (settings.CanonicalFunctionsType == null) || + (settings.SqlFunctionsType == null)) { convertedCall = null; return false; diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index 154cc990c..04119040d 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -36,14 +36,32 @@ public static bool TryConvert( var methodCall = (MethodCallExpression)tryParseOrDefault.Test; - if (methodCall.Method.IsStatic && (methodCall.Method.Name == "TryParse")) + if (!methodCall.Method.IsStatic || (methodCall.Method.Name != "TryParse")) { - converted = assignment.Update(settings.ConvertTryParseCall(methodCall)); - return true; + converted = null; + return false; + } + + var convertedValue = settings.ConvertTryParseCall(methodCall); + var fallbackValue = GetFallbackValue(tryParseOrDefault); + var nullString = default(string).ToConstantExpression(); + var sourceIsNotNull = Expression.NotEqual(methodCall.Arguments[0], nullString); + var convertedOrFallback = Expression.Condition(sourceIsNotNull, convertedValue, fallbackValue); + + converted = assignment.Update(convertedOrFallback); + return true; + } + + private static Expression GetFallbackValue(ConditionalExpression tryParseOrDefault) + { + var defaultValue = tryParseOrDefault.IfFalse; + + if (defaultValue.NodeType != ExpressionType.Default) + { + return defaultValue; } - converted = null; - return false; + return DefaultExpressionConverter.Convert((DefaultExpression)defaultValue); } } } \ No newline at end of file From b5cd537c64f9c049832b20ba5cc9fb9c4105d4e6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 18:09:22 +0000 Subject: [PATCH 043/176] Moving integration test projects to UnitTests.*.*LocalDb --- .../Properties/AssemblyInfo.cs | 7 ------- .../Properties/AssemblyInfo.cs | 7 ------- .../AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 4 ++-- .../App.config | 0 .../Infrastructure/Ef5TestLocalDbContext.cs | 8 ++++---- .../Infrastructure/Ef5TestLocalDbDefinition.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 7 +++++++ .../WhenConvertingToDateTimes.cs | 6 +++--- .../WhenConvertingToStrings.cs | 6 +++--- .../packages.config | 0 .../AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj | 4 ++-- .../App.config | 0 .../Infrastructure/Ef6TestLocalDbContext.cs | 8 ++++---- .../Infrastructure/Ef6TestLocalDbDefinition.cs | 6 +++--- .../Properties/AssemblyInfo.cs | 7 +++++++ .../WhenConvertingToDateTimes.cs | 6 +++--- .../packages.config | 0 AgileMapper.sln | 14 +++++++------- 18 files changed, 48 insertions(+), 48 deletions(-) delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs delete mode 100644 AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs rename AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj => AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj (97%) rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/App.config (100%) rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/Infrastructure/Ef5TestLocalDbContext.cs (72%) rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/Infrastructure/Ef5TestLocalDbDefinition.cs (57%) create mode 100644 AgileMapper.UnitTests.Orms.Ef5.LocalDb/Properties/AssemblyInfo.cs rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/SimpleTypeConversion/WhenConvertingToDateTimes.cs (61%) rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/SimpleTypeConversion/WhenConvertingToStrings.cs (60%) rename {AgileMapper.IntegrationTests.Orms.Ef5 => AgileMapper.UnitTests.Orms.Ef5.LocalDb}/packages.config (100%) rename AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj => AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj (97%) rename {AgileMapper.IntegrationTests.Orms.Ef6 => AgileMapper.UnitTests.Orms.Ef6.LocalDb}/App.config (100%) rename {AgileMapper.IntegrationTests.Orms.Ef6 => AgileMapper.UnitTests.Orms.Ef6.LocalDb}/Infrastructure/Ef6TestLocalDbContext.cs (72%) rename {AgileMapper.IntegrationTests.Orms.Ef6 => AgileMapper.UnitTests.Orms.Ef6.LocalDb}/Infrastructure/Ef6TestLocalDbDefinition.cs (57%) create mode 100644 AgileMapper.UnitTests.Orms.Ef6.LocalDb/Properties/AssemblyInfo.cs rename {AgileMapper.IntegrationTests.Orms.Ef6 => AgileMapper.UnitTests.Orms.Ef6.LocalDb}/SimpleTypeConversion/WhenConvertingToDateTimes.cs (61%) rename {AgileMapper.IntegrationTests.Orms.Ef6 => AgileMapper.UnitTests.Orms.Ef6.LocalDb}/packages.config (100%) diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs b/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs deleted file mode 100644 index 7b1da0d46..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] -[assembly: AssemblyDescription("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] - -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs b/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs deleted file mode 100644 index 7b1da0d46..000000000 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] -[assembly: AssemblyDescription("AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5")] - -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj similarity index 97% rename from AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index 689126362..b7282dc77 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/AgileMapper.IntegrationTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -8,8 +8,8 @@ {48B855A9-F42C-4D2A-9549-97ED27D59336} Library Properties - AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5 - AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5 + AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb + AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb v4.6.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/App.config b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config similarity index 100% rename from AgileMapper.IntegrationTests.Orms.Ef5/App.config rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs similarity index 72% rename from AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs index d560accae..1df479db9 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs @@ -1,9 +1,9 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.Infrastructure { using System.Data.SqlClient; - using UnitTests.Orms; - using UnitTests.Orms.Ef5.Infrastructure; - using UnitTests.Orms.Infrastructure; + using Ef5.Infrastructure; + using Orms; + using Orms.Infrastructure; public class Ef5TestLocalDbContext : Ef5TestDbContext, ITestLocalDbContext { diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbDefinition.cs similarity index 57% rename from AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbDefinition.cs index b0797d298..476fc16a9 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/Infrastructure/Ef5TestLocalDbDefinition.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbDefinition.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.Infrastructure { - using UnitTests.Orms; - using UnitTests.Orms.Infrastructure; + using Orms; + using Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..1d6e29c10 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb")] + +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs similarity index 61% rename from AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index d6b890585..5afd2e2e0 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,8 +1,8 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { using Infrastructure; - using UnitTests.Orms.Infrastructure; - using UnitTests.Orms.SimpleTypeConversion; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes { diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs similarity index 60% rename from AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs index cde97bbe9..ac726676d 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,8 +1,8 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef5.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { using Infrastructure; - using UnitTests.Orms.Infrastructure; - using UnitTests.Orms.SimpleTypeConversion; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; public class WhenConvertingToStrings : WhenConvertingToStrings { diff --git a/AgileMapper.IntegrationTests.Orms.Ef5/packages.config b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config similarity index 100% rename from AgileMapper.IntegrationTests.Orms.Ef5/packages.config rename to AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj similarity index 97% rename from AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj index ea07538f0..04a47790f 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/AgileMapper.IntegrationTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj @@ -8,8 +8,8 @@ {B75D1A61-006A-4951-82FE-A2943296A872} Library Properties - AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6 - AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6 + AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb + AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb v4.6.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/App.config b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/App.config similarity index 100% rename from AgileMapper.IntegrationTests.Orms.Ef6/App.config rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/App.config diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs similarity index 72% rename from AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs index 1c5a4a452..12bbefe05 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs @@ -1,9 +1,9 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.Infrastructure { using System.Data.SqlClient; - using UnitTests.Orms; - using UnitTests.Orms.Ef6.Infrastructure; - using UnitTests.Orms.Infrastructure; + using Ef6.Infrastructure; + using Orms; + using Orms.Infrastructure; public class Ef6TestLocalDbContext : Ef6TestDbContext, ITestLocalDbContext { diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbDefinition.cs similarity index 57% rename from AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbDefinition.cs index 4f31635bc..92ed7610c 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/Infrastructure/Ef6TestLocalDbDefinition.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbDefinition.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.Infrastructure +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.Infrastructure { - using UnitTests.Orms; - using UnitTests.Orms.Infrastructure; + using Orms; + using Orms.Infrastructure; using Xunit; [CollectionDefinition(TestConstants.OrmCollectionName)] diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..7cad80571 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb")] +[assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb")] + +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs similarity index 61% rename from AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 5da72f670..e64a28521 100644 --- a/AgileMapper.IntegrationTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,8 +1,8 @@ -namespace AgileObjects.AgileMapper.IntegrationTests.Orms.Ef6.SimpleTypeConversion +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion { using Infrastructure; - using UnitTests.Orms.Infrastructure; - using UnitTests.Orms.SimpleTypeConversion; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes { diff --git a/AgileMapper.IntegrationTests.Orms.Ef6/packages.config b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config similarity index 100% rename from AgileMapper.IntegrationTests.Orms.Ef6/packages.config rename to AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config diff --git a/AgileMapper.sln b/AgileMapper.sln index 752d7bb27..c8def48b8 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -30,11 +30,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore2", "AgileMapper.UnitTests.Orms.EfCore2\AgileMapper.UnitTests.Orms.EfCore2.csproj", "{2E3DF5C2-8A38-4A03-86D7-8D463C917E47}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTests.Orms.Ef5", "AgileMapper.IntegrationTests.Orms.Ef5\AgileMapper.IntegrationTests.Orms.Ef5.csproj", "{48B855A9-F42C-4D2A-9549-97ED27D59336}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.EfCore1", "AgileMapper.UnitTests.Orms.EfCore1\AgileMapper.UnitTests.Orms.EfCore1.csproj", "{FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.IntegrationTests.Orms.Ef6", "AgileMapper.IntegrationTests.Orms.Ef6\AgileMapper.IntegrationTests.Orms.Ef6.csproj", "{B75D1A61-006A-4951-82FE-A2943296A872}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.Ef5.LocalDb", "AgileMapper.UnitTests.Orms.Ef5.LocalDb\AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj", "{48B855A9-F42C-4D2A-9549-97ED27D59336}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.Ef6.LocalDb", "AgileMapper.UnitTests.Orms.Ef6.LocalDb\AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj", "{B75D1A61-006A-4951-82FE-A2943296A872}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -78,14 +78,14 @@ Global {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E3DF5C2-8A38-4A03-86D7-8D463C917E47}.Release|Any CPU.Build.0 = Release|Any CPU - {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.Build.0 = Release|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {FDEC5C57-9B2B-4599-80A6-DF0791BC7E1B}.Release|Any CPU.Build.0 = Release|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48B855A9-F42C-4D2A-9549-97ED27D59336}.Release|Any CPU.Build.0 = Release|Any CPU {B75D1A61-006A-4951-82FE-A2943296A872}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B75D1A61-006A-4951-82FE-A2943296A872}.Debug|Any CPU.Build.0 = Debug|Any CPU {B75D1A61-006A-4951-82FE-A2943296A872}.Release|Any CPU.ActiveCfg = Release|Any CPU From c58a019fa07c929deef704d9b85fc731abdb9c6a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 18:39:23 +0000 Subject: [PATCH 044/176] Guarding string -> DateTime projection in EF5 + EF6 using SqlFunctions.IsDate --- .../Infrastructure/Ef5TestLocalDbContext.cs | 2 + .../Infrastructure/Ef5TestDbContext.cs | 2 + .../Infrastructure/Ef6TestLocalDbContext.cs | 2 + .../Infrastructure/Ef6TestDbContext.cs | 2 + .../Infrastructure/EfCore1TestDbContext.cs | 2 + .../Infrastructure/EfCore2TestDbContext.cs | 2 + .../Infrastructure/ITestDbContext.cs | 2 + .../WhenConvertingToDateTimes.cs | 23 +++++++++++ .../Settings/DefaultQueryProviderSettings.cs | 2 +- .../Settings/Ef5QueryProviderSettings.cs | 6 +-- .../Settings/Ef6QueryProviderSettings.cs | 6 +-- .../Settings/IQueryProviderSettings.cs | 2 +- .../QueryProviderSettingsExtensions.cs | 39 ++++++++++++++++++- .../Queryables/TryParseAssignmentConverter.cs | 20 +--------- 14 files changed, 84 insertions(+), 28 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs index 1df479db9..d082e1f73 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs @@ -14,6 +14,8 @@ public Ef5TestLocalDbContext() public override bool StringToDateTimeConversionSupported => true; + public override bool StringToDateTimeValidationSupported => true; + void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 155c5cbf5..b5d48842b 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -36,6 +36,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public virtual bool StringToDateTimeConversionSupported => false; + public virtual bool StringToDateTimeValidationSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs index 12bbefe05..a6f0d6774 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs @@ -14,6 +14,8 @@ public Ef6TestLocalDbContext() public override bool StringToDateTimeConversionSupported => true; + public override bool StringToDateTimeValidationSupported => true; + void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 1e6d12aac..369e9663e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -36,6 +36,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public virtual bool StringToDateTimeConversionSupported => false; + public virtual bool StringToDateTimeValidationSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 440bce4d4..5ad6d00c8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -34,6 +34,8 @@ public EfCore1TestDbContext() public bool StringToDateTimeConversionSupported => true; + public bool StringToDateTimeValidationSupported => false; + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index c5b7d6439..63ab5dc14 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -37,6 +37,8 @@ public EfCore2TestDbContext() public bool StringToDateTimeConversionSupported => true; + public bool StringToDateTimeValidationSupported => false; + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 75d3082ef..205269a99 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -9,6 +9,8 @@ public interface ITestDbContext : IDisposable bool StringToDateTimeConversionSupported { get; } + bool StringToDateTimeValidationSupported { get; } + IDbSetWrapper Products { get; } IDbSetWrapper BoolItems { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index d805046cb..843a56b03 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -62,5 +62,28 @@ void Test(TOrmContext context) RunTestAndExpectThrow(Test); } } + + [Fact] + public void ShouldProjectAnUnparseableStringToADateTimeAsExpected() + { + void Test(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ShouldBe(default(DateTime)); + } + + if (Context.StringToDateTimeValidationSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } + } } } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 5bb20d850..c329d1af5 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -35,7 +35,7 @@ public DefaultQueryProviderSettings() public virtual Expression ConvertToStringCall(MethodCallExpression call) => call; - public virtual Expression ConvertTryParseCall(MethodCallExpression call) + public virtual Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) => this.GetConvertToTypeCall(call); #if !NET_STANDARD diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 080704ff0..5eb51d3c2 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -53,11 +53,11 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct subject); } - public override Expression ConvertTryParseCall(MethodCallExpression call) + public override Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) { - return this.TryGetDateTimeFromStringCall(call, out var convertedCall) + return this.TryGetDateTimeFromStringCall(call, fallbackValue, out var convertedCall) ? convertedCall - : base.ConvertTryParseCall(call); + : base.ConvertTryParseCall(call, fallbackValue); } #endif } diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 1ea826d34..035da2e0c 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -14,11 +14,11 @@ protected override Type LoadCanonicalFunctionsType() protected override Type LoadSqlFunctionsType() => GetTypeOrNull("EntityFramework.SqlServer", "System.Data.Entity.SqlServer.SqlFunctions"); - public override Expression ConvertTryParseCall(MethodCallExpression call) + public override Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) { - return this.TryGetDateTimeFromStringCall(call, out var convertedCall) + return this.TryGetDateTimeFromStringCall(call, fallbackValue, out var convertedCall) ? convertedCall - : base.ConvertTryParseCall(call); + : base.ConvertTryParseCall(call, fallbackValue); } #endif } diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 072eacf35..0b40be3ce 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -16,6 +16,6 @@ internal interface IQueryProviderSettings Expression ConvertToStringCall(MethodCallExpression call); - Expression ConvertTryParseCall(MethodCallExpression call); + Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 266b22c16..5eb8ead0c 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -31,6 +31,7 @@ public static Expression GetConvertToTypeCall( public static bool TryGetDateTimeFromStringCall( this IQueryProviderSettings settings, MethodCallExpression tryParseCall, + Expression fallbackValue, out Expression convertedCall) { if ((tryParseCall.Method.DeclaringType != typeof(DateTime)) || @@ -64,7 +65,7 @@ public static bool TryGetDateTimeFromStringCall( var sourceValue = tryParseCall.Arguments[0]; - convertedCall = Expression.Call( + var createDateTimeCall = Expression.Call( createDateTimeMethod, GetDatePartCall(datePartMethod, "yy", sourceValue), GetDatePartCall(datePartMethod, "mm", sourceValue), @@ -73,7 +74,18 @@ public static bool TryGetDateTimeFromStringCall( GetDatePartCall(datePartMethod, "mi", sourceValue), GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo(typeof(double?))); - convertedCall = Expression.Property(convertedCall, "Value"); + if (fallbackValue.NodeType == ExpressionType.Default) + { + fallbackValue = DefaultExpressionConverter.Convert((DefaultExpression)fallbackValue); + } + + var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); + + var nullString = default(string).ToConstantExpression(); + var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); + var convertedOrFallback = Expression.Condition(sourceIsNotNull, createdDateTime, fallbackValue); + + convertedCall = convertedOrFallback; return true; } @@ -84,6 +96,29 @@ private static Expression GetDatePartCall( { return Expression.Call(datePartMethod, datePart.ToConstantExpression(), sourceValue); } + + private static Expression GetGuardedDateCreation( + Expression createDateTimeCall, + Expression sourceValue, + Expression fallbackValue, + IQueryProviderSettings settings) + { + var createdDateTime = Expression.Property(createDateTimeCall, "Value"); + + var isDateMethod = settings.SqlFunctionsType.GetPublicStaticMethod("IsDate"); + + if (isDateMethod == null) + { + return createDateTimeCall; + } + + var isDateCall = Expression.Call(isDateMethod, sourceValue); + var one = 1.ToConstantExpression(typeof(int?)); + var isDateIsTrue = Expression.Equal(isDateCall, one); + var createdDateOrFallback = Expression.Condition(isDateIsTrue, createdDateTime, fallbackValue); + + return createdDateOrFallback; + } #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/TryParseAssignmentConverter.cs index 04119040d..c13081142 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/TryParseAssignmentConverter.cs @@ -42,26 +42,10 @@ public static bool TryConvert( return false; } - var convertedValue = settings.ConvertTryParseCall(methodCall); - var fallbackValue = GetFallbackValue(tryParseOrDefault); - var nullString = default(string).ToConstantExpression(); - var sourceIsNotNull = Expression.NotEqual(methodCall.Arguments[0], nullString); - var convertedOrFallback = Expression.Condition(sourceIsNotNull, convertedValue, fallbackValue); + var convertedValue = settings.ConvertTryParseCall(methodCall, tryParseOrDefault.IfFalse); - converted = assignment.Update(convertedOrFallback); + converted = assignment.Update(convertedValue); return true; } - - private static Expression GetFallbackValue(ConditionalExpression tryParseOrDefault) - { - var defaultValue = tryParseOrDefault.IfFalse; - - if (defaultValue.NodeType != ExpressionType.Default) - { - return defaultValue; - } - - return DefaultExpressionConverter.Convert((DefaultExpression)defaultValue); - } } } \ No newline at end of file From d166c730a16830869df521ded965787c97d185e8 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 12 Nov 2017 19:01:12 +0000 Subject: [PATCH 045/176] Adding Include() wrappers to IDbSetWrapper --- .../Infrastructure/Ef5DbSetWrapper.cs | 7 +++++++ .../Infrastructure/Ef6DbSetWrapper.cs | 7 +++++++ .../Infrastructure/EfCore1DbSetWrapper.cs | 7 +++++++ .../Infrastructure/EfCore2DbSetWrapper.cs | 7 +++++++ .../Infrastructure/DbSetWrapperBase.cs | 2 ++ AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs | 4 ++++ 6 files changed, 34 insertions(+) diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs index b8aa288fe..9bfb6c8d5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Infrastructure { + using System; using System.Data.Entity; using System.Linq; + using System.Linq.Expressions; using Orms.Infrastructure; public class Ef5DbSetWrapper : DbSetWrapperBase @@ -15,6 +17,11 @@ public Ef5DbSetWrapper(DbSet dbSet) _dbSet = dbSet; } + public override void Include(Expression> navigationPropertyPath) + { + _dbSet.Include(navigationPropertyPath); + } + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); public override void Clear() diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs index e89c5f946..e2d317980 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { + using System; using System.Data.Entity; + using System.Linq.Expressions; using Orms.Infrastructure; public class Ef6DbSetWrapper : DbSetWrapperBase @@ -14,6 +16,11 @@ public Ef6DbSetWrapper(DbSet dbSet) _dbSet = dbSet; } + public override void Include(Expression> navigationPropertyPath) + { + _dbSet.Include(navigationPropertyPath); + } + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); public override void Clear() => _dbSet.RemoveRange(_dbSet); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs index 966519102..e3c7bde77 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs @@ -1,5 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure { + using System; + using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; @@ -14,6 +16,11 @@ public EfCore1DbSetWrapper(DbSet dbSet) _dbSet = dbSet; } + public override void Include(Expression> navigationPropertyPath) + { + _dbSet.Include(navigationPropertyPath); + } + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); public override void Clear() => _dbSet.RemoveRange(_dbSet); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs index fceb2c023..4f197b4e7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs @@ -1,5 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure { + using System; + using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; @@ -14,6 +16,11 @@ public EfCore2DbSetWrapper(DbSet dbSet) _dbSet = dbSet; } + public override void Include(Expression> navigationPropertyPath) + { + _dbSet.Include(navigationPropertyPath); + } + public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); public override void Clear() => _dbSet.RemoveRange(_dbSet); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs index 28ce6ba5e..561a29c1b 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs @@ -25,6 +25,8 @@ protected DbSetWrapperBase(IQueryable dbSet) public IQueryProvider Provider => _dbSet.Provider; + public abstract void Include(Expression> navigationPropertyPath); + public abstract void Add(TEntity itemToAdd); public abstract void Clear(); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs index d082682ad..361819619 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs @@ -1,9 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { + using System; using System.Linq; + using System.Linq.Expressions; public interface IDbSetWrapper : IQueryable { + void Include(Expression> navigationPropertyPath); + void Add(TEntity itemToAdd); void Clear(); From 916804ff2dc7f4144fee46608a9a5ff187aa4f39 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 13 Nov 2017 08:22:58 +0000 Subject: [PATCH 046/176] Support for passing a particular mapper to a projection / Start of test coverage for projection mapper caching --- ...ileMapper.UnitTests.MoreTestClasses.csproj | 13 ++++ .../MappingExtensions.cs | 18 +++++ .../packages.config | 4 ++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 12 ++++ .../WhenCreatingProjections.cs | 44 +++++++++++++ .../packages.config | 1 + .../Infrastructure/OrmTestClassBase.cs | 15 +++++ .../AgileMapper.UnitTests.csproj | 1 - AgileMapper.UnitTests/MappingExtensions.cs | 14 ---- .../WhenViewingMappingPlans.cs | 3 +- AgileMapper/Parameters.cs | 2 +- AgileMapper/ProjectionExtensions.cs | 65 +++++++++++++++---- AgileMapper/Properties/AssemblyInfo.cs | 1 + .../Api/ProjectionMapperSelector.cs | 21 ++++++ 14 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs create mode 100644 AgileMapper.UnitTests.MoreTestClasses/packages.config create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs delete mode 100644 AgileMapper.UnitTests/MappingExtensions.cs create mode 100644 AgileMapper/Queryables/Api/ProjectionMapperSelector.cs diff --git a/AgileMapper.UnitTests.MoreTestClasses/AgileMapper.UnitTests.MoreTestClasses.csproj b/AgileMapper.UnitTests.MoreTestClasses/AgileMapper.UnitTests.MoreTestClasses.csproj index a59351640..c7bb9ec94 100644 --- a/AgileMapper.UnitTests.MoreTestClasses/AgileMapper.UnitTests.MoreTestClasses.csproj +++ b/AgileMapper.UnitTests.MoreTestClasses/AgileMapper.UnitTests.MoreTestClasses.csproj @@ -51,6 +51,9 @@ ..\AgileMapper.snk + + ..\packages\Shouldly.2.8.3\lib\net40\Shouldly.dll + @@ -68,6 +71,7 @@ + @@ -77,5 +81,14 @@ false + + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs b/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs new file mode 100644 index 000000000..f18e7d8ee --- /dev/null +++ b/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.MoreTestClasses +{ + using System.Linq; + using Shouldly; + + public static class MappingExtensions + { + public static void RootMapperCountShouldBeOne(this IMapper mapper) + { + RootMapperCountShouldBe(mapper, 1); + } + + public static void RootMapperCountShouldBe(this IMapper mapper, int expected) + { + ((Mapper)mapper).Context.ObjectMapperFactory.RootMappers.Count().ShouldBe(expected); + } + } +} diff --git a/AgileMapper.UnitTests.MoreTestClasses/packages.config b/AgileMapper.UnitTests.MoreTestClasses/packages.config new file mode 100644 index 000000000..ff08b12e1 --- /dev/null +++ b/AgileMapper.UnitTests.MoreTestClasses/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 72a568cc0..eb6f1b369 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -83,6 +83,9 @@ ..\packages\Remotion.Linq.2.1.2\lib\net45\Remotion.Linq.dll + + ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll + ..\packages\System.Collections.Immutable.1.4.0\lib\netstandard2.0\System.Collections.Immutable.dll @@ -145,6 +148,7 @@ + @@ -156,10 +160,18 @@ + + {049e1ee5-48ce-441a-b166-3cf6bec17957} + AgileMapper.UnitTests.MoreTestClasses + {66522d44-19f5-4af5-9d43-483a3cd6f958} AgileMapper.UnitTests.Orms + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs new file mode 100644 index 000000000..ac1ee9181 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -0,0 +1,44 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using System.Linq; + using Infrastructure; + using MoreTestClasses; + using Orms.Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public class WhenCreatingProjections : OrmTestClassBase + { + public WhenCreatingProjections(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldReuseACachedProjectionMapper() + { + RunTest((context, mapper) => + { + var stringDtos = context + .StringItems + .ProjectTo(c => c.Using(mapper)) + .ToArray(); + + stringDtos.ShouldBeEmpty(); + + context.StringItems.Add(new PublicString { Id = 1, Value = "New!" }); + context.SaveChanges(); + + var moreStringDtos = context + .StringItems + .ProjectTo(c => c.Using(mapper)) + .ToArray(); + + moreStringDtos.ShouldHaveSingleItem(); + + mapper.RootMapperCountShouldBeOne(); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config index 3e4d8e4de..905853b0e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -12,6 +12,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 626075f53..5b91b1b96 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -36,6 +36,21 @@ protected void RunTest(Action testAction) } } + protected void RunTest(Action testAction) + { + try + { + using (var mapper = Mapper.CreateNew()) + { + testAction.Invoke(Context, mapper); + } + } + finally + { + EmptyDbContext(); + } + } + private void EmptyDbContext() { Context.Products.Clear(); diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index ee936d5f6..496360ca1 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -121,7 +121,6 @@ - diff --git a/AgileMapper.UnitTests/MappingExtensions.cs b/AgileMapper.UnitTests/MappingExtensions.cs deleted file mode 100644 index 59006cfba..000000000 --- a/AgileMapper.UnitTests/MappingExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests -{ - using System.Collections.Generic; - using System.Linq; - using ObjectPopulation; - - internal static class MappingExtensions - { - public static ICollection RootMappers(this IMapper mapper) - { - return ((Mapper)mapper).Context.ObjectMapperFactory.RootMappers.ToArray(); - } - } -} diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 7652d478f..d9377f6ac 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; + using MoreTestClasses; using Shouldly; using TestClasses; using Xunit; @@ -339,7 +340,7 @@ public void ShouldShowAllCachedMappingPlans() plan.ShouldContain("Rule set: Merge"); plan.ShouldContain("Rule set: Overwrite"); - mapper.RootMappers().Count.ShouldBe(5); + mapper.RootMapperCountShouldBe(5); } } } diff --git a/AgileMapper/Parameters.cs b/AgileMapper/Parameters.cs index 9f1d3b1c0..58c74a9c1 100644 --- a/AgileMapper/Parameters.cs +++ b/AgileMapper/Parameters.cs @@ -13,7 +13,7 @@ internal static class Parameters public static readonly ParameterExpression MappingData = Create(); public static readonly ParameterExpression ObjectMappingData = Create(); public static readonly ParameterExpression Queryable = Create(); - public static readonly ParameterExpression MapperInternal = Create(); + public static readonly ParameterExpression Mapper = Create(); public static ParameterExpression Create(string name = null) => Create(typeof(T), name); diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 75e1201fa..260b169e6 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -8,6 +8,7 @@ using NetStandardPolyfills; using ObjectPopulation; using Queryables; + using Queryables.Api; /// /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. @@ -15,19 +16,57 @@ public static class ProjectionExtensions { /// - /// + /// Project the elements of the given to instances of the given + /// . /// - /// - /// - /// + /// The source collection on which to perform the projection. + /// + /// The target Type to which the elements of the given should be projected. + /// + /// + /// An IQueryable of the given to instances of the given + /// . The projection is not performed until the Queryable is enumerated + /// by a call to .ToArray() or similar. + /// public static IQueryable ProjectTo(this IQueryable sourceQueryable) where TResultElement : class + { + return ProjectTo(sourceQueryable, Mapper.Default); + } + + /// + /// Project the elements of the given to instances of the given + /// . + /// + /// The source collection on which to perform the projection. + /// A func providing the mapper with which the projection should be performed. + /// + /// The target Type to which the elements of the given should be projected. + /// + /// + /// An IQueryable of the given to instances of the given + /// . The projection is not performed until the Queryable is enumerated + /// by a call to .ToArray() or similar. + /// + public static IQueryable ProjectTo( + this IQueryable sourceQueryable, + Func mapperSelector) + where TResultElement : class + { + var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); + + return ProjectTo(sourceQueryable, mapper); + } + + private static IQueryable ProjectTo( + IQueryable sourceQueryable, + IMapper mapper) + where TResultElement : class { var projectCaller = GlobalContext.Instance.Cache.GetOrAdd( new SourceAndTargetTypesKey(sourceQueryable.ElementType, typeof(TResultElement)), key => { - // ReSharper disable once PossibleNullReferenceException var projectQueryMethod = typeof(ProjectionExtensions) .GetNonPublicStaticMethod("ProjectQuery") .MakeGenericMethod(key.SourceType, key.TargetType); @@ -37,33 +76,35 @@ public static IQueryable ProjectTo(this IQueryab var projectQueryCall = Expression.Call( projectQueryMethod, Parameters.Queryable.GetConversionTo(typedSourceQueryable), - Parameters.MapperInternal); + Parameters.Mapper); - var projectQueryLambda = Expression.Lambda>>( + var projectQueryLambda = Expression.Lambda>>( projectQueryCall, Parameters.Queryable, - Parameters.MapperInternal); + Parameters.Mapper); return projectQueryLambda.Compile(); }); - return projectCaller.Invoke(sourceQueryable, Mapper.Default); + return projectCaller.Invoke(sourceQueryable, mapper); } internal static IQueryable ProjectQuery( IQueryable sourceQueryable, - IMapperInternal mapper) + IMapper mapper) { + var mapperContext = ((IMapperInternal)mapper).Context; + var projectorKey = new QueryProjectorKey( MappingTypes.Fixed, sourceQueryable, - mapper.Context); + mapperContext); var rootMappingData = ObjectMappingDataFactory .ForProjection, IQueryable>( projectorKey, sourceQueryable, - mapper.Context.QueryProjectionMappingContext); + mapperContext.QueryProjectionMappingContext); var queryProjection = rootMappingData.MapStart(); diff --git a/AgileMapper/Properties/AssemblyInfo.cs b/AgileMapper/Properties/AssemblyInfo.cs index 17e4b6765..ca45a9754 100644 --- a/AgileMapper/Properties/AssemblyInfo.cs +++ b/AgileMapper/Properties/AssemblyInfo.cs @@ -10,3 +10,4 @@ [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.NonParallel, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] +[assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.MoreTestClasses, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] diff --git a/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs b/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs new file mode 100644 index 000000000..f0e2bdfeb --- /dev/null +++ b/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.Queryables.Api +{ + /// + /// Provides the option to supply a particular with which to perform a query projection. + /// + public class ProjectionMapperSelector + { + internal static readonly ProjectionMapperSelector Instance = new ProjectionMapperSelector(); + + private ProjectionMapperSelector() + { + } + + /// + /// Use the given in this query projection. + /// + /// The to use in this query projection. + /// The to use in this query projection. + public IMapper Using(IMapper mapper) => mapper; + } +} \ No newline at end of file From e0fa34cd8cce4d60a99b93505820ccdf3263c6ce Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 13 Nov 2017 08:56:07 +0000 Subject: [PATCH 047/176] Test coverage for a simple flattening projection / Maintaining member population order when mapping to member binndings --- .../Infrastructure/Ef5TestDbContext.cs | 10 ++++ .../Infrastructure/Ef6TestDbContext.cs | 10 ++++ .../Infrastructure/EfCore1TestDbContext.cs | 10 ++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 10 ++++ .../WhenProjectingToComplexTypeMembers.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.csproj | 4 ++ .../Infrastructure/ITestDbContext.cs | 4 ++ .../TestClasses/Address.cs | 14 ++++++ .../TestClasses/Person.cs | 16 ++++++ .../TestClasses/PersonDto.cs | 15 ++++++ .../WhenProjectingToComplexTypeMembers.cs | 49 +++++++++++++++++++ .../MemberInitPopulationExpressionFactory.cs | 2 +- 13 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToComplexTypeMembers.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Address.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Person.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs create mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index b5d48842b..2126d5c52 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -20,6 +20,10 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet Products { get; set; } + public DbSet Persons { get; set; } + + public DbSet
Addresses { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -41,6 +45,12 @@ protected Ef5TestDbContext(DbConnection dbConnection) IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Persons + => new Ef5DbSetWrapper(Persons); + + IDbSetWrapper
ITestDbContext.Addresses + => new Ef5DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.BoolItems => new Ef5DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 369e9663e..e4e9db4be 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -20,6 +20,10 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet Products { get; set; } + public DbSet Persons { get; set; } + + public DbSet
Addresses { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -41,6 +45,12 @@ protected Ef6TestDbContext(DbConnection dbConnection) IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Persons + => new Ef6DbSetWrapper(Persons); + + IDbSetWrapper
ITestDbContext.Addresses + => new Ef6DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.BoolItems => new Ef6DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 5ad6d00c8..261243ec6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -18,6 +18,10 @@ public EfCore1TestDbContext() public DbSet Products { get; set; } + public DbSet Persons { get; set; } + + public DbSet
Addresses { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -39,6 +43,12 @@ public EfCore1TestDbContext() IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Persons + => new EfCore1DbSetWrapper(Persons); + + IDbSetWrapper
ITestDbContext.Addresses + => new EfCore1DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore1DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index eb6f1b369..350806079 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -150,6 +150,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 63ab5dc14..9d9cca6dd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -21,6 +21,10 @@ public EfCore2TestDbContext() public DbSet Products { get; set; } + public DbSet Persons { get; set; } + + public DbSet
Addresses { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -42,6 +46,12 @@ public EfCore2TestDbContext() IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Persons + => new EfCore2DbSetWrapper(Persons); + + IDbSetWrapper
ITestDbContext.Addresses + => new EfCore2DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore2DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToComplexTypeMembers.cs new file mode 100644 index 000000000..ed2a99e3e --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToComplexTypeMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToComplexTypeMembers : WhenProjectingToComplexTypeMembers + { + public WhenProjectingToComplexTypeMembers(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 24932cac9..2301a2b7a 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -85,6 +85,9 @@ + + + @@ -102,6 +105,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 205269a99..244dee887 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -13,6 +13,10 @@ public interface ITestDbContext : IDisposable IDbSetWrapper Products { get; } + IDbSetWrapper Persons { get; } + + IDbSetWrapper
Addresses { get; } + IDbSetWrapper BoolItems { get; } IDbSetWrapper ShortItems { get; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Address.cs b/AgileMapper.UnitTests.Orms/TestClasses/Address.cs new file mode 100644 index 000000000..418caee4e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Address.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class Address + { + [Key] + public int AddressId { get; set; } + + public string Line1 { get; set; } + + public string Line2 { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Person.cs b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs new file mode 100644 index 000000000..baa0a828a --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class Person + { + [Key] + public int PersonId { get; set; } + + public string Name { get; set; } + + public int AddressId { get; set; } + + public Address Address { get; set; } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs new file mode 100644 index 000000000..0dbe02fe5 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class PersonDto + { + public int Id { get; set; } + + public string Name { get; set; } + + public int AddressId { get; set; } + + public string AddressLine1 { get; set; } + + public string AddressLine2 { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs new file mode 100644 index 000000000..d67b0fa93 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -0,0 +1,49 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenProjectingToComplexTypeMembers : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingToComplexTypeMembers(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectToAComplexTypeMember() + { + RunTest(context => + { + var person = new Person + { + PersonId = 1, + Name = "Test Db", + AddressId = 2, + Address = new Address + { + AddressId = 2, + Line1 = "Test Db Line 1", + Line2 = "Test Db Line 2" + } + }; + + context.Addresses.Add(person.Address); + context.Persons.Add(person); + context.SaveChanges(); + + var personDto = context.Persons.ProjectTo().First(); + + personDto.Id.ShouldBe(1); + personDto.Name.ShouldBe("Test Db"); + personDto.AddressId.ShouldBe(2); + personDto.AddressLine1.ShouldBe("Test Db Line 1"); + personDto.AddressLine2.ShouldBe("Test Db Line 2"); + }); + } + } +} diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs index 1f116d1e6..4e9f7ccb3 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs @@ -67,7 +67,7 @@ private static ICollection GetMemberBindingsFrom(IList Date: Tue, 14 Nov 2017 17:56:48 +0000 Subject: [PATCH 048/176] Test coverage for simple navigation property mapping in EF5 + EF6 / Short-circuiting default expression conversion for non-value types --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenProjectingToComplexTypeMembers.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenProjectingToComplexTypeMembers.cs | 13 +++++++++++++ .../WhenProjectingToComplexTypeMembers.cs | 6 +++--- .../Queryables/DefaultExpressionConverter.cs | 5 +++++ 6 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToComplexTypeMembers.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 5d54fc67f..490ac8d05 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -94,6 +94,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs new file mode 100644 index 000000000..b9975e884 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToComplexTypeMembers : WhenProjectingToComplexTypeMembers + { + public WhenProjectingToComplexTypeMembers(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 240f69451..54b35bd09 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -97,6 +97,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToComplexTypeMembers.cs new file mode 100644 index 000000000..e5a53e5c3 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToComplexTypeMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToComplexTypeMembers : WhenProjectingToComplexTypeMembers + { + public WhenProjectingToComplexTypeMembers(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index d67b0fa93..927c6d46b 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -23,10 +23,10 @@ public void ShouldProjectToAComplexTypeMember() { PersonId = 1, Name = "Test Db", - AddressId = 2, + AddressId = 1, Address = new Address { - AddressId = 2, + AddressId = 1, Line1 = "Test Db Line 1", Line2 = "Test Db Line 2" } @@ -40,7 +40,7 @@ public void ShouldProjectToAComplexTypeMember() personDto.Id.ShouldBe(1); personDto.Name.ShouldBe("Test Db"); - personDto.AddressId.ShouldBe(2); + personDto.AddressId.ShouldBe(1); personDto.AddressLine1.ShouldBe("Test Db Line 1"); personDto.AddressLine2.ShouldBe("Test Db Line 2"); }); diff --git a/AgileMapper/Queryables/DefaultExpressionConverter.cs b/AgileMapper/Queryables/DefaultExpressionConverter.cs index 6749b6e67..b4abc2e41 100644 --- a/AgileMapper/Queryables/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/DefaultExpressionConverter.cs @@ -12,6 +12,11 @@ public static Expression Convert(DefaultExpression defaultExpression) private static object GetDefaultValueFor(Type type) { + if (!type.IsValueType()) + { + return null; + } + var getDefaultValueCaller = GlobalContext.Instance.Cache.GetOrAdd(type, t => { var getDefaultValueCall = Expression From 55b80d653812fd1b5cbb3b8d524d07a2f1b8a039 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 14 Nov 2017 18:31:56 +0000 Subject: [PATCH 049/176] Skipping projection null-member guarding as it's handled by the provider! --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenProjectingToComplexTypeMembers.cs | 13 +++++++++ .../Infrastructure/OrmTestClassBase.cs | 2 ++ AgileMapper/MappingRuleSetCollection.cs | 6 ++-- AgileMapper/MappingRuleSetSettings.cs | 2 +- AgileMapper/Members/ExpressionInfoFinder.cs | 28 ++++++------------- .../Members/MemberMapperDataExtensions.cs | 8 ++++-- 7 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToComplexTypeMembers.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index e06b31f5b..ea9c4b57a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -211,6 +211,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToComplexTypeMembers.cs new file mode 100644 index 000000000..901e0ebb5 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToComplexTypeMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToComplexTypeMembers : WhenProjectingToComplexTypeMembers + { + public WhenProjectingToComplexTypeMembers(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 5b91b1b96..c3ca27acc 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -54,6 +54,8 @@ protected void RunTest(Action testAction) private void EmptyDbContext() { Context.Products.Clear(); + Context.Addresses.Clear(); + Context.Persons.Clear(); Context.BoolItems.Clear(); Context.ShortItems.Clear(); Context.IntItems.Clear(); diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 8648f1ddd..8ecedb6d5 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -16,7 +16,7 @@ internal class MappingRuleSetCollection { SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardStringAccesses = true + GuardMemberAccesses = true }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, @@ -29,7 +29,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardStringAccesses = true + GuardMemberAccesses = true }, MergeEnumerablePopulationStrategy.Instance, PreserveExistingValueMemberPopulationGuardFactory.Instance, @@ -42,7 +42,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardStringAccesses = true + GuardMemberAccesses = true }, OverwriteEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index d09860dc1..11ee436bb 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -12,6 +12,6 @@ internal class MappingRuleSetSettings public bool UseTryCatch { get; set; } - public bool GuardStringAccesses { get; set; } + public bool GuardMemberAccesses { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Members/ExpressionInfoFinder.cs b/AgileMapper/Members/ExpressionInfoFinder.cs index c4323073c..17e9f3e38 100644 --- a/AgileMapper/Members/ExpressionInfoFinder.cs +++ b/AgileMapper/Members/ExpressionInfoFinder.cs @@ -15,12 +15,9 @@ public ExpressionInfoFinder(Expression mappingDataObject) _mappingDataObject = mappingDataObject; } - public ExpressionInfo FindIn( - Expression expression, - bool targetCanBeNull, - bool guardStringAccesses) + public ExpressionInfo FindIn(Expression expression, bool targetCanBeNull) { - var finder = new ExpressionInfoFinderInstance(_mappingDataObject, targetCanBeNull, guardStringAccesses); + var finder = new ExpressionInfoFinderInstance(_mappingDataObject, targetCanBeNull); var info = finder.FindIn(expression); return info; @@ -35,25 +32,16 @@ private class ExpressionInfoFinderInstance : ExpressionVisitor private readonly ICollection _nullCheckSubjects; private readonly Dictionary _nestedAccessesByPath; private readonly bool _includeTargetNullChecking; - private readonly bool _guardStringAccesses; - public ExpressionInfoFinderInstance( - Expression mappingDataObject, - bool targetCanBeNull, - bool guardStringAccesses) + public ExpressionInfoFinderInstance(Expression mappingDataObject, bool targetCanBeNull) { _mappingDataObject = mappingDataObject; + _stringMemberAccessSubjects = new List(); _allInvocations = new List(); _multiInvocations = new List(); _nullCheckSubjects = new List(); _nestedAccessesByPath = new Dictionary(); _includeTargetNullChecking = targetCanBeNull; - _guardStringAccesses = guardStringAccesses; - - //if (guardStringAccesses) - { - _stringMemberAccessSubjects = new List(); - } } public ExpressionInfo FindIn(Expression expression) @@ -176,10 +164,7 @@ private void AddExistingNullCheck(Expression checkedAccess) private void AddStringMemberAccessSubjectIfAppropriate(Expression member) { - if (_guardStringAccesses && - (member != null) && - (member.Type == typeof(string)) && - AccessSubjectCouldBeNull(member)) + if ((member?.Type == typeof(string)) && AccessSubjectCouldBeNull(member)) { _stringMemberAccessSubjects.Add(member); } @@ -263,6 +248,9 @@ private bool AddMemberAccess(Expression memberAccess) public class ExpressionInfo { + public static readonly ExpressionInfo Empty = + new ExpressionInfo(Enumerable.EmptyArray, Enumerable.EmptyArray); + public ExpressionInfo( IList nestedAccesses, IList multiInvocations) diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 48b7a9d05..2d87e5407 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -72,10 +72,14 @@ public static ExpressionInfoFinder.ExpressionInfo GetExpressionInfoFor( Expression value, bool targetCanBeNull) { + if (!mapperData.RuleSet.Settings.GuardMemberAccesses) + { + return ExpressionInfoFinder.ExpressionInfo.Empty; + } + return mapperData.ExpressionInfoFinder.FindIn( value, - targetCanBeNull, - mapperData.RuleSet.Settings.GuardStringAccesses); + targetCanBeNull); } public static bool SourceIsNotFlatObject(this IMemberMapperData mapperData) From 571de7413600b6d4dcfa0b23f708f1b6bb8bc5f7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 15 Nov 2017 18:47:56 +0000 Subject: [PATCH 050/176] Support for string -> Guid projection in EF Core --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../Infrastructure/Ef5TestDbContext.cs | 3 +- .../WhenConvertingToGuids.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Infrastructure/Ef6TestDbContext.cs | 2 + .../WhenConvertingToGuids.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../Infrastructure/EfCore1TestDbContext.cs | 2 + .../WhenConvertingToGuids.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 2 + .../WhenConvertingToGuids.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../Infrastructure/ITestDbContext.cs | 2 + .../WhenConvertingToGuids.cs | 43 +++++++++++++++ .../TestClasses/PublicGuidDto.cs | 12 +++++ .../Extensions/ExpressionExtensions.cs | 4 ++ .../Settings/DefaultQueryProviderSettings.cs | 53 ++++++++++++++++--- .../Settings/Ef5QueryProviderSettings.cs | 8 +-- .../Settings/Ef6QueryProviderSettings.cs | 10 ++-- .../QueryProviderSettingsExtensions.cs | 39 ++++---------- 21 files changed, 187 insertions(+), 51 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicGuidDto.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 490ac8d05..9bcf74570 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 2126d5c52..ab7a24c49 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -17,7 +17,6 @@ protected Ef5TestDbContext(DbConnection dbConnection) : base(dbConnection, true) { } - public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -42,6 +41,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public virtual bool StringToDateTimeValidationSupported => false; + public bool StringToGuidConversionSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs new file mode 100644 index 000000000..a467dd39a --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToGuids : WhenConvertingToGuids + { + public WhenConvertingToGuids(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 54b35bd09..f93c08d2f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,6 +91,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index e4e9db4be..d66994aa3 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -42,6 +42,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public virtual bool StringToDateTimeValidationSupported => false; + public virtual bool StringToGuidConversionSupported => false; + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs new file mode 100644 index 000000000..990289aab --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToGuids : WhenConvertingToGuids + { + public WhenConvertingToGuids(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index ea9c4b57a..5293c4e28 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -207,6 +207,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 261243ec6..63495c8da 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -40,6 +40,8 @@ public EfCore1TestDbContext() public bool StringToDateTimeValidationSupported => false; + public bool StringToGuidConversionSupported => true; + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs new file mode 100644 index 000000000..921cb0af4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToGuids : WhenConvertingToGuids + { + public WhenConvertingToGuids(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 350806079..d228bada7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -145,6 +145,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 9d9cca6dd..2d67b8588 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -43,6 +43,8 @@ public EfCore2TestDbContext() public bool StringToDateTimeValidationSupported => false; + public bool StringToGuidConversionSupported => true; + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs new file mode 100644 index 000000000..a717d59bb --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToGuids : WhenConvertingToGuids + { + public WhenConvertingToGuids(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 2301a2b7a..0b0267e46 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -83,12 +83,14 @@ + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 244dee887..8060c25b0 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -11,6 +11,8 @@ public interface ITestDbContext : IDisposable bool StringToDateTimeValidationSupported { get; } + bool StringToGuidConversionSupported { get; } + IDbSetWrapper Products { get; } IDbSetWrapper Persons { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs new file mode 100644 index 000000000..0e6601467 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToGuids : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToGuids(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAParseableStringToAGuidAsExpected() + { + void Test(TOrmContext context) + { + var guid = Guid.NewGuid(); + + context.StringItems.Add(new PublicString { Value = guid.ToString() }); + context.SaveChanges(); + + var guidItem = context.StringItems.ProjectTo().First(); + + guidItem.Value.ShouldBe(guid); + } + + if (Context.StringToGuidConversionSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicGuidDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicGuidDto.cs new file mode 100644 index 000000000..577436e0b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicGuidDto.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + + public class PublicGuidDto + { + public int Id { get; set; } + + + public Guid Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Extensions/ExpressionExtensions.cs b/AgileMapper/Extensions/ExpressionExtensions.cs index 1850b0ef3..87c61ba50 100644 --- a/AgileMapper/Extensions/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/ExpressionExtensions.cs @@ -154,6 +154,10 @@ public static Expression GetValueOrDefaultCall(this Expression nullableExpressio return Expression.Call(nullableExpression, parameterlessGetValueOrDefault); } + [DebuggerStepThrough] + public static Expression GetConversionTo(this Expression expression) + => GetConversionTo(expression, typeof(T)); + [DebuggerStepThrough] public static Expression GetConversionTo(this Expression expression, Type targetType) => (expression.Type != targetType) ? Expression.Convert(expression, targetType) : expression; diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index c329d1af5..d3468cf62 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -1,13 +1,10 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { -#if !NET_STANDARD using System; using System.Linq; -#endif using System.Linq.Expressions; -#if !NET_STANDARD - using System.Reflection; -#endif + using Extensions; + using NetStandardPolyfills; internal class DefaultQueryProviderSettings : IQueryProviderSettings { @@ -33,10 +30,50 @@ public DefaultQueryProviderSettings() public virtual bool SupportsToString => false; - public virtual Expression ConvertToStringCall(MethodCallExpression call) => call; + public virtual Expression ConvertToStringCall(MethodCallExpression call) + => call.Object.GetConversionTo(); + + public Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) + { + if (call.Method.DeclaringType == typeof(Guid)) + { + return GetConvertStringToGuid(call, fallbackValue); + } + + Expression conversion; + + if ((call.Method.DeclaringType == typeof(DateTime)) && + (conversion = GetParseStringToDateTimeOrNull(call, fallbackValue)) != null) + { + return conversion; + } + + // ReSharper disable once PossibleNullReferenceException + // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, + // but it at least gives a decent error message: + var convertMethodName = "To" + call.Method.DeclaringType.Name; - public virtual Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) - => this.GetConvertToTypeCall(call); + var convertMethod = typeof(Convert) + .GetPublicStaticMethods(convertMethodName) + .First(m => m.GetParameters().HasOne() && (m.GetParameters()[0].ParameterType == typeof(string))); + + conversion = Expression.Call(convertMethod, call.Arguments.First()); + + return conversion; + } + + protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) + => null; + + private static Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) + { + var parseMethod = typeof(Guid) + .GetPublicStaticMethod("Parse", parameterCount: 1); + + var guidConversion = Expression.Call(parseMethod, guidTryParseCall.Arguments.First()); + + return guidConversion; + } #if !NET_STANDARD protected static Type GetTypeOrNull(string loadedAssemblyName, string typeName) diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 5eb51d3c2..e2d3fff12 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -53,12 +53,8 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct subject); } - public override Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) - { - return this.TryGetDateTimeFromStringCall(call, fallbackValue, out var convertedCall) - ? convertedCall - : base.ConvertTryParseCall(call, fallbackValue); - } + protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) + => QueryProviderSettingsExtensions.GetParseStringToDateTimeOrNull(this, call, fallbackValue); #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 035da2e0c..0e20b00a0 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { +#if !NET_STANDARD using System; using System.Linq.Expressions; +#endif internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { @@ -14,12 +16,8 @@ protected override Type LoadCanonicalFunctionsType() protected override Type LoadSqlFunctionsType() => GetTypeOrNull("EntityFramework.SqlServer", "System.Data.Entity.SqlServer.SqlFunctions"); - public override Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) - { - return this.TryGetDateTimeFromStringCall(call, fallbackValue, out var convertedCall) - ? convertedCall - : base.ConvertTryParseCall(call, fallbackValue); - } + protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) + => QueryProviderSettingsExtensions.GetParseStringToDateTimeOrNull(this, call, fallbackValue); #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 5eb8ead0c..d621f5a2b 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -1,45 +1,27 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { +#if !NET_STANDARD using System; - using System.Linq; using System.Linq.Expressions; + using System.Linq; using System.Reflection; using Extensions; using NetStandardPolyfills; +#endif internal static class QueryProviderSettingsExtensions { - public static Expression GetConvertToTypeCall( - this IQueryProviderSettings settings, - MethodCallExpression tryParseCall) - { - // ReSharper disable once PossibleNullReferenceException - // Attempt to use Convert.ToInt32 - irretrievably unsupported in non-EDMX EF5 and EF6, - // but it at least gives a decent error message: - var convertMethodName = "To" + tryParseCall.Method.DeclaringType.Name; - - var convertMethod = typeof(Convert) - .GetPublicStaticMethods(convertMethodName) - .First(m => m.GetParameters().HasOne() && (m.GetParameters()[0].ParameterType == typeof(string))); - - var convertCall = Expression.Call(convertMethod, tryParseCall.Arguments.First()); - - return convertCall; - } - #if !NET_STANDARD - public static bool TryGetDateTimeFromStringCall( + public static Expression GetParseStringToDateTimeOrNull( this IQueryProviderSettings settings, MethodCallExpression tryParseCall, - Expression fallbackValue, - out Expression convertedCall) + Expression fallbackValue) { if ((tryParseCall.Method.DeclaringType != typeof(DateTime)) || (settings.CanonicalFunctionsType == null) || (settings.SqlFunctionsType == null)) { - convertedCall = null; - return false; + return null; } var createDateTimeMethod = settings @@ -48,8 +30,7 @@ public static bool TryGetDateTimeFromStringCall( if (createDateTimeMethod == null) { - convertedCall = null; - return false; + return null; } var datePartMethod = settings @@ -59,8 +40,7 @@ public static bool TryGetDateTimeFromStringCall( if (datePartMethod == null) { - convertedCall = null; - return false; + return null; } var sourceValue = tryParseCall.Arguments[0]; @@ -85,8 +65,7 @@ public static bool TryGetDateTimeFromStringCall( var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); var convertedOrFallback = Expression.Condition(sourceIsNotNull, createdDateTime, fallbackValue); - convertedCall = convertedOrFallback; - return true; + return convertedOrFallback; } private static Expression GetDatePartCall( From 92159862e5f16439ba553acc77b89c89da58a646 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 15 Nov 2017 19:00:37 +0000 Subject: [PATCH 051/176] Handling null strings in EF Core string -> Guid projections --- .../WhenConvertingToGuids.cs | 23 +++++++++++++++++++ .../Queryables/DefaultExpressionConverter.cs | 3 +++ .../Settings/DefaultQueryProviderSettings.cs | 14 +++++++++-- .../QueryProviderSettingsExtensions.cs | 8 +++---- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index 0e6601467..2ad1cb00e 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -39,5 +39,28 @@ void Test(TOrmContext context) RunTestAndExpectThrow(Test); } } + + [Fact] + public void ShouldProjectANullStringToAGuidAsExpected() + { + void Test(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var guidItem = context.StringItems.ProjectTo().First(); + + guidItem.Value.ShouldBe(default(Guid)); + } + + if (Context.StringToGuidConversionSupported) + { + RunTest(Test); + } + else + { + RunTestAndExpectThrow(Test); + } + } } } diff --git a/AgileMapper/Queryables/DefaultExpressionConverter.cs b/AgileMapper/Queryables/DefaultExpressionConverter.cs index b4abc2e41..2e315217a 100644 --- a/AgileMapper/Queryables/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/DefaultExpressionConverter.cs @@ -7,6 +7,9 @@ internal static class DefaultExpressionConverter { + public static Expression Convert(Expression defaultExpression) + => Convert((DefaultExpression)defaultExpression); + public static Expression Convert(DefaultExpression defaultExpression) => GetDefaultValueFor(defaultExpression.Type).ToConstantExpression(defaultExpression.Type); diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index d3468cf62..42a4382f3 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -70,9 +70,19 @@ private static Expression GetConvertStringToGuid(MethodCallExpression guidTryPar var parseMethod = typeof(Guid) .GetPublicStaticMethod("Parse", parameterCount: 1); - var guidConversion = Expression.Call(parseMethod, guidTryParseCall.Arguments.First()); + var sourceValue = guidTryParseCall.Arguments.First(); + var guidConversion = Expression.Call(parseMethod, sourceValue); - return guidConversion; + if (fallbackValue.NodeType == ExpressionType.Default) + { + fallbackValue = DefaultExpressionConverter.Convert(fallbackValue); + } + + var nullString = default(string).ToConstantExpression(); + var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); + var convertedOrFallback = Expression.Condition(sourceIsNotNull, guidConversion, fallbackValue); + + return convertedOrFallback; } #if !NET_STANDARD diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index d621f5a2b..1acade11b 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -14,10 +14,10 @@ internal static class QueryProviderSettingsExtensions #if !NET_STANDARD public static Expression GetParseStringToDateTimeOrNull( this IQueryProviderSettings settings, - MethodCallExpression tryParseCall, + MethodCallExpression dateTimeTryParseCall, Expression fallbackValue) { - if ((tryParseCall.Method.DeclaringType != typeof(DateTime)) || + if ((dateTimeTryParseCall.Method.DeclaringType != typeof(DateTime)) || (settings.CanonicalFunctionsType == null) || (settings.SqlFunctionsType == null)) { @@ -43,7 +43,7 @@ public static Expression GetParseStringToDateTimeOrNull( return null; } - var sourceValue = tryParseCall.Arguments[0]; + var sourceValue = dateTimeTryParseCall.Arguments[0]; var createDateTimeCall = Expression.Call( createDateTimeMethod, @@ -56,7 +56,7 @@ public static Expression GetParseStringToDateTimeOrNull( if (fallbackValue.NodeType == ExpressionType.Default) { - fallbackValue = DefaultExpressionConverter.Convert((DefaultExpression)fallbackValue); + fallbackValue = DefaultExpressionConverter.Convert(fallbackValue); } var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); From 9d2ee60845ecf5dd1ab2d827d651fa97b9d89187 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 15 Nov 2017 19:07:12 +0000 Subject: [PATCH 052/176] Tidying --- .../Infrastructure/Ef5TestDbContext.cs | 1 + AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs | 2 +- AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs | 2 +- AgileMapper/Queryables/Settings/IQueryProviderSettings.cs | 2 ++ .../Queryables/Settings/QueryProviderSettingsExtensions.cs | 6 ++---- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index ab7a24c49..b7502544a 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -17,6 +17,7 @@ protected Ef5TestDbContext(DbConnection dbConnection) : base(dbConnection, true) { } + public DbSet Products { get; set; } public DbSet Persons { get; set; } diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index e2d3fff12..26fa03b0f 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -54,7 +54,7 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct } protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) - => QueryProviderSettingsExtensions.GetParseStringToDateTimeOrNull(this, call, fallbackValue); + => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 0e20b00a0..726e525a8 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -17,7 +17,7 @@ protected override Type LoadSqlFunctionsType() => GetTypeOrNull("EntityFramework.SqlServer", "System.Data.Entity.SqlServer.SqlFunctions"); protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) - => QueryProviderSettingsExtensions.GetParseStringToDateTimeOrNull(this, call, fallbackValue); + => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); #endif } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 0b40be3ce..ed46fb6e4 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { +#if !NET_STANDARD using System; +#endif using System.Linq.Expressions; internal interface IQueryProviderSettings diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 1acade11b..0561f18e0 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -12,14 +12,12 @@ internal static class QueryProviderSettingsExtensions { #if !NET_STANDARD - public static Expression GetParseStringToDateTimeOrNull( + public static Expression GetCreateDateTimeFromStringOrNull( this IQueryProviderSettings settings, MethodCallExpression dateTimeTryParseCall, Expression fallbackValue) { - if ((dateTimeTryParseCall.Method.DeclaringType != typeof(DateTime)) || - (settings.CanonicalFunctionsType == null) || - (settings.SqlFunctionsType == null)) + if ((settings.CanonicalFunctionsType == null) || (settings.SqlFunctionsType == null)) { return null; } From 4d7ce4026349eec8f9506a250761cdb20af0e6fc Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 17 Nov 2017 18:33:50 +0000 Subject: [PATCH 053/176] Reorganising DateTime projection tests to get clear test names --- .../Infrastructure/Ef5TestLocalDbContext.cs | 4 - .../WhenConvertingToDateTimes.cs | 20 ++++- .../Infrastructure/Ef5TestDbContext.cs | 4 - .../WhenConvertingToDateTimes.cs | 19 +++- .../Infrastructure/Ef6TestLocalDbContext.cs | 4 - .../WhenConvertingToDateTimes.cs | 19 +++- .../Infrastructure/Ef6TestDbContext.cs | 4 - .../Infrastructure/InMemoryEf6TestContext.cs | 1 - .../WhenConvertingToDateTimes.cs | 20 ++++- .../Infrastructure/EfCore1TestDbContext.cs | 4 - .../WhenConvertingToDateTimes.cs | 19 +++- .../Infrastructure/EfCore2TestDbContext.cs | 4 - .../WhenConvertingToDateTimes.cs | 19 +++- .../AgileMapper.UnitTests.Orms.csproj | 6 +- .../Infrastructure/ITestDbContext.cs | 4 - .../IDateTimeConversionFailureTest.cs | 9 ++ .../DateTimes/IDateTimeConverterTest.cs | 9 ++ .../IDateTimeValidationFailureTest.cs | 7 ++ .../DateTimes/IDateTimeValidatorTest.cs | 7 ++ .../DateTimes/WhenConvertingToDateTimes.cs | 79 ++++++++++++++++ .../WhenConvertingToDateTimes.cs | 89 ------------------- 21 files changed, 224 insertions(+), 127 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs index d082e1f73..c37d5210f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Infrastructure/Ef5TestLocalDbContext.cs @@ -12,10 +12,6 @@ public Ef5TestLocalDbContext() { } - public override bool StringToDateTimeConversionSupported => true; - - public override bool StringToDateTimeValidationSupported => true; - void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 5afd2e2e0..fa996feef 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,13 +2,29 @@ { using Infrastructure; using Orms.Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConverterTest, + IDateTimeValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToADateTime() + => RunShouldProjectAParseableStringToADateTime(); + + [Fact] + public void ShouldProjectANullStringToADateTime() + => RunShouldProjectANullStringToADateTime(); + + [Fact] + public void ShouldProjectAnUnparseableStringToADateTime() + => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index b7502544a..fbfa4cd69 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -38,10 +38,6 @@ protected Ef5TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; - public virtual bool StringToDateTimeConversionSupported => false; - - public virtual bool StringToDateTimeValidationSupported => false; - public bool StringToGuidConversionSupported => false; IDbSetWrapper ITestDbContext.Products diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index e8019551c..4e329edf3 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,12 +2,29 @@ { using Infrastructure; using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConversionFailureTest, + IDateTimeValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToADateTime() + => RunShouldErrorProjectingAParseableStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingANullStringToADateTime() + => RunShouldErrorProjectingANullStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToADateTime() + => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs index a6f0d6774..fde6e1b82 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/Infrastructure/Ef6TestLocalDbContext.cs @@ -12,10 +12,6 @@ public Ef6TestLocalDbContext() { } - public override bool StringToDateTimeConversionSupported => true; - - public override bool StringToDateTimeValidationSupported => true; - void ITestLocalDbContext.CreateDatabase() => Database.Create(); void ITestLocalDbContext.DeleteDatabase() => Database.Delete(); diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index e64a28521..ebd404fe1 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -3,12 +3,29 @@ using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConverterTest, + IDateTimeValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToADateTime() + => RunShouldProjectAParseableStringToADateTime(); + + [Fact] + public void ShouldProjectANullStringToADateTime() + => RunShouldProjectANullStringToADateTime(); + + [Fact] + public void ShouldProjectAnUnparseableStringToADateTime() + => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index d66994aa3..022f55311 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -38,10 +38,6 @@ protected Ef6TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; - public virtual bool StringToDateTimeConversionSupported => false; - - public virtual bool StringToDateTimeValidationSupported => false; - public virtual bool StringToGuidConversionSupported => false; IDbSetWrapper ITestDbContext.Products diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs index 25bbb61b7..39d6d2121 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Infrastructure { - using System.Data.Entity.SqlServer; using Orms.Infrastructure; public class InMemoryEf6TestContext : InMemoryOrmTestContext diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 343734bdb..989c0648a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,13 +1,29 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConversionFailureTest, + IDateTimeValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToADateTime() + => RunShouldErrorProjectingAParseableStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingANullStringToADateTime() + => RunShouldErrorProjectingANullStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToADateTime() + => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 63495c8da..85325fd4d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -36,10 +36,6 @@ public EfCore1TestDbContext() public bool StringToNumberConversionSupported => true; - public bool StringToDateTimeConversionSupported => true; - - public bool StringToDateTimeValidationSupported => false; - public bool StringToGuidConversionSupported => true; IDbSetWrapper ITestDbContext.Products diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs index dcfdfa687..264a31828 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,12 +2,29 @@ { using Infrastructure; using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConverterTest, + IDateTimeValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToADateTime() + => RunShouldProjectAParseableStringToADateTime(); + + [Fact] + public void ShouldProjectANullStringToADateTime() + => RunShouldProjectANullStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToADateTime() + => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 2d67b8588..9c367f7bc 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -39,10 +39,6 @@ public EfCore2TestDbContext() public bool StringToNumberConversionSupported => true; - public bool StringToDateTimeConversionSupported => true; - - public bool StringToDateTimeValidationSupported => false; - public bool StringToGuidConversionSupported => true; IDbSetWrapper ITestDbContext.Products diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 12a75c03c..64eabeef9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,12 +2,29 @@ { using Infrastructure; using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.DateTimes; + using Xunit; - public class WhenConvertingToDateTimes : WhenConvertingToDateTimes + public class WhenConvertingToDateTimes : + WhenConvertingToDateTimes, + IDateTimeConverterTest, + IDateTimeValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToADateTime() + => RunShouldProjectAParseableStringToADateTime(); + + [Fact] + public void ShouldProjectANullStringToADateTime() + => RunShouldProjectANullStringToADateTime(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToADateTime() + => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 0b0267e46..8b3a3f605 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -83,8 +83,12 @@ + + + + - + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 8060c25b0..a680197b8 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -7,10 +7,6 @@ public interface ITestDbContext : IDisposable { bool StringToNumberConversionSupported { get; } - bool StringToDateTimeConversionSupported { get; } - - bool StringToDateTimeValidationSupported { get; } - bool StringToGuidConversionSupported { get; } IDbSetWrapper Products { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs new file mode 100644 index 000000000..a94e80ca2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +{ + public interface IDateTimeConversionFailureTest + { + void ShouldErrorProjectingAParseableStringToADateTime(); + + void ShouldErrorProjectingANullStringToADateTime(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs new file mode 100644 index 000000000..97e37d5da --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +{ + public interface IDateTimeConverterTest + { + void ShouldProjectAParseableStringToADateTime(); + + void ShouldProjectANullStringToADateTime(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs new file mode 100644 index 000000000..5e21d41bc --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +{ + public interface IDateTimeValidationFailureTest + { + void ShouldErrorProjectingAnUnparseableStringToADateTime(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs new file mode 100644 index 000000000..e2e24da10 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +{ + public interface IDateTimeValidatorTest + { + void ShouldProjectAnUnparseableStringToADateTime(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs new file mode 100644 index 000000000..51c6fbdf6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs @@ -0,0 +1,79 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +{ + using System; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + + public abstract class WhenConvertingToDateTimes : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToDateTimes(ITestContext context) + : base(context) + { + } + + #region Parseable String -> DateTime + + protected void RunShouldProjectAParseableStringToADateTime() + => RunTest(ProjectParseableStringToADateTime); + + protected void RunShouldErrorProjectingAParseableStringToADateTime() + => RunTestAndExpectThrow(ProjectParseableStringToADateTime); + + private static void ProjectParseableStringToADateTime(TOrmContext context) + { + var now = DateTime.Now; + + context.StringItems.Add(new PublicString { Value = now.ToString("s") }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ShouldBe(now, TimeSpan.FromSeconds(1)); + } + + #endregion + + #region Null String -> DateTime + + protected void RunShouldProjectANullStringToADateTime() + => RunTest(ProjectANullStringToADateTime); + + protected void RunShouldErrorProjectingANullStringToADateTime() + => RunTestAndExpectThrow(ProjectANullStringToADateTime); + + private static void ProjectANullStringToADateTime(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ShouldBe(default(DateTime)); + } + + #endregion + + #region Unparseable String -> DateTime + + protected void RunShouldProjectAnUnparseableStringToADateTime() + => RunTest(ProjectAnUnparseableStringToADateTime); + + protected void RunShouldErrorProjectingAnUnparseableStringToADateTime() + => RunTestAndExpectThrow(ProjectAnUnparseableStringToADateTime); + + private static void ProjectAnUnparseableStringToADateTime(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); + context.SaveChanges(); + + var dateTimeItem = context.StringItems.ProjectTo().First(); + + dateTimeItem.Value.ShouldBe(default(DateTime)); + } + + #endregion + } +} diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs deleted file mode 100644 index 843a56b03..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion -{ - using System; - using System.Linq; - using Infrastructure; - using Shouldly; - using TestClasses; - using Xunit; - - public abstract class WhenConvertingToDateTimes : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenConvertingToDateTimes(ITestContext context) - : base(context) - { - } - - [Fact] - public void ShouldProjectAParseableStringToADateTimeAsExpected() - { - void Test(TOrmContext context) - { - var now = DateTime.Now; - - context.StringItems.Add(new PublicString { Value = now.ToString("s") }); - context.SaveChanges(); - - var dateTimeItem = context.StringItems.ProjectTo().First(); - - dateTimeItem.Value.ShouldBe(now, TimeSpan.FromSeconds(1)); - } - - if (Context.StringToDateTimeConversionSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - - [Fact] - public void ShouldProjectANullStringToADateTimeAsExpected() - { - void Test(TOrmContext context) - { - context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); - - var dateTimeItem = context.StringItems.ProjectTo().First(); - - dateTimeItem.Value.ShouldBe(default(DateTime)); - } - - if (Context.StringToDateTimeConversionSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - - [Fact] - public void ShouldProjectAnUnparseableStringToADateTimeAsExpected() - { - void Test(TOrmContext context) - { - context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); - context.SaveChanges(); - - var dateTimeItem = context.StringItems.ProjectTo().First(); - - dateTimeItem.Value.ShouldBe(default(DateTime)); - } - - if (Context.StringToDateTimeValidationSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - } -} From 20b1293544c24d1ead665f47043cc8f18fab41f1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 17 Nov 2017 18:46:47 +0000 Subject: [PATCH 054/176] Reorganising guid projection tests to get clearer test names --- .../Infrastructure/Ef5TestDbContext.cs | 2 - .../WhenConvertingToDateTimes.cs | 1 - .../WhenConvertingToGuids.cs | 15 ++++- .../WhenConvertingToDateTimes.cs | 1 - .../Infrastructure/Ef6TestDbContext.cs | 2 - .../Infrastructure/InMemoryEf6TestContext.cs | 6 -- .../WhenConvertingToGuids.cs | 15 ++++- .../Infrastructure/EfCore1TestDbContext.cs | 2 - .../WhenConvertingToDateTimes.cs | 1 - .../WhenConvertingToGuids.cs | 15 ++++- .../Infrastructure/EfCore2TestDbContext.cs | 2 - .../WhenConvertingToDateTimes.cs | 1 - .../WhenConvertingToGuids.cs | 15 ++++- .../AgileMapper.UnitTests.Orms.csproj | 4 +- .../Infrastructure/ITestDbContext.cs | 2 - .../Guids/IGuidConversionFailureTest.cs | 9 +++ .../Guids/IGuidConverterTest.cs | 9 +++ .../Guids/WhenConvertingToGuids.cs | 59 +++++++++++++++++ .../WhenConvertingToGuids.cs | 66 ------------------- 19 files changed, 132 insertions(+), 95 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index fbfa4cd69..ac558d0d0 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -38,8 +38,6 @@ protected Ef5TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; - public bool StringToGuidConversionSupported => false; - IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 4e329edf3..bab346c6c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; using Orms.SimpleTypeConversion.DateTimes; using Xunit; diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs index a467dd39a..8dbdb8837 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,24 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Guids; + using Xunit; - public class WhenConvertingToGuids : WhenConvertingToGuids + public class WhenConvertingToGuids : + WhenConvertingToGuids, + IGuidConversionFailureTest { public WhenConvertingToGuids(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToAGuid() + => RunShouldErrorProjectingAParseableStringToAGuid(); + + [Fact] + public void ShouldErrorProjectingANullStringToAGuid() + => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index ebd404fe1..4113b6d6a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,7 +2,6 @@ { using Infrastructure; using Orms.Infrastructure; - using Orms.SimpleTypeConversion; using Orms.SimpleTypeConversion.DateTimes; using Xunit; diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 022f55311..4c104b163 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -38,8 +38,6 @@ protected Ef6TestDbContext(DbConnection dbConnection) public bool StringToNumberConversionSupported => false; - public virtual bool StringToGuidConversionSupported => false; - IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs index 39d6d2121..8a5b605a3 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/InMemoryEf6TestContext.cs @@ -4,11 +4,5 @@ public class InMemoryEf6TestContext : InMemoryOrmTestContext { - public InMemoryEf6TestContext() - { - // ReSharper disable once UnusedVariable - // Touch SqlFunctions to load System.Data.Entity into the AppDomain: - //var functionsType = typeof(SqlFunctions); - } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs index 990289aab..152a2cbc3 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,24 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Guids; + using Xunit; - public class WhenConvertingToGuids : WhenConvertingToGuids + public class WhenConvertingToGuids : + WhenConvertingToGuids, + IGuidConversionFailureTest { public WhenConvertingToGuids(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToAGuid() + => RunShouldErrorProjectingAParseableStringToAGuid(); + + [Fact] + public void ShouldErrorProjectingANullStringToAGuid() + => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 85325fd4d..0a09a62a4 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -36,8 +36,6 @@ public EfCore1TestDbContext() public bool StringToNumberConversionSupported => true; - public bool StringToGuidConversionSupported => true; - IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 264a31828..acb39ed2b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; using Orms.SimpleTypeConversion.DateTimes; using Xunit; diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs index 921cb0af4..d78d8fb71 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,24 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Guids; + using Xunit; - public class WhenConvertingToGuids : WhenConvertingToGuids + public class WhenConvertingToGuids : + WhenConvertingToGuids, + IGuidConverterTest { public WhenConvertingToGuids(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToAGuid() + => RunShouldProjectAParseableStringToAGuid(); + + [Fact] + public void ShouldProjectANullStringToAGuid() + => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 9c367f7bc..1f69b1c8a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -39,8 +39,6 @@ public EfCore2TestDbContext() public bool StringToNumberConversionSupported => true; - public bool StringToGuidConversionSupported => true; - IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 64eabeef9..d23350b32 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; using Orms.SimpleTypeConversion.DateTimes; using Xunit; diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs index a717d59bb..4cb9a16ed 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,24 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Guids; + using Xunit; - public class WhenConvertingToGuids : WhenConvertingToGuids + public class WhenConvertingToGuids : + WhenConvertingToGuids, + IGuidConverterTest { public WhenConvertingToGuids(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToAGuid() + => RunShouldProjectAParseableStringToAGuid(); + + [Fact] + public void ShouldProjectANullStringToAGuid() + => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 8b3a3f605..f31fa57fc 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -87,7 +87,9 @@ - + + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index a680197b8..edb258e21 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -7,8 +7,6 @@ public interface ITestDbContext : IDisposable { bool StringToNumberConversionSupported { get; } - bool StringToGuidConversionSupported { get; } - IDbSetWrapper Products { get; } IDbSetWrapper Persons { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs new file mode 100644 index 000000000..cfffe7653 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids +{ + public interface IGuidConversionFailureTest + { + void ShouldErrorProjectingAParseableStringToAGuid(); + + void ShouldErrorProjectingANullStringToAGuid(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs new file mode 100644 index 000000000..598994fe7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids +{ + public interface IGuidConverterTest + { + void ShouldProjectAParseableStringToAGuid(); + + void ShouldProjectANullStringToAGuid(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs new file mode 100644 index 000000000..f982b0947 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs @@ -0,0 +1,59 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids +{ + using System; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + + public abstract class WhenConvertingToGuids : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToGuids(ITestContext context) + : base(context) + { + } + + #region Parseable String -> Guid + + protected void RunShouldProjectAParseableStringToAGuid() + => RunTest(ProjectAParseableStringToAGuid); + + protected void RunShouldErrorProjectingAParseableStringToAGuid() + => RunTestAndExpectThrow(ProjectAParseableStringToAGuid); + + private static void ProjectAParseableStringToAGuid(TOrmContext context) + { + var guid = Guid.NewGuid(); + + context.StringItems.Add(new PublicString { Value = guid.ToString() }); + context.SaveChanges(); + + var guidItem = context.StringItems.ProjectTo().First(); + + guidItem.Value.ShouldBe(guid); + } + + #endregion + + #region Null String -> Guid + + protected void RunShouldProjectANullStringToAGuid() + => RunTest(ProjectANullStringToAGuid); + + protected void RunShouldErrorProjectingANullStringToAGuid() + => RunTestAndExpectThrow(ProjectANullStringToAGuid); + + private static void ProjectANullStringToAGuid(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var guidItem = context.StringItems.ProjectTo().First(); + + guidItem.Value.ShouldBe(default(Guid)); + } + + #endregion + } +} diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs deleted file mode 100644 index 2ad1cb00e..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion -{ - using System; - using System.Linq; - using Infrastructure; - using Shouldly; - using TestClasses; - using Xunit; - - public abstract class WhenConvertingToGuids : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenConvertingToGuids(ITestContext context) - : base(context) - { - } - - [Fact] - public void ShouldProjectAParseableStringToAGuidAsExpected() - { - void Test(TOrmContext context) - { - var guid = Guid.NewGuid(); - - context.StringItems.Add(new PublicString { Value = guid.ToString() }); - context.SaveChanges(); - - var guidItem = context.StringItems.ProjectTo().First(); - - guidItem.Value.ShouldBe(guid); - } - - if (Context.StringToGuidConversionSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - - [Fact] - public void ShouldProjectANullStringToAGuidAsExpected() - { - void Test(TOrmContext context) - { - context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); - - var guidItem = context.StringItems.ProjectTo().First(); - - guidItem.Value.ShouldBe(default(Guid)); - } - - if (Context.StringToGuidConversionSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - } -} From 68fb9eec332df0485b5a64c2497755eb7e962da0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 08:41:39 +0000 Subject: [PATCH 055/176] Organising string -> int projection tests to get clear test names --- .../Infrastructure/Ef5TestDbContext.cs | 2 - .../WhenConvertingToInts.cs | 21 ++- .../Infrastructure/Ef6TestDbContext.cs | 2 - .../WhenConvertingToInts.cs | 20 ++- .../Infrastructure/EfCore1TestDbContext.cs | 2 - .../WhenConvertingToInts.cs | 20 ++- .../Infrastructure/EfCore2TestDbContext.cs | 2 - .../WhenConvertingToInts.cs | 20 ++- .../AgileMapper.UnitTests.Orms.csproj | 2 +- .../Infrastructure/ITestDbContext.cs | 2 - .../Integers/WhenConvertingToInts.cs | 156 ++++++++++++++++++ .../WhenConvertingToInts.cs | 109 ------------ 12 files changed, 230 insertions(+), 128 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index ac558d0d0..393eb4d83 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -36,8 +36,6 @@ protected Ef5TestDbContext(DbConnection dbConnection) #region ITestDbContext Members - public bool StringToNumberConversionSupported => false; - IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index 6511a14ef..f61bc4df5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,30 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Integers; + using Xunit; + + public class WhenConvertingToInts : + WhenConvertingToInts, + IStringToIntegerConversionFailureTest, + IStringToIntegerValidationFailureTest - public class WhenConvertingToInts : WhenConvertingToInts { public WhenConvertingToInts(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToAnInt() + => RunShouldErrorProjectingAParseableStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingANullStringToAnInt() + => RunShouldErrorProjectingANullStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToAnInt() + => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 4c104b163..c415cc24c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -36,8 +36,6 @@ protected Ef6TestDbContext(DbConnection dbConnection) #region ITestDbContext Members - public bool StringToNumberConversionSupported => false; - IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 072a55630..d20bdfb86 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,29 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Integers; + using Xunit; - public class WhenConvertingToInts : WhenConvertingToInts + public class WhenConvertingToInts : + WhenConvertingToInts, + IStringToIntegerConversionFailureTest, + IStringToIntegerValidationFailureTest { public WhenConvertingToInts(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAParseableStringToAnInt() + => RunShouldErrorProjectingAParseableStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingANullStringToAnInt() + => RunShouldErrorProjectingANullStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToAnInt() + => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 0a09a62a4..873eeef65 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -34,8 +34,6 @@ public EfCore1TestDbContext() #region ITestDbContext Members - public bool StringToNumberConversionSupported => true; - IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs index 67a5355f7..121b2f5cf 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,29 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Integers; + using Xunit; - public class WhenConvertingToInts : WhenConvertingToInts + public class WhenConvertingToInts : + WhenConvertingToInts, + IStringToIntegerConverterTest, + IStringToIntegerValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToAnInt() + => RunShouldProjectAParseableStringToAnInt(); + + [Fact] + public void ShouldProjectANullStringToAnInt() + => RunShouldProjectANullStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToAnInt() + => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 1f69b1c8a..68528a306 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -37,8 +37,6 @@ public EfCore2TestDbContext() #region ITestDbContext Members - public bool StringToNumberConversionSupported => true; - IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 06ce28653..0214d73bd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,29 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion; + using Orms.SimpleTypeConversion.Integers; + using Xunit; - public class WhenConvertingToInts : WhenConvertingToInts + public class WhenConvertingToInts : + WhenConvertingToInts, + IStringToIntegerConverterTest, + IStringToIntegerValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAParseableStringToAnInt() + => RunShouldProjectAParseableStringToAnInt(); + + [Fact] + public void ShouldProjectANullStringToAnInt() + => RunShouldProjectANullStringToAnInt(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableStringToAnInt() + => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index f31fa57fc..be8a5a344 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -102,7 +102,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index edb258e21..5f7616588 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -5,8 +5,6 @@ public interface ITestDbContext : IDisposable { - bool StringToNumberConversionSupported { get; } - IDbSetWrapper Products { get; } IDbSetWrapper Persons { get; } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs new file mode 100644 index 000000000..bf472775c --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs @@ -0,0 +1,156 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Integers +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public interface IStringToIntegerConverterTest + { + void ShouldProjectAParseableStringToAnInt(); + + void ShouldProjectANullStringToAnInt(); + } + + public interface IStringToIntegerConversionFailureTest + { + void ShouldErrorProjectingAParseableStringToAnInt(); + + void ShouldErrorProjectingANullStringToAnInt(); + } + + public interface IStringToIntegerValidatorTest + { + void ShouldProjectAnUnparseableStringToAnInt(); + } + + public interface IStringToIntegerValidationFailureTest + { + void ShouldErrorProjectingAnUnparseableStringToAnInt(); + } + + public abstract class WhenConvertingToInts : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToInts(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAShortToAnInt() + { + RunTest(context => + { + context.ShortItems.Add(new PublicShort { Value = 123 }); + context.SaveChanges(); + + var intItem = context.ShortItems.ProjectTo().First(); + + intItem.Value.ShouldBe(123); + }); + } + + [Fact] + public void ShouldProjectAnInRangeLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLong { Value = 12345L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(12345); + }); + } + + [Fact] + public void ShouldProjectATooBigLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLong { Value = long.MaxValue }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } + + [Fact] + public void ShouldProjectATooSmallLongToAnInt() + { + RunTest(context => + { + context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); + context.SaveChanges(); + + var intItem = context.LongItems.ProjectTo().First(); + + intItem.Value.ShouldBe(0); + }); + } + + #region Parseable String -> Int + + protected void RunShouldProjectAParseableStringToAnInt() + => RunTest(ProjectParseableStringToInt); + + protected void RunShouldErrorProjectingAParseableStringToAnInt() + => RunTestAndExpectThrow(ProjectParseableStringToInt); + + private static void ProjectParseableStringToInt(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "738" }); + context.SaveChanges(); + + var intItem = context.StringItems.ProjectTo().First(); + + intItem.Value.ShouldBe(738); + } + + #endregion + + #region Null String -> Int + + protected void RunShouldProjectANullStringToAnInt() + => RunTest(ProjectNullStringToInt); + + protected void RunShouldErrorProjectingANullStringToAnInt() + => RunTestAndExpectThrow(ProjectNullStringToInt); + + private static void ProjectNullStringToInt(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var intItem = context.StringItems.ProjectTo().First(); + + intItem.Value.ShouldBe(default(int)); + } + + #endregion + + #region Unparseable String -> Int + + protected void RunShouldProjectAnUnparseableStringToAnInt() + => RunTest(ProjectUnparseableStringToInt); + + protected void RunShouldErrorProjectingAnUnparseableStringToAnInt() + => RunTestAndExpectThrow(ProjectUnparseableStringToInt); + + private void ProjectUnparseableStringToInt(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "hsejk" }); + context.SaveChanges(); + + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + context.StringItems.ProjectTo().First(); + } + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs deleted file mode 100644 index ab4e5904c..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion -{ - using System.Linq; - using Infrastructure; - using Shouldly; - using TestClasses; - using Xunit; - - public abstract class WhenConvertingToInts : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenConvertingToInts(ITestContext context) - : base(context) - { - } - - [Fact] - public void ShouldProjectAShortToAnInt() - { - RunTest(context => - { - context.ShortItems.Add(new PublicShort { Value = 123 }); - context.SaveChanges(); - - var intItem = context.ShortItems.ProjectTo().First(); - - intItem.Value.ShouldBe(123); - }); - } - - [Fact] - public void ShouldProjectAnInRangeLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLong { Value = 12345L }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(12345); - }); - } - - [Fact] - public void ShouldProjectATooBigLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLong { Value = long.MaxValue }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(0); - }); - } - - [Fact] - public void ShouldProjectATooSmallLongToAnInt() - { - RunTest(context => - { - context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); - context.SaveChanges(); - - var intItem = context.LongItems.ProjectTo().First(); - - intItem.Value.ShouldBe(0); - }); - } - - [Fact] - public void ShouldProjectAParseableStringToAnIntAsExpected() - { - void Test(TOrmContext context) - { - context.StringItems.Add(new PublicString { Value = "738" }); - context.SaveChanges(); - - var intItem = context.StringItems.ProjectTo().First(); - - intItem.Value.ShouldBe(738); - } - - if (Context.StringToNumberConversionSupported) - { - RunTest(Test); - } - else - { - RunTestAndExpectThrow(Test); - } - } - - [Fact] - public void ShouldProjectAnUnparseableStringToAnIntAsExpected() - { - RunTestAndExpectThrow(context => - { - context.StringItems.Add(new PublicString { Value = "hsejk" }); - context.SaveChanges(); - - // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - context.StringItems.ProjectTo().First(); - }); - } - } -} \ No newline at end of file From dcea8356bcbc6a60d96abf667f9cbe377eda6bd5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 08:57:18 +0000 Subject: [PATCH 056/176] Reorganising simple type projection tests to use generic interfaces --- .../WhenConvertingToDateTimes.cs | 13 +++++----- .../WhenConvertingToDateTimes.cs | 13 +++++----- .../WhenConvertingToGuids.cs | 9 ++++--- .../WhenConvertingToInts.cs | 12 ++++----- .../WhenConvertingToDateTimes.cs | 13 +++++----- .../WhenConvertingToDateTimes.cs | 13 +++++----- .../WhenConvertingToGuids.cs | 9 ++++--- .../WhenConvertingToInts.cs | 12 ++++----- .../WhenConvertingToDateTimes.cs | 13 +++++----- .../WhenConvertingToGuids.cs | 9 ++++--- .../WhenConvertingToInts.cs | 12 ++++----- .../WhenConvertingToDateTimes.cs | 15 ++++++----- .../WhenConvertingToGuids.cs | 9 ++++--- .../WhenConvertingToInts.cs | 12 ++++----- .../AgileMapper.UnitTests.Orms.csproj | 17 ++++++------ .../IDateTimeConversionFailureTest.cs | 9 ------- .../DateTimes/IDateTimeConverterTest.cs | 9 ------- .../IDateTimeValidationFailureTest.cs | 7 ----- .../DateTimes/IDateTimeValidatorTest.cs | 7 ----- .../Guids/IGuidConversionFailureTest.cs | 9 ------- .../Guids/IGuidConverterTest.cs | 9 ------- .../IStringConversionFailureTest.cs | 10 +++++++ .../IStringConversionValidationFailureTest.cs | 8 ++++++ .../IStringConversionValidatorTest.cs | 8 ++++++ .../IStringConverterTest.cs | 10 +++++++ .../WhenConvertingToDateTimes.cs | 2 +- .../{Guids => }/WhenConvertingToGuids.cs | 2 +- .../{Integers => }/WhenConvertingToInts.cs | 26 +------------------ 28 files changed, 134 insertions(+), 163 deletions(-) delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs rename AgileMapper.UnitTests.Orms/SimpleTypeConversion/{DateTimes => }/WhenConvertingToDateTimes.cs (99%) rename AgileMapper.UnitTests.Orms/SimpleTypeConversion/{Guids => }/WhenConvertingToGuids.cs (99%) rename AgileMapper.UnitTests.Orms/SimpleTypeConversion/{Integers => }/WhenConvertingToInts.cs (86%) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index fa996feef..0041aef24 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,15 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { + using System; using Infrastructure; using Orms.Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConverterTest, - IDateTimeValidatorTest + IStringConverterTest, + IStringConversionValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) @@ -16,15 +17,15 @@ public WhenConvertingToDateTimes(LocalDbTestContext conte } [Fact] - public void ShouldProjectAParseableStringToADateTime() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullStringToADateTime() + public void ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldProjectAnUnparseableStringToADateTime() + public void ShouldProjectAnUnparseableString() => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index bab346c6c..f58d01dc9 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,13 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConversionFailureTest, - IDateTimeValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf5TestContext context) : base(context) @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToADateTime() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADateTime(); [Fact] - public void ShouldErrorProjectingANullStringToADateTime() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToADateTime() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs index 8dbdb8837..aafe86987 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,12 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.Guids; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IGuidConversionFailureTest + IStringConversionFailureTest { public WhenConvertingToGuids(InMemoryEf5TestContext context) : base(context) @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToAGuid() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAGuid(); [Fact] - public void ShouldErrorProjectingANullStringToAGuid() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index f61bc4df5..4bbae42b1 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion.Integers; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToInts : WhenConvertingToInts, - IStringToIntegerConversionFailureTest, - IStringToIntegerValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEf5TestContext context) @@ -16,15 +16,15 @@ public WhenConvertingToInts(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToAnInt() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAnInt(); [Fact] - public void ShouldErrorProjectingANullStringToAnInt() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToAnInt() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 4113b6d6a..52c4608bb 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,15 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion { + using System; using Infrastructure; using Orms.Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConverterTest, - IDateTimeValidatorTest + IStringConverterTest, + IStringConversionValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) @@ -16,15 +17,15 @@ public WhenConvertingToDateTimes(LocalDbTestContext conte } [Fact] - public void ShouldProjectAParseableStringToADateTime() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullStringToADateTime() + public void ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldProjectAnUnparseableStringToADateTime() + public void ShouldProjectAnUnparseableString() => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 989c0648a..611b76ad0 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,13 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConversionFailureTest, - IDateTimeValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf6TestContext context) : base(context) @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToADateTime() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADateTime(); [Fact] - public void ShouldErrorProjectingANullStringToADateTime() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToADateTime() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs index 152a2cbc3..c078e235c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,12 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.Guids; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IGuidConversionFailureTest + IStringConversionFailureTest { public WhenConvertingToGuids(InMemoryEf6TestContext context) : base(context) @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToAGuid() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAGuid(); [Fact] - public void ShouldErrorProjectingANullStringToAGuid() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index d20bdfb86..98a62dea9 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion.Integers; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToInts : WhenConvertingToInts, - IStringToIntegerConversionFailureTest, - IStringToIntegerValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEf6TestContext context) : base(context) @@ -15,15 +15,15 @@ public WhenConvertingToInts(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableStringToAnInt() + public void ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAnInt(); [Fact] - public void ShouldErrorProjectingANullStringToAnInt() + public void ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToAnInt() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs index acb39ed2b..3b831b078 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,13 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConverterTest, - IDateTimeValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) : base(context) @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableStringToADateTime() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullStringToADateTime() + public void ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToADateTime() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs index d78d8fb71..5a0e6f027 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,12 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.Guids; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IGuidConverterTest + IStringConverterTest { public WhenConvertingToGuids(InMemoryEfCore1TestContext context) : base(context) @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableStringToAGuid() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAGuid(); [Fact] - public void ShouldProjectANullStringToAGuid() + public void ShouldProjectANullString() => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs index 121b2f5cf..a0043695d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion.Integers; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToInts : WhenConvertingToInts, - IStringToIntegerConverterTest, - IStringToIntegerValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore1TestContext context) : base(context) @@ -15,15 +15,15 @@ public WhenConvertingToInts(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableStringToAnInt() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAnInt(); [Fact] - public void ShouldProjectANullStringToAnInt() + public void ShouldProjectANullString() => RunShouldProjectANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToAnInt() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs index d23350b32..fddbef4ff 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,13 +1,14 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.DateTimes; + using Orms.SimpleTypeConversion; using Xunit; - public class WhenConvertingToDateTimes : + public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IDateTimeConverterTest, - IDateTimeValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) : base(context) @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableStringToADateTime() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullStringToADateTime() + public void ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToADateTime() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs index 4cb9a16ed..19c0f5631 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,12 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System; using Infrastructure; - using Orms.SimpleTypeConversion.Guids; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IGuidConverterTest + IStringConverterTest { public WhenConvertingToGuids(InMemoryEfCore2TestContext context) : base(context) @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableStringToAGuid() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAGuid(); [Fact] - public void ShouldProjectANullStringToAGuid() + public void ShouldProjectANullString() => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 0214d73bd..94da2edc6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,13 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { using Infrastructure; - using Orms.SimpleTypeConversion.Integers; + using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToInts : WhenConvertingToInts, - IStringToIntegerConverterTest, - IStringToIntegerValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore2TestContext context) : base(context) @@ -15,15 +15,15 @@ public WhenConvertingToInts(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableStringToAnInt() + public void ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAnInt(); [Fact] - public void ShouldProjectANullStringToAnInt() + public void ShouldProjectANullString() => RunShouldProjectANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableStringToAnInt() + public void ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index be8a5a344..ce75e9a22 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -83,14 +83,12 @@ - - - - - - - - + + + + + + @@ -102,7 +100,7 @@ - + @@ -127,6 +125,7 @@ AgileMapper + diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs deleted file mode 100644 index a94e80ca2..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConversionFailureTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes -{ - public interface IDateTimeConversionFailureTest - { - void ShouldErrorProjectingAParseableStringToADateTime(); - - void ShouldErrorProjectingANullStringToADateTime(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs deleted file mode 100644 index 97e37d5da..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeConverterTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes -{ - public interface IDateTimeConverterTest - { - void ShouldProjectAParseableStringToADateTime(); - - void ShouldProjectANullStringToADateTime(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs deleted file mode 100644 index 5e21d41bc..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidationFailureTest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes -{ - public interface IDateTimeValidationFailureTest - { - void ShouldErrorProjectingAnUnparseableStringToADateTime(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs deleted file mode 100644 index e2e24da10..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/IDateTimeValidatorTest.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes -{ - public interface IDateTimeValidatorTest - { - void ShouldProjectAnUnparseableStringToADateTime(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs deleted file mode 100644 index cfffe7653..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConversionFailureTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids -{ - public interface IGuidConversionFailureTest - { - void ShouldErrorProjectingAParseableStringToAGuid(); - - void ShouldErrorProjectingANullStringToAGuid(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs deleted file mode 100644 index 598994fe7..000000000 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/IGuidConverterTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids -{ - public interface IGuidConverterTest - { - void ShouldProjectAParseableStringToAGuid(); - - void ShouldProjectANullStringToAGuid(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs new file mode 100644 index 000000000..92578229a --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + // ReSharper disable once UnusedTypeParameter + public interface IStringConversionFailureTest + { + void ShouldErrorProjectingAParseableString(); + + void ShouldErrorProjectingANullString(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs new file mode 100644 index 000000000..ba878d5a7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + // ReSharper disable once UnusedTypeParameter + public interface IStringConversionValidationFailureTest + { + void ShouldErrorProjectingAnUnparseableString(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs new file mode 100644 index 000000000..6b40749e7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs @@ -0,0 +1,8 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + // ReSharper disable once UnusedTypeParameter + public interface IStringConversionValidatorTest + { + void ShouldProjectAnUnparseableString(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs new file mode 100644 index 000000000..a32f8e079 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + // ReSharper disable once UnusedTypeParameter + public interface IStringConverterTest + { + void ShouldProjectAParseableString(); + + void ShouldProjectANullString(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs similarity index 99% rename from AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs rename to AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 51c6fbdf6..a9cabd570 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/DateTimes/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.DateTimes +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System; using System.Linq; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs similarity index 99% rename from AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs rename to AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index f982b0947..df1d70e13 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Guids/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Guids +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System; using System.Linq; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs similarity index 86% rename from AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs rename to AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index bf472775c..3a3bc3436 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/Integers/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion.Integers +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System.Linq; using Infrastructure; @@ -6,30 +6,6 @@ using TestClasses; using Xunit; - public interface IStringToIntegerConverterTest - { - void ShouldProjectAParseableStringToAnInt(); - - void ShouldProjectANullStringToAnInt(); - } - - public interface IStringToIntegerConversionFailureTest - { - void ShouldErrorProjectingAParseableStringToAnInt(); - - void ShouldErrorProjectingANullStringToAnInt(); - } - - public interface IStringToIntegerValidatorTest - { - void ShouldProjectAnUnparseableStringToAnInt(); - } - - public interface IStringToIntegerValidationFailureTest - { - void ShouldErrorProjectingAnUnparseableStringToAnInt(); - } - public abstract class WhenConvertingToInts : OrmTestClassBase where TOrmContext : ITestDbContext, new() { From e605086e265ae52c3a54d43239297df0a67be29f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 09:13:15 +0000 Subject: [PATCH 057/176] Test coverage for -> double projection --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConvertingToDoubles.cs | 30 +++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConvertingToDoubles.cs | 30 +++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConvertingToDoubles.cs | 29 +++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConvertingToDoubles.cs | 29 +++++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../WhenConvertingToDoubles.cs | 104 ++++++++++++++++++ .../WhenConvertingToInts.cs | 2 +- .../TestClasses/PublicDoubleDto.cs | 10 ++ 12 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicDoubleDto.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 9bcf74570..2156b9917 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs new file mode 100644 index 000000000..db9920edf --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -0,0 +1,30 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToDoubles : + WhenConvertingToDoubles, + IStringConversionFailureTest, + IStringConversionValidationFailureTest + + { + public WhenConvertingToDoubles(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldErrorProjectingAParseableString() + => RunShouldErrorProjectingAParseableStringToADouble(); + + [Fact] + public void ShouldErrorProjectingANullString() + => RunShouldErrorProjectingANullStringToADouble(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableString() + => RunShouldErrorProjectingAnUnparseableStringToADouble(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index f93c08d2f..21e45e316 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,6 +91,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs new file mode 100644 index 000000000..cb8e71efc --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -0,0 +1,30 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToDoubles : + WhenConvertingToDoubles, + IStringConversionFailureTest, + IStringConversionValidationFailureTest + + { + public WhenConvertingToDoubles(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldErrorProjectingAParseableString() + => RunShouldErrorProjectingAParseableStringToADouble(); + + [Fact] + public void ShouldErrorProjectingANullString() + => RunShouldErrorProjectingANullStringToADouble(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableString() + => RunShouldErrorProjectingAnUnparseableStringToADouble(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 5293c4e28..9cfbe163c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -207,6 +207,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs new file mode 100644 index 000000000..572bb238d --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToDoubles : + WhenConvertingToDoubles, + IStringConverterTest, + IStringConversionValidationFailureTest + { + public WhenConvertingToDoubles(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAParseableString() + => RunShouldProjectAParseableStringToADouble(); + + [Fact] + public void ShouldProjectANullString() + => RunShouldProjectANullStringToADouble(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableString() + => RunShouldErrorProjectingAnUnparseableStringToADouble(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index d228bada7..69d7dcddc 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -146,6 +146,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs new file mode 100644 index 000000000..852450c27 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToDoubles : + WhenConvertingToDoubles, + IStringConverterTest, + IStringConversionValidationFailureTest + { + public WhenConvertingToDoubles(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAParseableString() + => RunShouldProjectAParseableStringToADouble(); + + [Fact] + public void ShouldProjectANullString() + => RunShouldProjectANullStringToADouble(); + + [Fact] + public void ShouldErrorProjectingAnUnparseableString() + => RunShouldErrorProjectingAnUnparseableStringToADouble(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index ce75e9a22..eabe4878a 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -89,12 +89,14 @@ + + diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs new file mode 100644 index 000000000..fb29b2400 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -0,0 +1,104 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenConvertingToDoubles : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToDoubles(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAShortToADouble() + { + RunTest(context => + { + context.ShortItems.Add(new PublicShort { Value = 123 }); + context.SaveChanges(); + + var doubleItem = context.ShortItems.ProjectTo().First(); + + doubleItem.Value.ShouldBe(123d); + }); + } + + [Fact] + public void ShouldProjectALongToADouble() + { + RunTest(context => + { + context.LongItems.Add(new PublicLong { Value = 12345L }); + context.SaveChanges(); + + var doubleItem = context.LongItems.ProjectTo().First(); + + doubleItem.Value.ShouldBe(12345d); + }); + } + + #region Parseable String -> Double + + protected void RunShouldProjectAParseableStringToADouble() + => RunTest(ProjectParseableStringToDouble); + + protected void RunShouldErrorProjectingAParseableStringToADouble() + => RunTestAndExpectThrow(ProjectParseableStringToDouble); + + private static void ProjectParseableStringToDouble(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "738.01" }); + context.SaveChanges(); + + var doubleItem = context.StringItems.ProjectTo().First(); + + doubleItem.Value.ShouldBe(738.01); + } + + #endregion + + #region Null String -> Double + + protected void RunShouldProjectANullStringToADouble() + => RunTest(ProjectNullStringToDouble); + + protected void RunShouldErrorProjectingANullStringToADouble() + => RunTestAndExpectThrow(ProjectNullStringToDouble); + + private static void ProjectNullStringToDouble(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = default(string) }); + context.SaveChanges(); + + var doubleItem = context.StringItems.ProjectTo().First(); + + doubleItem.Value.ShouldBe(default(double)); + } + + #endregion + + #region Unparseable String -> Double + + protected void RunShouldProjectAnUnparseableStringToADouble() + => RunTest(ProjectUnparseableStringToDouble); + + protected void RunShouldErrorProjectingAnUnparseableStringToADouble() + => RunTestAndExpectThrow(ProjectUnparseableStringToDouble); + + private static void ProjectUnparseableStringToDouble(TOrmContext context) + { + context.StringItems.Add(new PublicString { Value = "poioiujygy" }); + context.SaveChanges(); + + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + context.StringItems.ProjectTo().First(); + } + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 3a3bc3436..3a54842ad 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -118,7 +118,7 @@ protected void RunShouldProjectAnUnparseableStringToAnInt() protected void RunShouldErrorProjectingAnUnparseableStringToAnInt() => RunTestAndExpectThrow(ProjectUnparseableStringToInt); - private void ProjectUnparseableStringToInt(TOrmContext context) + private static void ProjectUnparseableStringToInt(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "hsejk" }); context.SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicDoubleDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicDoubleDto.cs new file mode 100644 index 000000000..0880d3656 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicDoubleDto.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class PublicDoubleDto + { + public int Id { get; set; } + + + public double Value { get; set; } + } +} \ No newline at end of file From f9702a2872b7d0df6af4c2084a99da08a7e0f867 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 09:50:13 +0000 Subject: [PATCH 058/176] Simplifying complex type member projection test / Start of collection member support --- .../Infrastructure/Ef5TestDbContext.cs | 10 +++ .../Infrastructure/Ef6TestDbContext.cs | 10 +++ .../Infrastructure/EfCore1TestDbContext.cs | 10 +++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 10 +++ .../WhenProjectingToEnumerableMembers.cs | 13 ++++ .../AgileMapper.UnitTests.Orms.csproj | 5 ++ .../Infrastructure/ITestDbContext.cs | 4 ++ .../Infrastructure/OrmTestClassBase.cs | 2 + .../Properties/AssemblyInfo.cs | 3 + .../TestClasses/Rota.cs | 18 +++++ .../TestClasses/RotaDto.cs | 16 +++++ .../TestClasses/RotaEntry.cs | 23 ++++++ .../TestClasses/RotaEntryDto.cs | 21 ++++++ .../WhenProjectingToComplexTypeMembers.cs | 3 - .../WhenProjectingToEnumerableMembers.cs | 70 +++++++++++++++++++ AgileMapper/MappingRuleSetCollection.cs | 4 +- ...ojectSourceEnumerablePopulationStrategy.cs | 16 +++++ 18 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Rota.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/RotaDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/RotaEntry.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs create mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 393eb4d83..aba1d834d 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -24,6 +24,10 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet
Addresses { get; set; } + public DbSet Rotas { get; set; } + + public DbSet RotaEntries { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -45,6 +49,12 @@ IDbSetWrapper ITestDbContext.Persons IDbSetWrapper
ITestDbContext.Addresses => new Ef5DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.Rotas + => new Ef5DbSetWrapper(Rotas); + + IDbSetWrapper ITestDbContext.RotaEntries + => new Ef5DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.BoolItems => new Ef5DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index c415cc24c..29b11dad7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -24,6 +24,10 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet
Addresses { get; set; } + public DbSet Rotas { get; set; } + + public DbSet RotaEntries { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -45,6 +49,12 @@ IDbSetWrapper ITestDbContext.Persons IDbSetWrapper
ITestDbContext.Addresses => new Ef6DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.Rotas + => new Ef6DbSetWrapper(Rotas); + + IDbSetWrapper ITestDbContext.RotaEntries + => new Ef6DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.BoolItems => new Ef6DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 873eeef65..62c73efca 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -22,6 +22,10 @@ public EfCore1TestDbContext() public DbSet
Addresses { get; set; } + public DbSet Rotas { get; set; } + + public DbSet RotaEntries { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -43,6 +47,12 @@ IDbSetWrapper ITestDbContext.Persons IDbSetWrapper
ITestDbContext.Addresses => new EfCore1DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.Rotas + => new EfCore1DbSetWrapper(Rotas); + + IDbSetWrapper ITestDbContext.RotaEntries + => new EfCore1DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore1DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 69d7dcddc..36979dece 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -152,6 +152,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 68528a306..306e48c1b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -25,6 +25,10 @@ public EfCore2TestDbContext() public DbSet
Addresses { get; set; } + public DbSet Rotas { get; set; } + + public DbSet RotaEntries { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -46,6 +50,12 @@ IDbSetWrapper ITestDbContext.Persons IDbSetWrapper
ITestDbContext.Addresses => new EfCore2DbSetWrapper
(Addresses); + IDbSetWrapper ITestDbContext.Rotas + => new EfCore2DbSetWrapper(Rotas); + + IDbSetWrapper ITestDbContext.RotaEntries + => new EfCore2DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore2DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..7fe74ac8e --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index eabe4878a..41265a5a8 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -98,6 +98,10 @@ + + + + @@ -114,6 +118,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 5f7616588..5d180117c 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -11,6 +11,10 @@ public interface ITestDbContext : IDisposable IDbSetWrapper
Addresses { get; } + IDbSetWrapper Rotas { get; } + + IDbSetWrapper RotaEntries { get; } + IDbSetWrapper BoolItems { get; } IDbSetWrapper ShortItems { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index c3ca27acc..26bee1298 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -56,6 +56,8 @@ private void EmptyDbContext() Context.Products.Clear(); Context.Addresses.Clear(); Context.Persons.Clear(); + Context.Rotas.Clear(); + Context.RotaEntries.Clear(); Context.BoolItems.Clear(); Context.ShortItems.Clear(); Context.IntItems.Clear(); diff --git a/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs index a5324d950..5c07fa563 100644 --- a/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs +++ b/AgileMapper.UnitTests.Orms/Properties/AssemblyInfo.cs @@ -1,7 +1,10 @@ using System.Reflection; using System.Runtime.InteropServices; +using Xunit; [assembly: AssemblyTitle("AgileObjects.AgileMapper.UnitTests.Orms")] [assembly: AssemblyDescription("AgileObjects.AgileMapper.UnitTests.Orms")] [assembly: ComVisible(false)] + +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs b/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs new file mode 100644 index 000000000..4fbe5f168 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + + public class Rota + { + [Key] + public int Id { get; set; } + + public DateTime StartDate { get; set; } + + public DateTime EndDate { get; set; } + + public IEnumerable Entries { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/RotaDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/RotaDto.cs new file mode 100644 index 000000000..0597b1005 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/RotaDto.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.Collections.Generic; + + public class RotaDto + { + public int Id { get; set; } + + public DateTime StartDate { get; set; } + + public DateTime EndDate { get; set; } + + public ICollection Entries { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/RotaEntry.cs b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntry.cs new file mode 100644 index 000000000..086daf1da --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntry.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.ComponentModel.DataAnnotations; + + public class RotaEntry + { + [Key] + public int Id { get; set; } + + public int PersonId { get; set; } + + public DayOfWeek DayOfWeek { get; set; } + + public byte StartHour { get; set; } + + public byte StartMinute { get; set; } + + public byte EndHour { get; set; } + + public byte EndMinute { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs new file mode 100644 index 000000000..dd659209e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs @@ -0,0 +1,21 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + + public class RotaEntryDto + { + public int Id { get; set; } + + public int PersonId { get; set; } + + public DayOfWeek DayOfWeek { get; set; } + + public int StartHour { get; set; } + + public int StartMinute { get; set; } + + public int EndHour { get; set; } + + public int EndMinute { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 927c6d46b..75caeb308 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -21,18 +21,15 @@ public void ShouldProjectToAComplexTypeMember() { var person = new Person { - PersonId = 1, Name = "Test Db", AddressId = 1, Address = new Address { - AddressId = 1, Line1 = "Test Db Line 1", Line2 = "Test Db Line 2" } }; - context.Addresses.Add(person.Address); context.Persons.Add(person); context.SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..5239d7ea5 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,70 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenProjectingToEnumerableMembers : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingToEnumerableMembers(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectToAComplexTypeCollectionMember() + { + RunTest(context => + { + var rotaEntry1 = new RotaEntry + { + DayOfWeek = DayOfWeek.Monday, + PersonId = 10, + StartHour = 8, + StartMinute = 45, + EndHour = 5, + EndMinute = 15 + }; + + var rotaEntry2 = new RotaEntry + { + DayOfWeek = DayOfWeek.Tuesday, + PersonId = 8, + StartHour = 9, + StartMinute = 00, + EndHour = 4, + EndMinute = 30 + }; + + var rotaEntry3 = new RotaEntry + { + DayOfWeek = DayOfWeek.Friday, + PersonId = 51, + StartHour = 10, + StartMinute = 30, + EndHour = 10, + EndMinute = 31 + }; + + var rota = new Rota + { + StartDate = DateTime.Today, + EndDate = DateTime.Today.AddDays(7), + Entries = new List { rotaEntry1, rotaEntry2, rotaEntry3 } + }; + + context.Rotas.Add(rota); + context.SaveChanges(); + + var rotaDto = context.Rotas.Where(r => r.Id == 1).ProjectTo().First(); + + rotaDto.Id.ShouldBe(1); + }); + } + } +} diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 8ecedb6d5..f74b69355 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -55,9 +55,9 @@ internal class MappingRuleSetCollection UseMemberInitialisation = true, UseSingleRootMappingExpression = true }, - CopySourceEnumerablePopulationStrategy.Instance, + ProjectSourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, - ExistingOrDefaultValueDataSourceFactory.Instance); + DefaultValueDataSourceFactory.Instance); #endregion diff --git a/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs new file mode 100644 index 000000000..65992bfd2 --- /dev/null +++ b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables +{ + using System.Linq.Expressions; + + internal class ProjectSourceEnumerablePopulationStrategy : EnumerablePopulationStrategyBase + { + public static readonly IEnumerablePopulationStrategy Instance = new ProjectSourceEnumerablePopulationStrategy(); + + protected override Expression GetEnumerablePopulation( + EnumerablePopulationBuilder builder, + IObjectMappingData mappingData) + { + return builder; + } + } +} \ No newline at end of file From 2ec8bf408197ca4ec437c347bf11646c812a3a41 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 13:07:12 +0000 Subject: [PATCH 059/176] Complex type enumerable member projection - working in EF6, EF5 can't use ToList(), EF Core can't use AsQueryable() --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenProjectingToEnumerableMembers.cs | 13 +++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenProjectingToEnumerableMembers.cs | 13 +++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenProjectingToEnumerableMembers.cs | 13 +++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- .../Infrastructure/EfCore2TestDbContext.cs | 7 +- .../TestClasses/Rota.cs | 2 +- .../WhenProjectingToEnumerableMembers.cs | 20 ++++ AgileMapper.sln | 2 +- .../Extensions/ExpressionExtensions.cs | 4 +- AgileMapper/Extensions/TypeExtensions.cs | 9 +- .../Members/MemberMapperDataExtensions.cs | 4 +- .../EnumerablePopulationBuilder.cs | 110 ++++++++++++------ ...ojectSourceEnumerablePopulationStrategy.cs | 2 + .../ObjectPopulation/MapperDataContext.cs | 30 ++--- .../ObjectPopulation/MappingFactory.cs | 20 +--- .../ObjectMappingDataFactory.cs | 18 ++- .../QueryProjectionExpressionFactory.cs | 17 +-- .../Queryables/QueryProjectionModifier.cs | 11 ++ 21 files changed, 203 insertions(+), 97 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 2156b9917..75b2fa85e 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -97,6 +97,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..342ec174e --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 21e45e316..8d43a4585 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -100,6 +100,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..715a3fba8 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 9cfbe163c..25eeba102 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -214,6 +214,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..5b4e564e2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + using Orms; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 36979dece..71db1121b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -152,8 +152,8 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 306e48c1b..2b0c289a7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -15,7 +15,12 @@ public class EfCore2TestDbContext : DbContext, ITestDbContext .Options; public EfCore2TestDbContext() - : base(_inMemoryOptions) + : this(_inMemoryOptions) + { + } + + protected EfCore2TestDbContext(DbContextOptions options) + : base(options) { } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs b/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs index 4fbe5f168..c84c70cd9 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Rota.cs @@ -13,6 +13,6 @@ public class Rota public DateTime EndDate { get; set; } - public IEnumerable Entries { get; set; } + public ICollection Entries { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs index 5239d7ea5..42419fe3b 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs @@ -64,6 +64,26 @@ public void ShouldProjectToAComplexTypeCollectionMember() var rotaDto = context.Rotas.Where(r => r.Id == 1).ProjectTo().First(); rotaDto.Id.ShouldBe(1); + rotaDto.StartDate.ShouldBe(rota.StartDate); + rotaDto.EndDate.ShouldBe(rota.EndDate); + rotaDto.Entries.Count.ShouldBe(rota.Entries.Count()); + + var i = 0; + + foreach (var rotaEntry in rota.Entries) + { + var rotaEntryDto = rotaDto.Entries.ElementAt(i); + + rotaEntryDto.Id.ShouldBe(rotaEntry.Id); + rotaEntryDto.DayOfWeek.ShouldBe(rotaEntry.DayOfWeek); + rotaEntryDto.PersonId.ShouldBe(rotaEntry.PersonId); + rotaEntryDto.StartHour.ShouldBe(rotaEntry.StartHour); + rotaEntryDto.StartMinute.ShouldBe(rotaEntry.StartMinute); + rotaEntryDto.EndHour.ShouldBe(rotaEntry.EndHour); + rotaEntryDto.EndMinute.ShouldBe(rotaEntry.EndMinute); + + ++i; + } }); } } diff --git a/AgileMapper.sln b/AgileMapper.sln index c8def48b8..fd0e15b1d 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2008 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05AB6D17-6066-41D5-8E79-31C342DFC2DC}" ProjectSection(SolutionItems) = preProject diff --git a/AgileMapper/Extensions/ExpressionExtensions.cs b/AgileMapper/Extensions/ExpressionExtensions.cs index 87c61ba50..73c5e9331 100644 --- a/AgileMapper/Extensions/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/ExpressionExtensions.cs @@ -21,7 +21,7 @@ internal static partial class ExpressionExtensions private static readonly MethodInfo _linqToArrayMethod = typeof(Enumerable) .GetPublicStaticMethod("ToArray"); - private static readonly MethodInfo _linqToListMethod = typeof(Enumerable) + public static readonly MethodInfo LinqToListMethod = typeof(Enumerable) .GetPublicStaticMethod("ToList"); private static readonly MethodInfo _stringEqualsMethod = typeof(string) @@ -236,7 +236,7 @@ public static Expression WithToReadOnlyCollectionCall(this Expression enumerable } public static Expression WithToListCall(this Expression enumerable, Type elementType) - => GetToEnumerableCall(enumerable, _linqToListMethod, elementType); + => GetToEnumerableCall(enumerable, LinqToListMethod, elementType); private static Expression GetToEnumerableCall(Expression enumerable, MethodInfo method, Type elementType) { diff --git a/AgileMapper/Extensions/TypeExtensions.cs b/AgileMapper/Extensions/TypeExtensions.cs index 26bf0cb11..868132b9b 100644 --- a/AgileMapper/Extensions/TypeExtensions.cs +++ b/AgileMapper/Extensions/TypeExtensions.cs @@ -172,8 +172,13 @@ public static bool IsPublic(this Type type) public static bool IsEnumerable(this Type type) { return type.IsArray || - (type != typeof(string) && - typeof(IEnumerable).IsAssignableFrom(type)); + (type != typeof(string) && + typeof(IEnumerable).IsAssignableFrom(type)); + } + + public static bool IsQueryable(this Type type) + { + return type.IsGenericType() && type.GetGenericTypeDefinition() == typeof(IQueryable<>); } public static bool IsComplex(this Type type) diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 2d87e5407..2e001539c 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -19,7 +19,7 @@ public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) => mapperData.IsRoot && mapperData.RuleSet.Settings.UseSingleRootMappingExpression; public static bool UseMemberInitialisation(this IMemberMapperData mapperData) - => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.Context.IsPartOfUserStructMapping; + => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.Context.IsPartOfUserStructMapping(); public static IMemberMapperData GetRootMapperData(this IMemberMapperData mapperData) { @@ -128,7 +128,7 @@ public static bool TargetMemberIsEnumerableElement(this IBasicMapperData mapperD [DebuggerStepThrough] public static bool TargetMemberHasInitAccessibleValue(this IMemberMapperData mapperData) - => mapperData.TargetMember.IsReadable && !mapperData.Context.IsPartOfUserStructMapping; + => mapperData.TargetMember.IsReadable && !mapperData.Context.IsPartOfUserStructMapping(); [DebuggerStepThrough] public static bool TargetMemberIsUserStruct(this IBasicMapperData mapperData) diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index c1165bacf..7537cb400 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -14,18 +14,28 @@ internal class EnumerablePopulationBuilder { #region Untyped MethodInfos - private static readonly MethodInfo _selectWithoutIndexMethod = typeof(Enumerable) + private static readonly MethodInfo _enumerableSelectWithoutIndexMethod = typeof(Enumerable) .GetPublicStaticMethods("Select") - .Last(m => + .First(m => (m.GetParameters().Length == 2) && (m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2)); - private static readonly MethodInfo _selectWithIndexMethod = typeof(Enumerable) + private static readonly MethodInfo _enumerableSelectWithIndexMethod = typeof(Enumerable) .GetPublicStaticMethods("Select") - .Last(m => + .First(m => (m.GetParameters().Length == 2) && (m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 3)); + private static readonly MethodInfo _queryableSelectMethod = typeof(Queryable) + .GetPublicStaticMethods("Select") + .First(m => + (m.GetParameters().Length == 2) && + (m.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2)); + + private static readonly MethodInfo _asQueryableMethod = typeof(Queryable) + .GetPublicStaticMethods("AsQueryable") + .Last(m => m.ContainsGenericParameters); + private static readonly MethodInfo _forEachMethod = typeof(EnumerableExtensions) .GetPublicStaticMethods("ForEach") .First(); @@ -258,12 +268,12 @@ private void CreateSourceTypeHelper(Expression sourceValue) #region Target Variable Population - public void PopulateTargetVariableFromSourceObjectOnly() - => AssignTargetVariableTo(GetSourceOnlyReturnValue()); + public void PopulateTargetVariableFromSourceObjectOnly(IObjectMappingData mappingData = null) + => AssignTargetVariableTo(GetSourceOnlyReturnValue(mappingData)); - private Expression GetSourceOnlyReturnValue() + private Expression GetSourceOnlyReturnValue(IObjectMappingData mappingData) { - var convertedSourceItems = _sourceItemsSelector.SourceItemsProjectedToTargetType().GetResult(); + var convertedSourceItems = _sourceItemsSelector.SourceItemsProjectedToTargetType(mappingData).GetResult(); var returnValue = ConvertForReturnValue(convertedSourceItems); return returnValue; @@ -443,6 +453,8 @@ public Expression GetElementConversion(Expression sourceElement, IObjectMappingD return GetSimpleElementConversion(sourceElement); } + mappingData = ObjectMappingDataFactory.ForElement(mappingData); + var targetMember = mappingData.MapperData.TargetMember; Expression existingElementValue; @@ -509,53 +521,75 @@ private static Expression GetElementMapping( public Expression GetSourceItemsProjection( Expression sourceEnumerableValue, Func projectionFuncFactory) - { - return GetSourceItemsProjection( - sourceEnumerableValue, - _selectWithoutIndexMethod, - projectionFuncFactory.Invoke); - } - - public Expression GetSourceItemsProjection( - Expression sourceEnumerableValue, - MethodInfo selectMethod, - Func projectionFuncFactory) { return CreateSourceItemsProjection( sourceEnumerableValue, - selectMethod, (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter), - _sourceElementParameter); + counterRequired: false); } public Expression GetSourceItemsProjection( Expression sourceEnumerableValue, Func projectionFuncFactory) { - return CreateSourceItemsProjection( - sourceEnumerableValue, - _selectWithIndexMethod, - projectionFuncFactory, - _sourceElementParameter, - Counter); + return CreateSourceItemsProjection(sourceEnumerableValue, projectionFuncFactory, counterRequired: true); } private Expression CreateSourceItemsProjection( Expression sourceEnumerableValue, - MethodInfo linqSelectOverload, Func projectionFuncFactory, - params ParameterExpression[] projectionFuncParameters) + bool counterRequired) { - var funcTypes = projectionFuncParameters - .Select(p => p.Type) - .ToArray() - .Append(Context.TargetElementType); + MethodInfo linqSelectOverload; + + var isQueryableMapping = MapperData.Context.IsPartOfQueryableMapping(); + + if (isQueryableMapping) + { + if (!MapperData.SourceType.IsQueryable()) + { + var asQueryableMethod = _asQueryableMethod.MakeGenericMethod(Context.SourceElementType); - var projectionFunc = Expression.Lambda( - Expression.GetFuncType(funcTypes), + sourceEnumerableValue = Expression.Call(asQueryableMethod, sourceEnumerableValue); + } + + counterRequired = false; + linqSelectOverload = _queryableSelectMethod; + } + else + { + linqSelectOverload = counterRequired + ? _enumerableSelectWithIndexMethod + : _enumerableSelectWithoutIndexMethod; + } + + ParameterExpression[] projectionFuncParameters; + Type[] funcTypes; + + if (counterRequired) + { + projectionFuncParameters = new[] { _sourceElementParameter, Counter }; + funcTypes = new[] { Context.SourceElementType, Counter.Type, Context.TargetElementType }; + } + else + { + projectionFuncParameters = new[] { _sourceElementParameter }; + funcTypes = new[] { Context.SourceElementType, Context.TargetElementType }; + } + + var projectionFuncType = Expression.GetFuncType(funcTypes); + + Expression projectionFunc = Expression.Lambda( + projectionFuncType, projectionFuncFactory.Invoke(_sourceElementParameter, Counter), projectionFuncParameters); + if (isQueryableMapping) + { + projectionFuncType = typeof(Expression<>).MakeGenericType(projectionFuncType); + projectionFunc = projectionFunc.ToConstantExpression(projectionFuncType); + } + var typedSelectMethod = linqSelectOverload.MakeGenericMethod(Context.ElementTypes); var typedSelectCall = Expression.Call(typedSelectMethod, sourceEnumerableValue, projectionFunc); @@ -678,13 +712,13 @@ internal SourceItemsSelector(EnumerablePopulationBuilder builder) _builder = builder; } - public SourceItemsSelector SourceItemsProjectedToTargetType() + public SourceItemsSelector SourceItemsProjectedToTargetType(IObjectMappingData mappingData = null) { var context = _builder.Context; var sourceEnumerableValue = _builder._sourceAdapter.GetSourceValue(); if (context.ElementTypesAreTheSame || - (sourceEnumerableValue.Type.GetEnumerableElementType() == context.TargetElementType)) + (sourceEnumerableValue.Type.GetEnumerableElementType() == context.TargetElementType)) { _result = sourceEnumerableValue; return this; @@ -692,7 +726,7 @@ public SourceItemsSelector SourceItemsProjectedToTargetType() _result = _builder.GetSourceItemsProjection( sourceEnumerableValue, - _builder.GetSimpleElementConversion); + (sourceElement, counter) => _builder.GetElementConversion(sourceElement, mappingData)); return this; } diff --git a/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs index 65992bfd2..4d9b21e65 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs @@ -10,6 +10,8 @@ protected override Expression GetEnumerablePopulation( EnumerablePopulationBuilder builder, IObjectMappingData mappingData) { + builder.PopulateTargetVariableFromSourceObjectOnly(mappingData); + return builder; } } diff --git a/AgileMapper/ObjectPopulation/MapperDataContext.cs b/AgileMapper/ObjectPopulation/MapperDataContext.cs index 63a721221..712a90fe1 100644 --- a/AgileMapper/ObjectPopulation/MapperDataContext.cs +++ b/AgileMapper/ObjectPopulation/MapperDataContext.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { + using System; using System.Linq; using Extensions; using Members; @@ -91,26 +92,29 @@ private void BubbleMappingNeededToParent() public bool UseLocalVariable { get; } public bool UseMappingTryCatch - => _mapperData.RuleSet.Settings.UseTryCatch && (_mapperData.IsRoot || !IsPartOfUserStructMapping); + => _mapperData.RuleSet.Settings.UseTryCatch && (_mapperData.IsRoot || !IsPartOfUserStructMapping()); - public bool IsPartOfUserStructMapping + public bool IsPartOfUserStructMapping() + => CheckHierarchy(mapperData => mapperData.TargetMemberIsUserStruct()); + + public bool IsPartOfQueryableMapping() + => CheckHierarchy(mapperData => mapperData.SourceType.IsQueryable()); + + private bool CheckHierarchy(Func predicate) { - get - { - var mapperData = _mapperData; + var mapperData = _mapperData; - while (mapperData != null) + while (mapperData != null) + { + if (predicate.Invoke(mapperData)) { - if (mapperData.TargetMemberIsUserStruct()) - { - return true; - } - - mapperData = mapperData.Parent; + return true; } - return false; + mapperData = mapperData.Parent; } + + return false; } public bool UsesMappingDataObjectAsParameter diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 9f1694de8..7d567cd2d 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -98,10 +98,7 @@ public static Expression GetElementMapping( { var mapperData = mappingData.MapperData; - if (CreateElementMappingDataFor(mapperData)) - { - mappingData = ObjectMappingDataFactory.ForElement(mappingData); - } + mappingData = ObjectMappingDataFactory.ForElement(mappingData); mapperData.TargetMember.MapCreating(mapperData.SourceMember); @@ -113,21 +110,6 @@ public static Expression GetElementMapping( return GetElementMapping(mappingData, sourceElementValue, targetElementValue); } - private static bool CreateElementMappingDataFor(IBasicMapperData mapperData) - { - if (!mapperData.TargetMemberIsEnumerableElement()) - { - return true; - } - - if (mapperData.TargetMember.IsEnumerable) - { - return !mapperData.TargetMember.ElementType.IsSimple(); - } - - return false; - } - public static Expression GetElementMapping( IObjectMappingData elementMappingData, Expression sourceElementValue, diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 9ffc1f2c0..f59b59073 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -153,14 +153,30 @@ public static IObjectMappingData ForChild( parent); } - public static IObjectMappingData ForElement(IObjectMappingData parent) + public static IObjectMappingData ForElement(IObjectMappingData parentOrElement) { + if (IsElementMappingData(parentOrElement.MapperData)) + { + return parentOrElement; + } + + var parent = parentOrElement; var sourceElementType = parent.MapperData.SourceMember.GetElementMember().Type; var targetElementType = parent.MapperData.TargetMember.GetElementMember().Type; return ForElement(sourceElementType, targetElementType, parent); } + private static bool IsElementMappingData(IBasicMapperData mapperData) + { + if (mapperData.TargetMemberIsEnumerableElement()) + { + return true; + } + + return !mapperData.TargetMember.IsEnumerable || mapperData.TargetMember.ElementType.IsSimple(); + } + public static IObjectMappingData ForElement( Type sourceElementType, Type targetElementType, diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 457dde631..2ad4e9b9a 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - using System.Reflection; using Extensions; - using NetStandardPolyfills; using ObjectPopulation; using Settings; @@ -13,23 +11,11 @@ internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase { public static readonly MappingExpressionFactoryBase Instance = new QueryProjectionExpressionFactory(); - #region Cached Items - - private static readonly MethodInfo _queryableSelectMethod = typeof(Queryable) - .GetPublicStaticMethods() - .First(m => - (m.Name == "Select") && - (m.GetParameters().Last().ParameterType.GetGenericArguments().First().GetGenericArguments().Length == 2)); - - #endregion - public override bool IsFor(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; - return mapperData.TargetMember.IsEnumerable && - mapperData.SourceType.IsGenericType() && - mapperData.SourceType.GetGenericTypeDefinition() == typeof(IQueryable<>); + return mapperData.TargetMember.IsEnumerable && mapperData.SourceType.IsQueryable(); } protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) @@ -42,7 +28,6 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat .EnumerablePopulationBuilder .GetSourceItemsProjection( mapperData.SourceObject, - _queryableSelectMethod, sourceParameter => MappingFactory.GetElementMapping( sourceParameter, mapperData.TargetMember.ElementType.ToDefaultExpression(), diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 73dfe795d..a5db846b5 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables { using System.Linq.Expressions; + using Extensions; using Settings; internal class QueryProjectionModifier : ExpressionVisitor @@ -32,6 +33,16 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig return base.VisitMemberAssignment(assignment); } + protected override Expression VisitConstant(ConstantExpression constant) + { + if (constant.Value is LambdaExpression lambda) + { + return VisitAndConvert(lambda, "ModifyLambda").ToConstantExpression(constant.Type); + } + + return base.VisitConstant(constant); + } + protected override Expression VisitMethodCall(MethodCallExpression methodCall) { if (ToStringConverter.TryConvert(methodCall, _settings, out var converted)) From 51d382284e2b1d8a94ec8f72b8a7097d102703b3 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 13:39:43 +0000 Subject: [PATCH 060/176] Comple type enumerable member projection supported in EF6 + EF Core, EF5 can't use ToList --- .../EnumerablePopulationBuilder.cs | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 7537cb400..18451cd2a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -32,10 +32,6 @@ internal class EnumerablePopulationBuilder (m.GetParameters().Length == 2) && (m.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2)); - private static readonly MethodInfo _asQueryableMethod = typeof(Queryable) - .GetPublicStaticMethods("AsQueryable") - .Last(m => m.ContainsGenericParameters); - private static readonly MethodInfo _forEachMethod = typeof(EnumerableExtensions) .GetPublicStaticMethods("ForEach") .First(); @@ -540,28 +536,18 @@ private Expression CreateSourceItemsProjection( Func projectionFuncFactory, bool counterRequired) { - MethodInfo linqSelectOverload; - - var isQueryableMapping = MapperData.Context.IsPartOfQueryableMapping(); + var isRootQueryableMapping = MapperData.SourceType.IsQueryable(); - if (isQueryableMapping) + if (isRootQueryableMapping || MapperData.Context.IsPartOfQueryableMapping()) { - if (!MapperData.SourceType.IsQueryable()) - { - var asQueryableMethod = _asQueryableMethod.MakeGenericMethod(Context.SourceElementType); - - sourceEnumerableValue = Expression.Call(asQueryableMethod, sourceEnumerableValue); - } - counterRequired = false; - linqSelectOverload = _queryableSelectMethod; } - else - { - linqSelectOverload = counterRequired + + var linqSelectOverload = isRootQueryableMapping + ? _queryableSelectMethod + : counterRequired ? _enumerableSelectWithIndexMethod : _enumerableSelectWithoutIndexMethod; - } ParameterExpression[] projectionFuncParameters; Type[] funcTypes; @@ -584,7 +570,7 @@ private Expression CreateSourceItemsProjection( projectionFuncFactory.Invoke(_sourceElementParameter, Counter), projectionFuncParameters); - if (isQueryableMapping) + if (isRootQueryableMapping) { projectionFuncType = typeof(Expression<>).MakeGenericType(projectionFuncType); projectionFunc = projectionFunc.ToConstantExpression(projectionFuncType); From ad5ebb55bd2054ce1f1ddab10a7eee1ac374d10e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 13:51:40 +0000 Subject: [PATCH 061/176] Reverting element mapping data creation check --- .../EnumerablePopulationBuilder.cs | 2 -- .../ObjectPopulation/MappingFactory.cs | 20 ++++++++++++++++++- .../ObjectMappingDataFactory.cs | 18 +---------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 18451cd2a..c648b56aa 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -449,8 +449,6 @@ public Expression GetElementConversion(Expression sourceElement, IObjectMappingD return GetSimpleElementConversion(sourceElement); } - mappingData = ObjectMappingDataFactory.ForElement(mappingData); - var targetMember = mappingData.MapperData.TargetMember; Expression existingElementValue; diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 7d567cd2d..9f1694de8 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -98,7 +98,10 @@ public static Expression GetElementMapping( { var mapperData = mappingData.MapperData; - mappingData = ObjectMappingDataFactory.ForElement(mappingData); + if (CreateElementMappingDataFor(mapperData)) + { + mappingData = ObjectMappingDataFactory.ForElement(mappingData); + } mapperData.TargetMember.MapCreating(mapperData.SourceMember); @@ -110,6 +113,21 @@ public static Expression GetElementMapping( return GetElementMapping(mappingData, sourceElementValue, targetElementValue); } + private static bool CreateElementMappingDataFor(IBasicMapperData mapperData) + { + if (!mapperData.TargetMemberIsEnumerableElement()) + { + return true; + } + + if (mapperData.TargetMember.IsEnumerable) + { + return !mapperData.TargetMember.ElementType.IsSimple(); + } + + return false; + } + public static Expression GetElementMapping( IObjectMappingData elementMappingData, Expression sourceElementValue, diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index f59b59073..9ffc1f2c0 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -153,30 +153,14 @@ public static IObjectMappingData ForChild( parent); } - public static IObjectMappingData ForElement(IObjectMappingData parentOrElement) + public static IObjectMappingData ForElement(IObjectMappingData parent) { - if (IsElementMappingData(parentOrElement.MapperData)) - { - return parentOrElement; - } - - var parent = parentOrElement; var sourceElementType = parent.MapperData.SourceMember.GetElementMember().Type; var targetElementType = parent.MapperData.TargetMember.GetElementMember().Type; return ForElement(sourceElementType, targetElementType, parent); } - private static bool IsElementMappingData(IBasicMapperData mapperData) - { - if (mapperData.TargetMemberIsEnumerableElement()) - { - return true; - } - - return !mapperData.TargetMember.IsEnumerable || mapperData.TargetMember.ElementType.IsSimple(); - } - public static IObjectMappingData ForElement( Type sourceElementType, Type targetElementType, From a1c96cedd758bd5d3ecc4b1fcef575ec1be77de6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 14:09:54 +0000 Subject: [PATCH 062/176] Removing pointless type argument from simple type conversion test interfaces --- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDoubles.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 3 +-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDoubles.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 3 +-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDoubles.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 3 +-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToDateTimes.cs | 5 ++--- .../SimpleTypeConversion/WhenConvertingToDoubles.cs | 4 ++-- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 3 +-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 4 ++-- .../SimpleTypeConversion/IStringConversionFailureTest.cs | 3 +-- .../IStringConversionValidationFailureTest.cs | 3 +-- .../SimpleTypeConversion/IStringConversionValidatorTest.cs | 3 +-- .../SimpleTypeConversion/IStringConverterTest.cs | 3 +-- 22 files changed, 36 insertions(+), 50 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 0041aef24..b37a0971c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { - using System; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; @@ -8,8 +7,8 @@ public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConverterTest, - IStringConversionValidatorTest + IStringConverterTest, + IStringConversionValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index f58d01dc9..17af52b67 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf5TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs index db9920edf..4a93b639f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -6,8 +6,8 @@ public class WhenConvertingToDoubles : WhenConvertingToDoubles, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDoubles(InMemoryEf5TestContext context) diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs index aafe86987..c25cb94ee 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,12 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IStringConversionFailureTest + IStringConversionFailureTest { public WhenConvertingToGuids(InMemoryEf5TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index 4bbae42b1..c3d61163a 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,8 +6,8 @@ public class WhenConvertingToInts : WhenConvertingToInts, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEf5TestContext context) diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 52c4608bb..c15556969 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion { - using System; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; @@ -8,8 +7,8 @@ public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConverterTest, - IStringConversionValidatorTest + IStringConverterTest, + IStringConversionValidatorTest { public WhenConvertingToDateTimes(LocalDbTestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 611b76ad0..9582c1e88 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEf6TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs index cb8e71efc..43a2fa2fd 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -6,8 +6,8 @@ public class WhenConvertingToDoubles : WhenConvertingToDoubles, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToDoubles(InMemoryEf6TestContext context) diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs index c078e235c..f56022435 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,12 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IStringConversionFailureTest + IStringConversionFailureTest { public WhenConvertingToGuids(InMemoryEf6TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 98a62dea9..8be83e7a2 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,8 +6,8 @@ public class WhenConvertingToInts : WhenConvertingToInts, - IStringConversionFailureTest, - IStringConversionValidationFailureTest + IStringConversionFailureTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEf6TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 3b831b078..29dab15f1 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs index 572bb238d..f067e152a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -6,8 +6,8 @@ public class WhenConvertingToDoubles : WhenConvertingToDoubles, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDoubles(InMemoryEfCore1TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs index 5a0e6f027..4ff46222d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,12 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IStringConverterTest + IStringConverterTest { public WhenConvertingToGuids(InMemoryEfCore1TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs index a0043695d..bea0b1fc5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,8 +6,8 @@ public class WhenConvertingToInts : WhenConvertingToInts, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore1TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs index fddbef4ff..dcbd6aa79 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,14 +1,13 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToDateTimes : WhenConvertingToDateTimes, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs index 852450c27..1fd67c41d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -6,8 +6,8 @@ public class WhenConvertingToDoubles : WhenConvertingToDoubles, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToDoubles(InMemoryEfCore2TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs index 19c0f5631..7997c385a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,13 +1,12 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { - using System; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; public class WhenConvertingToGuids : WhenConvertingToGuids, - IStringConverterTest + IStringConverterTest { public WhenConvertingToGuids(InMemoryEfCore2TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 94da2edc6..a2bf251ac 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -6,8 +6,8 @@ public class WhenConvertingToInts : WhenConvertingToInts, - IStringConverterTest, - IStringConversionValidationFailureTest + IStringConverterTest, + IStringConversionValidationFailureTest { public WhenConvertingToInts(InMemoryEfCore2TestContext context) : base(context) diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs index 92578229a..32bfefa37 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { - // ReSharper disable once UnusedTypeParameter - public interface IStringConversionFailureTest + public interface IStringConversionFailureTest { void ShouldErrorProjectingAParseableString(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs index ba878d5a7..58ac79148 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { - // ReSharper disable once UnusedTypeParameter - public interface IStringConversionValidationFailureTest + public interface IStringConversionValidationFailureTest { void ShouldErrorProjectingAnUnparseableString(); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs index 6b40749e7..10bcb3347 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { - // ReSharper disable once UnusedTypeParameter - public interface IStringConversionValidatorTest + public interface IStringConversionValidatorTest { void ShouldProjectAnUnparseableString(); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs index a32f8e079..d9a259a14 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { - // ReSharper disable once UnusedTypeParameter - public interface IStringConverterTest + public interface IStringConverterTest { void ShouldProjectAParseableString(); From bf426b946010c806394497873092e9778f35a2ed Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 18 Nov 2017 14:21:35 +0000 Subject: [PATCH 063/176] Introducing collection projection test interfaces as EF5 just can't do ToList --- .../WhenProjectingToEnumerableMembers.cs | 11 ++- .../WhenProjectingToEnumerableMembers.cs | 11 ++- .../WhenProjectingToEnumerableMembers.cs | 11 ++- .../WhenProjectingToEnumerableMembers.cs | 11 ++- .../AgileMapper.UnitTests.Orms.csproj | 4 +- .../ICollectionMemberProjectionFailureTest.cs | 7 ++ .../ICollectionMemberProjectorTest.cs | 7 ++ .../WhenProjectingToEnumerableMembers.cs | 95 +++++++++++++++++++ .../WhenProjectingToEnumerableMembers.cs | 90 ------------------ 9 files changed, 148 insertions(+), 99 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs create mode 100644 AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs delete mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs index 342ec174e..1a0d120c2 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { + using Enumerables; using Infrastructure; - using Orms; + using Xunit; - public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + public class WhenProjectingToEnumerableMembers : + WhenProjectingToEnumerableMembers, + ICollectionMemberProjectionFailureTest { public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingToAComplexTypeCollectionMember() + => RunShouldErrorProjectingToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs index 715a3fba8..b79933e22 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { + using Enumerables; using Infrastructure; - using Orms; + using Xunit; - public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + public class WhenProjectingToEnumerableMembers : + WhenProjectingToEnumerableMembers, + ICollectionMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs index 5b4e564e2..c433ec951 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { + using Enumerables; using Infrastructure; - using Orms; + using Xunit; - public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + public class WhenProjectingToEnumerableMembers : + WhenProjectingToEnumerableMembers, + ICollectionMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs index 7fe74ac8e..b1ca45f22 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { + using Enumerables; using Infrastructure; - using Orms; + using Xunit; - public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + public class WhenProjectingToEnumerableMembers : + WhenProjectingToEnumerableMembers, + ICollectionMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 41265a5a8..753d73aae 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -76,6 +76,8 @@ VersionInfo.cs + + @@ -118,7 +120,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs new file mode 100644 index 000000000..42be30cef --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables +{ + public interface ICollectionMemberProjectionFailureTest + { + void ShouldErrorProjectingToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs new file mode 100644 index 000000000..2759aa939 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables +{ + public interface ICollectionMemberProjectorTest + { + void ShouldProjectToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..32e97cb0c --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,95 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + + public abstract class WhenProjectingToEnumerableMembers : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingToEnumerableMembers(ITestContext context) + : base(context) + { + } + + #region Project -> Collection + + protected void RunShouldProjectToAComplexTypeCollectionMember() + => RunTest(ProjectToComplexTypeCollectionMember); + + protected void RunShouldErrorProjectingToAComplexTypeCollectionMember() + => RunTestAndExpectThrow(ProjectToComplexTypeCollectionMember); + + protected void ProjectToComplexTypeCollectionMember(TOrmContext context) + { + var rotaEntry1 = new RotaEntry + { + DayOfWeek = DayOfWeek.Monday, + PersonId = 10, + StartHour = 8, + StartMinute = 45, + EndHour = 5, + EndMinute = 15 + }; + + var rotaEntry2 = new RotaEntry + { + DayOfWeek = DayOfWeek.Tuesday, + PersonId = 8, + StartHour = 9, + StartMinute = 00, + EndHour = 4, + EndMinute = 30 + }; + + var rotaEntry3 = new RotaEntry + { + DayOfWeek = DayOfWeek.Friday, + PersonId = 51, + StartHour = 10, + StartMinute = 30, + EndHour = 10, + EndMinute = 31 + }; + + var rota = new Rota + { + StartDate = DateTime.Today, + EndDate = DateTime.Today.AddDays(7), + Entries = new List { rotaEntry1, rotaEntry2, rotaEntry3 } + }; + + context.Rotas.Add(rota); + context.SaveChanges(); + + var rotaDto = context.Rotas.Where(r => r.Id == 1).ProjectTo().First(); + + rotaDto.Id.ShouldBe(1); + rotaDto.StartDate.ShouldBe(rota.StartDate); + rotaDto.EndDate.ShouldBe(rota.EndDate); + rotaDto.Entries.Count.ShouldBe(rota.Entries.Count()); + + var i = 0; + + foreach (var rotaEntry in rota.Entries) + { + var rotaEntryDto = rotaDto.Entries.ElementAt(i); + + rotaEntryDto.Id.ShouldBe(rotaEntry.Id); + rotaEntryDto.DayOfWeek.ShouldBe(rotaEntry.DayOfWeek); + rotaEntryDto.PersonId.ShouldBe(rotaEntry.PersonId); + rotaEntryDto.StartHour.ShouldBe(rotaEntry.StartHour); + rotaEntryDto.StartMinute.ShouldBe(rotaEntry.StartMinute); + rotaEntryDto.EndHour.ShouldBe(rotaEntry.EndHour); + rotaEntryDto.EndMinute.ShouldBe(rotaEntry.EndMinute); + + ++i; + } + } + + #endregion + } +} diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs deleted file mode 100644 index 42419fe3b..000000000 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Infrastructure; - using Shouldly; - using TestClasses; - using Xunit; - - public abstract class WhenProjectingToEnumerableMembers : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenProjectingToEnumerableMembers(ITestContext context) - : base(context) - { - } - - [Fact] - public void ShouldProjectToAComplexTypeCollectionMember() - { - RunTest(context => - { - var rotaEntry1 = new RotaEntry - { - DayOfWeek = DayOfWeek.Monday, - PersonId = 10, - StartHour = 8, - StartMinute = 45, - EndHour = 5, - EndMinute = 15 - }; - - var rotaEntry2 = new RotaEntry - { - DayOfWeek = DayOfWeek.Tuesday, - PersonId = 8, - StartHour = 9, - StartMinute = 00, - EndHour = 4, - EndMinute = 30 - }; - - var rotaEntry3 = new RotaEntry - { - DayOfWeek = DayOfWeek.Friday, - PersonId = 51, - StartHour = 10, - StartMinute = 30, - EndHour = 10, - EndMinute = 31 - }; - - var rota = new Rota - { - StartDate = DateTime.Today, - EndDate = DateTime.Today.AddDays(7), - Entries = new List { rotaEntry1, rotaEntry2, rotaEntry3 } - }; - - context.Rotas.Add(rota); - context.SaveChanges(); - - var rotaDto = context.Rotas.Where(r => r.Id == 1).ProjectTo().First(); - - rotaDto.Id.ShouldBe(1); - rotaDto.StartDate.ShouldBe(rota.StartDate); - rotaDto.EndDate.ShouldBe(rota.EndDate); - rotaDto.Entries.Count.ShouldBe(rota.Entries.Count()); - - var i = 0; - - foreach (var rotaEntry in rota.Entries) - { - var rotaEntryDto = rotaDto.Entries.ElementAt(i); - - rotaEntryDto.Id.ShouldBe(rotaEntry.Id); - rotaEntryDto.DayOfWeek.ShouldBe(rotaEntry.DayOfWeek); - rotaEntryDto.PersonId.ShouldBe(rotaEntry.PersonId); - rotaEntryDto.StartHour.ShouldBe(rotaEntry.StartHour); - rotaEntryDto.StartMinute.ShouldBe(rotaEntry.StartMinute); - rotaEntryDto.EndHour.ShouldBe(rotaEntry.EndHour); - rotaEntryDto.EndMinute.ShouldBe(rotaEntry.EndMinute); - - ++i; - } - }); - } - } -} From ac6a954eeac9d5edf080fca63e559db63f1283b2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 23 Nov 2017 20:56:32 +0000 Subject: [PATCH 064/176] Test coverage for projecting to a nested complex type Enumerable - EF Core 1+ 2 covered --- .../Infrastructure/Ef5TestDbContext.cs | 10 +++++ .../Infrastructure/Ef6TestDbContext.cs | 10 +++++ .../Infrastructure/EfCore1TestDbContext.cs | 10 +++++ .../WhenProjectingToEnumerableMembers.cs | 7 ++- .../Infrastructure/EfCore2TestDbContext.cs | 10 +++++ .../WhenProjectingToEnumerableMembers.cs | 7 ++- .../AgileMapper.UnitTests.Orms.csproj | 5 +++ .../IEnumerableMemberProjectorTest.cs | 7 +++ .../WhenProjectingToEnumerableMembers.cs | 44 +++++++++++++++++++ .../Infrastructure/ITestDbContext.cs | 4 ++ .../TestClasses/Order.cs | 16 +++++++ .../TestClasses/OrderDto.cs | 14 ++++++ .../TestClasses/OrderItem.cs | 10 +++++ .../TestClasses/OrderItemDto.cs | 7 +++ 14 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Order.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/OrderDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/OrderItem.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/OrderItemDto.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index aba1d834d..2c88fc45a 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -28,6 +28,10 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet RotaEntries { get; set; } + public DbSet Orders { get; set; } + + public DbSet OrderItems { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -55,6 +59,12 @@ IDbSetWrapper ITestDbContext.Rotas IDbSetWrapper ITestDbContext.RotaEntries => new Ef5DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.Orders + => new Ef5DbSetWrapper(Orders); + + IDbSetWrapper ITestDbContext.OrderItems + => new Ef5DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.BoolItems => new Ef5DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 29b11dad7..3c3af6c23 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -28,6 +28,10 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet RotaEntries { get; set; } + public DbSet Orders { get; set; } + + public DbSet OrderItems { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -55,6 +59,12 @@ IDbSetWrapper ITestDbContext.Rotas IDbSetWrapper ITestDbContext.RotaEntries => new Ef6DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.Orders + => new Ef6DbSetWrapper(Orders); + + IDbSetWrapper ITestDbContext.OrderItems + => new Ef6DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.BoolItems => new Ef6DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 62c73efca..0382d7c59 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -26,6 +26,10 @@ public EfCore1TestDbContext() public DbSet RotaEntries { get; set; } + public DbSet Orders { get; set; } + + public DbSet OrderItems { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -53,6 +57,12 @@ IDbSetWrapper ITestDbContext.Rotas IDbSetWrapper ITestDbContext.RotaEntries => new EfCore1DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.Orders + => new EfCore1DbSetWrapper(Orders); + + IDbSetWrapper ITestDbContext.OrderItems + => new EfCore1DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore1DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs index c433ec951..30c11bac6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -6,7 +6,8 @@ public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest + ICollectionMemberProjectorTest, + IEnumerableMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) : base(context) @@ -16,5 +17,9 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) [Fact] public void ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); + + [Fact] + public void ShouldProjectToAComplexTypeEnumerableMember() + => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 2b0c289a7..d3bc06d4c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -34,6 +34,10 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet RotaEntries { get; set; } + public DbSet Orders { get; set; } + + public DbSet OrderItems { get; set; } + public DbSet BoolItems { get; set; } public DbSet ShortItems { get; set; } @@ -61,6 +65,12 @@ IDbSetWrapper ITestDbContext.Rotas IDbSetWrapper ITestDbContext.RotaEntries => new EfCore2DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.Orders + => new EfCore2DbSetWrapper(Orders); + + IDbSetWrapper ITestDbContext.OrderItems + => new EfCore2DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore2DbSetWrapper(BoolItems); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs index b1ca45f22..7f30d5c0f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -6,7 +6,8 @@ public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest + ICollectionMemberProjectorTest, + IEnumerableMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) : base(context) @@ -16,5 +17,9 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) [Fact] public void ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); + + [Fact] + public void ShouldProjectToAComplexTypeEnumerableMember() + => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 753d73aae..062e59d41 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -78,6 +78,7 @@ + @@ -94,6 +95,10 @@ + + + + diff --git a/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs new file mode 100644 index 000000000..964463f17 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables +{ + public interface IEnumerableMemberProjectorTest + { + void ShouldProjectToAComplexTypeEnumerableMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index 32e97cb0c..fb9b7ed26 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -91,5 +91,49 @@ protected void ProjectToComplexTypeCollectionMember(TOrmContext context) } #endregion + + #region Project -> Enumerable + + protected void RunShouldProjectToAComplexTypeEnumerableMember() + => RunTest(ProjectToComplexTypeEnumerableMember); + + protected void ProjectToComplexTypeEnumerableMember(TOrmContext context) + { + var item1 = new OrderItem + { + }; + + var item2 = new OrderItem + { + }; + + var order = new Order + { + DatePlaced = DateTime.Now, + Items = new List { item1, item2 } + }; + + context.Orders.Add(order); + context.SaveChanges(); + + var rotaDto = context.Orders.Where(r => r.Id == 1).ProjectTo().First(); + + rotaDto.Id.ShouldBe(1); + rotaDto.DatePlaced.ShouldBe(order.DatePlaced); + rotaDto.Items.Count().ShouldBe(order.Items.Count()); + + var i = 0; + + foreach (var orderItem in order.Items) + { + var orderItemDto = order.Items.ElementAt(i); + + orderItemDto.Id.ShouldBe(orderItem.Id); + + ++i; + } + } + + #endregion } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 5d180117c..9eba06f45 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -15,6 +15,10 @@ public interface ITestDbContext : IDisposable IDbSetWrapper RotaEntries { get; } + IDbSetWrapper Orders { get; } + + IDbSetWrapper OrderItems { get; } + IDbSetWrapper BoolItems { get; } IDbSetWrapper ShortItems { get; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs new file mode 100644 index 000000000..21436e2ae --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + + public class Order + { + [Key] + public int Id { get; set; } + + public DateTime DatePlaced { get; set; } + + public IEnumerable Items { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/OrderDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderDto.cs new file mode 100644 index 000000000..7e0a68faa --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderDto.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.Collections.Generic; + + public class OrderDto + { + public int Id { get; set; } + + public DateTime DatePlaced { get; set; } + + public IEnumerable Items { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/OrderItem.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderItem.cs new file mode 100644 index 000000000..4f71a164d --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderItem.cs @@ -0,0 +1,10 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class OrderItem + { + [Key] + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/OrderItemDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderItemDto.cs new file mode 100644 index 000000000..108f0f142 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderItemDto.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class OrderItemDto + { + public int Id { get; set; } + } +} \ No newline at end of file From fdda5e1634637bcea3266192a26f0cf640c2a0eb Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 24 Nov 2017 18:15:18 +0000 Subject: [PATCH 065/176] Support for projecting to IEnumerables - Ef5 + EF6 passing tests --- .../WhenProjectingToEnumerableMembers.cs | 7 ++++++- .../WhenProjectingToEnumerableMembers.cs | 7 ++++++- AgileMapper.UnitTests.Orms/TestClasses/Order.cs | 2 +- AgileMapper/MappingRuleSetCollection.cs | 3 ++- AgileMapper/MappingRuleSetSettings.cs | 2 ++ .../Enumerables/EnumerablePopulationBuilder.cs | 7 +++++-- .../Enumerables/EnumerableTypeHelper.cs | 13 ++++++++++++- 7 files changed, 34 insertions(+), 7 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs index 1a0d120c2..e8e41367b 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs @@ -6,7 +6,8 @@ public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers, - ICollectionMemberProjectionFailureTest + ICollectionMemberProjectionFailureTest, + IEnumerableMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) : base(context) @@ -16,5 +17,9 @@ public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) [Fact] public void ShouldErrorProjectingToAComplexTypeCollectionMember() => RunShouldErrorProjectingToAComplexTypeCollectionMember(); + + [Fact] + public void ShouldProjectToAComplexTypeEnumerableMember() + => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs index b79933e22..f1fd8e05a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs @@ -6,7 +6,8 @@ public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest + ICollectionMemberProjectorTest, + IEnumerableMemberProjectorTest { public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) : base(context) @@ -16,5 +17,9 @@ public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) [Fact] public void ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); + + [Fact] + public void ShouldProjectToAComplexTypeEnumerableMember() + => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs index 21436e2ae..cb7aa02b6 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs @@ -11,6 +11,6 @@ public class Order public DateTime DatePlaced { get; set; } - public IEnumerable Items { get; set; } + public ICollection Items { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index f74b69355..45fe8078a 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -53,7 +53,8 @@ internal class MappingRuleSetCollection new MappingRuleSetSettings { UseMemberInitialisation = true, - UseSingleRootMappingExpression = true + UseSingleRootMappingExpression = true, + AllowEnumerableAssignment = true }, ProjectSourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index 11ee436bb..5ec75fe61 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -13,5 +13,7 @@ internal class MappingRuleSetSettings public bool UseTryCatch { get; set; } public bool GuardMemberAccesses { get; set; } + + public bool AllowEnumerableAssignment { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index c648b56aa..dcbd7f953 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -653,9 +653,12 @@ private Expression ConvertForReturnValue(Expression value) return value; } - return TargetTypeHelper.GetEnumerableConversion(value); + return GetEnumerableConversion(value); } + public Expression GetEnumerableConversion(Expression value) + => TargetTypeHelper.GetEnumerableConversion(value, MapperData.RuleSet.Settings.AllowEnumerableAssignment); + private Expression GetTargetMethodCall(string methodName, Expression argument = null) { var method = TargetTypeHelper.CollectionInterfaceType.GetMethod(methodName) @@ -738,7 +741,7 @@ public Expression GetResult() return _result; } - _result = _builder.TargetTypeHelper.GetEnumerableConversion(_result); + _result = _builder.GetEnumerableConversion(_result); return _result; } diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 3325a2bcb..424041301 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -87,8 +87,14 @@ public Expression GetWrapperConstruction(Expression existingItems, Expression ne newItemsCount); } - public Expression GetEnumerableConversion(Expression instance) + public Expression GetEnumerableConversion(Expression instance, bool allowEnumerableAssignment) { + if (EnumerableType.IsAssignableFrom(instance.Type) && + (allowEnumerableAssignment || ValueIsNotEnumerableInterface(instance))) + { + return instance; + } + if (IsArray) { return instance.WithToArrayCall(ElementType); @@ -101,5 +107,10 @@ public Expression GetEnumerableConversion(Expression instance) return instance.WithToListCall(ElementType); } + + private static bool ValueIsNotEnumerableInterface(Expression instance) + { + return instance.Type != typeof(IEnumerable<>).MakeGenericType(instance.Type.GetEnumerableElementType()); + } } } \ No newline at end of file From f10ef92aa7857b7d0455340062d6db79a0b655f6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 24 Nov 2017 19:04:02 +0000 Subject: [PATCH 066/176] Excluding mapping plan comments from non mapping plan expressions / All-ORM test coverage for projecting to unmapped members --- .../WhenProjectingFlatTypes.cs | 42 ++++++++++++------- AgileMapper/Api/PlanTargetTypeSelector.cs | 5 ++- AgileMapper/IMappingContext.cs | 2 + AgileMapper/MappingExecutor.cs | 2 + .../MappingExpressionFactoryBase.cs | 11 +++-- .../MemberPopulationFactory.cs | 15 ++++++- AgileMapper/SimpleMappingContext.cs | 2 + 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 31e63a543..15d4bdc2f 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -19,30 +19,40 @@ public void ShouldProjectAFlatTypeToAnArray() { RunTest(context => { - context.Products.Add(new Product - { - ProductId = 1, - Name = "Product One" - }); - - context.Products.Add(new Product - { - ProductId = 2, - Name = "Product Two" - }); + var product1 = new Product { Name = "Product One" }; + var product2 = new Product { Name = "Product Two" }; + context.Products.Add(product1); + context.Products.Add(product2); context.SaveChanges(); - var products = context.Products.ToArray(); var productDtos = context.Products.ProjectTo().ToArray(); productDtos.Length.ShouldBe(2); - productDtos[0].ProductId.ShouldBe(products[0].ProductId); - productDtos[0].Name.ShouldBe(products[0].Name); + productDtos[0].ProductId.ShouldBe(product1.ProductId); + productDtos[0].Name.ShouldBe(product1.Name); - productDtos[1].ProductId.ShouldBe(products[1].ProductId); - productDtos[1].Name.ShouldBe(products[1].Name); + productDtos[1].ProductId.ShouldBe(product2.ProductId); + productDtos[1].Name.ShouldBe(product2.Name); + }); + } + + [Fact] + public void ShouldProjectAFlatTypeToANonMatchingTypeList() + { + RunTest(context => + { + var product = new Product { Name = "Uno" }; + + context.Products.Add(product); + context.SaveChanges(); + + var productDtos = context.Products.ProjectTo().ToList(); + + productDtos.ShouldHaveSingleItem(); + productDtos[0].Id.ShouldBe(product.ProductId); + productDtos[0].Value.ShouldBeNull(); }); } } diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index 6c01ba997..446237019 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -59,7 +59,10 @@ private MappingPlan GetMappingPlan( MappingRuleSet ruleSet, IEnumerable>>> configurations = null) { - var planContext = new SimpleMappingContext(ruleSet, _mapperContext); + var planContext = new SimpleMappingContext(ruleSet, _mapperContext) + { + AddUnsuccessfulMemberPopulations = true + }; if (configurations != null) { diff --git a/AgileMapper/IMappingContext.cs b/AgileMapper/IMappingContext.cs index bafafd714..a247626e1 100644 --- a/AgileMapper/IMappingContext.cs +++ b/AgileMapper/IMappingContext.cs @@ -5,5 +5,7 @@ internal interface IMappingContext MapperContext MapperContext { get; } MappingRuleSet RuleSet { get; } + + bool AddUnsuccessfulMemberPopulations { get; } } } \ No newline at end of file diff --git a/AgileMapper/MappingExecutor.cs b/AgileMapper/MappingExecutor.cs index 9e7fddbb9..d6fbc1f22 100644 --- a/AgileMapper/MappingExecutor.cs +++ b/AgileMapper/MappingExecutor.cs @@ -21,6 +21,8 @@ public MappingExecutor(TSource source, MapperContext mapperContext) public MappingRuleSet RuleSet { get; private set; } + public bool AddUnsuccessfulMemberPopulations => false; + #region Inline Configuration public TResult ToANew( diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index bcedfe549..74f14dd7a 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -155,12 +155,17 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping goto CreateFullMappingBlock; } - var localVariableAssignment = mappingExpressions.First(exp => exp.NodeType == ExpressionType.Assign); + var localVariableAssignment = (BinaryExpression)mappingExpressions.First(exp => exp.NodeType == ExpressionType.Assign); if (mappingExpressions.Last() == localVariableAssignment) { - var assignedValue = ((BinaryExpression)localVariableAssignment).Right; - returnExpression = GetReturnExpression(assignedValue, mappingExtras); + var assignedValue = localVariableAssignment.Right; + + returnExpression = (assignedValue.NodeType == ExpressionType.Invoke) + ? Expression.Block( + new[] { (ParameterExpression)localVariableAssignment.Left }, + GetReturnExpression(localVariableAssignment, mappingExtras)) + : GetReturnExpression(assignedValue, mappingExtras); if (mappingExpressions.HasOne()) { diff --git a/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs b/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs index 8de7a6d2b..35e17e540 100644 --- a/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs +++ b/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Linq.Expressions; using Configuration; using DataSources; + using Extensions; using Members; using Members.Population; @@ -28,7 +29,19 @@ public IEnumerable Create(IObjectMappingData mappingData) { return _targetMembersFactory .Invoke(mappingData.MapperData) - .Select(tm => Create(tm, mappingData)); + .Select(tm => + { + var memberPopulation = Create(tm, mappingData); + + if (memberPopulation.IsSuccessful || + mappingData.MappingContext.AddUnsuccessfulMemberPopulations) + { + return memberPopulation; + } + + return null; + }) + .WhereNotNull(); } private static IMemberPopulation Create(QualifiedMember targetMember, IObjectMappingData mappingData) diff --git a/AgileMapper/SimpleMappingContext.cs b/AgileMapper/SimpleMappingContext.cs index d675ff661..64850e413 100644 --- a/AgileMapper/SimpleMappingContext.cs +++ b/AgileMapper/SimpleMappingContext.cs @@ -12,5 +12,7 @@ public SimpleMappingContext(MappingRuleSet ruleSet, MapperContext mapperContext) public MapperContext MapperContext { get; } public MappingRuleSet RuleSet { get; } + + public bool AddUnsuccessfulMemberPopulations { get; set; } } } \ No newline at end of file From d08f38d5452f19abb9e7edb8c07e01d73f4e6884 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 24 Nov 2017 19:43:52 +0000 Subject: [PATCH 067/176] Support for generating projection mapping plans! --- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../WhenViewingMappingPlans.cs | 27 ++++++++++ .../Api/IPlanTargetTypeAndRuleSetSelector.cs | 11 ++++ AgileMapper/Api/PlanTargetTypeSelector.cs | 50 ++++++++++++++++++- .../ObjectMappingDataFactory.cs | 13 +++-- AgileMapper/Plans/MappingPlan.cs | 8 +-- AgileMapper/ProjectionExtensions.cs | 13 +---- AgileMapper/Properties/AssemblyInfo.cs | 1 + AgileMapper/Queryables/QueryProjectorKey.cs | 12 ++--- .../Settings/QueryProviderSettings.cs | 5 ++ 10 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 062e59d41..ea5f2ff6a 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -126,6 +126,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs new file mode 100644 index 000000000..d5a2897d6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -0,0 +1,27 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Linq; + using Shouldly; + using TestClasses; + using Xunit; + + public class WhenViewingMappingPlans + { + [Fact] + public void ShouldCreateAProjectionMappingPlan() + { + string plan = Mapper + .GetPlanFor() + .ProjectedTo(); + + plan.ShouldContain("Rule Set: Project"); + plan.ShouldContain("Source.Select("); + plan.ShouldContain("new ProductDto"); + + var cachedMapper = Mapper.Default.Context.ObjectMapperFactory.RootMappers.ShouldHaveSingleItem(); + + cachedMapper.MapperData.SourceType.ShouldBe(typeof(IQueryable)); + cachedMapper.MapperData.TargetType.ShouldBe(typeof(IQueryable)); + } + } +} diff --git a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs index 75c28be13..7264119a2 100644 --- a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs +++ b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs @@ -94,5 +94,16 @@ MappingPlan OnTo( /// MappingPlan Over( params Expression>>[] configurations); + + /// + /// Create and compile a mapping function for a Queryable projection 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 object detailing the function to be executed during a mapping. To see + /// a string representation of the function assign the result to a string variable, or call .ToString(). + /// + MappingPlan ProjectedTo(); } } \ No newline at end of file diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index 446237019..08f35b5d4 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -1,13 +1,34 @@ namespace AgileObjects.AgileMapper.Api { using System; + using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using AgileMapper.Configuration.Inline; using Configuration; + using ObjectPopulation; using Plans; + internal class MappingPlanPlaceholderQueryable : IQueryable + { + public static readonly IQueryable Default = new MappingPlanPlaceholderQueryable(); + + public Type ElementType => typeof(T); + + public Expression Expression => null; + + public IQueryProvider Provider => null; + + #region IEnumerable Members + + public IEnumerator GetEnumerator() => Enumerable.Empty.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + #endregion + } + internal class PlanTargetTypeSelector : IPlanTargetTypeSelector, IPlanTargetTypeAndRuleSetSelector @@ -55,6 +76,20 @@ public MappingPlan Over( Expression>>[] configurations) => GetMappingPlan(_mapperContext.RuleSets.Overwrite, configurations); + public MappingPlan ProjectedTo() + { + IObjectMappingData CreateProjectionMappingData(IMappingContext planContext) + { + return ObjectMappingDataFactory.ForProjection( + MappingPlanPlaceholderQueryable.Default, + planContext); + } + + return GetMappingPlan( + _mapperContext.QueryProjectionMappingContext, + CreateProjectionMappingData); + } + private MappingPlan GetMappingPlan( MappingRuleSet ruleSet, IEnumerable>>> configurations = null) @@ -64,13 +99,26 @@ private MappingPlan GetMappingPlan( AddUnsuccessfulMemberPopulations = true }; + return GetMappingPlan( + planContext, + ObjectMappingDataFactory.ForRootFixedTypes, + configurations); + } + + private static MappingPlan GetMappingPlan( + IMappingContext planContext, + Func mappingDataFactory, + IEnumerable>>> configurations = null) + { if (configurations != null) { InlineMappingConfigurator .ConfigureMapperContext(configurations, planContext); } - return MappingPlan.For(planContext); + var mappingData = mappingDataFactory.Invoke(planContext); + + return MappingPlan.For(mappingData); } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 9ffc1f2c0..99650330a 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -11,6 +11,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members; using Members.Sources; using NetStandardPolyfills; + using Queryables; internal class ObjectMappingDataFactory : IObjectMappingDataFactoryBridge { @@ -22,14 +23,18 @@ public static ObjectMappingData ForRootFixedTypes ForProjection( - ObjectMapperKeyBase projectorKey, - TSourceQueryable sourceQueryable, + public static ObjectMappingData, IQueryable> ForProjection( + IQueryable sourceQueryable, IMappingContext mappingContext) { + var projectorKey = new QueryProjectorKey( + MappingTypes.Fixed, + sourceQueryable.Provider, + mappingContext.MapperContext); + return ForRootFixedTypes( sourceQueryable, - default(TResultQueryable), + default(IQueryable), projectorKey, mappingContext); } diff --git a/AgileMapper/Plans/MappingPlan.cs b/AgileMapper/Plans/MappingPlan.cs index 438fcf78d..20389ee08 100644 --- a/AgileMapper/Plans/MappingPlan.cs +++ b/AgileMapper/Plans/MappingPlan.cs @@ -28,13 +28,7 @@ internal MappingPlan(IObjectMapper cachedMapper) } } - internal static MappingPlan For(IMappingContext mappingContext) - { - var mappingData = ObjectMappingDataFactory - .ForRootFixedTypes(mappingContext); - - return new MappingPlan(mappingData.Mapper); - } + internal static MappingPlan For(IObjectMappingData mappingData) => new MappingPlan(mappingData.Mapper); /// /// Converts the given to its string representation. diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 260b169e6..9ad5d3d79 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -4,10 +4,8 @@ using System.Linq; using System.Linq.Expressions; using Extensions; - using Members; using NetStandardPolyfills; using ObjectPopulation; - using Queryables; using Queryables.Api; /// @@ -95,16 +93,9 @@ internal static IQueryable ProjectQuery.Fixed, + var rootMappingData = ObjectMappingDataFactory.ForProjection( sourceQueryable, - mapperContext); - - var rootMappingData = ObjectMappingDataFactory - .ForProjection, IQueryable>( - projectorKey, - sourceQueryable, - mapperContext.QueryProjectionMappingContext); + mapperContext.QueryProjectionMappingContext); var queryProjection = rootMappingData.MapStart(); diff --git a/AgileMapper/Properties/AssemblyInfo.cs b/AgileMapper/Properties/AssemblyInfo.cs index 72af4dfea..47eb9d525 100644 --- a/AgileMapper/Properties/AssemblyInfo.cs +++ b/AgileMapper/Properties/AssemblyInfo.cs @@ -10,4 +10,5 @@ [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.NonParallel, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] +[assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.Orms, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] [assembly: InternalsVisibleTo("AgileObjects.AgileMapper.UnitTests.MoreTestClasses, PublicKey=0024000004800000940000000602000000240000525341310004000001000100570b21a39fbc3df774a5e60b41cd41078ebae1c9210a3ae4355d518a0abecab27f9346fbfe941618dc835e99ab21b75ff38e5815dceebdd8480dc14c0ee14f5cdcd3ace7f980173238c9d827f95a6f46100ff19a7dcf912c9291bf95dcd64694692a193428f2a35023bbed186f3c8f9769e01e5077a8ea5cabafe5d7948024af")] diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs index 6ebc1bc1a..36eff04cc 100644 --- a/AgileMapper/Queryables/QueryProjectorKey.cs +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -7,33 +7,31 @@ internal class QueryProjectorKey : ObjectMapperKeyBase { + private readonly IQueryProvider _queryProvider; private readonly MapperContext _mapperContext; public QueryProjectorKey( MappingTypes mappingTypes, - IQueryable sourceQueryable, + IQueryProvider queryProvider, MapperContext mapperContext) : base(mappingTypes) { + _queryProvider = queryProvider; _mapperContext = mapperContext; - SourceQueryable = sourceQueryable; } - public IQueryable SourceQueryable { get; } - public override IMembersSource GetMembersSource(IObjectMappingData parentMappingData) => _mapperContext.RootMembersSource; protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTypes) - => new QueryProjectorKey(newMappingTypes, SourceQueryable, _mapperContext); + => new QueryProjectorKey(newMappingTypes, _queryProvider, _mapperContext); public override bool Equals(object obj) { var otherKey = (QueryProjectorKey)obj; // ReSharper disable once PossibleNullReferenceException - return TypesMatch(otherKey) && - (otherKey.SourceQueryable.Provider == SourceQueryable.Provider); + return TypesMatch(otherKey) && (otherKey._queryProvider == _queryProvider); } #region ExcludeFromCodeCoverage diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs index fc45a07f4..d3a7151e6 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs @@ -12,6 +12,11 @@ internal static class QueryProviderSettings public static IQueryProviderSettings For(IQueryable queryable) { + if (queryable.Provider == null) + { + return _defaultSettings; + } + var queryableProviderAssemblyName = queryable.Provider.GetType().GetAssembly().GetName(); var queryableProviderName = queryableProviderAssemblyName.FullName; From d92d40f08aaf63269c476da168bc6fbfea093c43 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 11:13:34 +0000 Subject: [PATCH 068/176] Fixing projection mapping plan caching --- .../MappingExtensions.cs | 13 +++++--- .../WhenCreatingProjections.cs | 32 +++++++++++-------- AgileMapper/Queryables/QueryProjectorKey.cs | 17 +++++++--- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs b/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs index f18e7d8ee..0b78fd13e 100644 --- a/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs +++ b/AgileMapper.UnitTests.MoreTestClasses/MappingExtensions.cs @@ -1,18 +1,23 @@ namespace AgileObjects.AgileMapper.UnitTests.MoreTestClasses { + using System.Collections.Generic; using System.Linq; using Shouldly; public static class MappingExtensions { - public static void RootMapperCountShouldBeOne(this IMapper mapper) + public static object RootMapperCountShouldBeOne(this IMapper mapper) { - RootMapperCountShouldBe(mapper, 1); + return RootMapperCountShouldBe(mapper, 1).First(); } - public static void RootMapperCountShouldBe(this IMapper mapper, int expected) + public static ICollection RootMapperCountShouldBe(this IMapper mapper, int expected) { - ((Mapper)mapper).Context.ObjectMapperFactory.RootMappers.Count().ShouldBe(expected); + var rootMappers = ((Mapper)mapper).Context.ObjectMapperFactory.RootMappers.ToArray(); + + rootMappers.Length.ShouldBe(expected); + + return rootMappers; } } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index ac1ee9181..034451f38 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -18,24 +18,30 @@ public WhenCreatingProjections(InMemoryEfCore2TestContext context) [Fact] public void ShouldReuseACachedProjectionMapper() { - RunTest((context, mapper) => + RunTest(mapper => { - var stringDtos = context - .StringItems - .ProjectTo(c => c.Using(mapper)) - .ToArray(); + using (var context1 = new EfCore2TestDbContext()) + { + var stringDtos = context1 + .StringItems + .ProjectTo(c => c.Using(mapper)) + .ToArray(); - stringDtos.ShouldBeEmpty(); + stringDtos.ShouldBeEmpty(); + } - context.StringItems.Add(new PublicString { Id = 1, Value = "New!" }); - context.SaveChanges(); + using (var context2 = new EfCore2TestDbContext()) + { + context2.StringItems.Add(new PublicString { Id = 1, Value = "New!" }); + context2.SaveChanges(); - var moreStringDtos = context - .StringItems - .ProjectTo(c => c.Using(mapper)) - .ToArray(); + var moreStringDtos = context2 + .StringItems + .ProjectTo(c => c.Using(mapper)) + .ToArray(); - moreStringDtos.ShouldHaveSingleItem(); + moreStringDtos.ShouldHaveSingleItem(); + } mapper.RootMapperCountShouldBeOne(); }); diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs index 36eff04cc..96a02e6cb 100644 --- a/AgileMapper/Queryables/QueryProjectorKey.cs +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables { + using System; using System.Linq; using Members; using Members.Sources; @@ -7,16 +8,24 @@ internal class QueryProjectorKey : ObjectMapperKeyBase { - private readonly IQueryProvider _queryProvider; + private readonly Type _queryProviderType; private readonly MapperContext _mapperContext; public QueryProjectorKey( MappingTypes mappingTypes, IQueryProvider queryProvider, MapperContext mapperContext) + : this(mappingTypes, queryProvider.GetType(), mapperContext) + { + } + + public QueryProjectorKey( + MappingTypes mappingTypes, + Type queryProviderType, + MapperContext mapperContext) : base(mappingTypes) { - _queryProvider = queryProvider; + _queryProviderType = queryProviderType; _mapperContext = mapperContext; } @@ -24,14 +33,14 @@ public override IMembersSource GetMembersSource(IObjectMappingData parentMapping => _mapperContext.RootMembersSource; protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTypes) - => new QueryProjectorKey(newMappingTypes, _queryProvider, _mapperContext); + => new QueryProjectorKey(newMappingTypes, _queryProviderType, _mapperContext); public override bool Equals(object obj) { var otherKey = (QueryProjectorKey)obj; // ReSharper disable once PossibleNullReferenceException - return TypesMatch(otherKey) && (otherKey._queryProvider == _queryProvider); + return TypesMatch(otherKey) && (otherKey._queryProviderType == _queryProviderType); } #region ExcludeFromCodeCoverage From 87b141b4400b0379a1fe9abe138139af78823be4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 12:12:24 +0000 Subject: [PATCH 069/176] Generating query provider type-specific mapping plans --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenViewingMappingPlans.cs | 18 ++++++++ .../AgileMapper.UnitTests.Orms.csproj | 4 ++ .../Infrastructure/OrmTestClassBase.cs | 4 +- .../WhenViewingMappingPlans.cs | 46 ++++++++++++++----- .../Api/IPlanTargetTypeAndRuleSetSelector.cs | 13 ++++-- AgileMapper/Api/PlanTargetTypeSelector.cs | 35 ++++++-------- .../ObjectMappingDataFactory.cs | 7 +-- AgileMapper/ProjectionExtensions.cs | 8 ++++ .../Api/QueryProviderTypeSelector.cs | 32 +++++++++++++ .../QueryProjectionExpressionFactory.cs | 5 +- AgileMapper/Queryables/QueryProjectorKey.cs | 18 ++------ .../Settings/QueryProviderSettings.cs | 11 ++--- 13 files changed, 134 insertions(+), 68 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs create mode 100644 AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 71db1121b..427604147 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -154,6 +154,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs new file mode 100644 index 000000000..5439e992b --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using System; + using Infrastructure; + using Microsoft.EntityFrameworkCore.Query.Internal; + using Queryables.Api; + + public class WhenViewingMappingPlans : WhenViewingMappingPlans + { + public WhenViewingMappingPlans(InMemoryEfCore2TestContext context) + : base(context) + { + } + + protected override Type GetQueryProviderType(QueryProviderTypeSelector selector) + => selector.Using(); + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index ea5f2ff6a..c909d16a6 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -135,6 +135,10 @@ + + {049E1EE5-48CE-441A-B166-3CF6BEC17957} + AgileMapper.UnitTests.MoreTestClasses + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} AgileMapper diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 26bee1298..41b54d484 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -36,13 +36,13 @@ protected void RunTest(Action testAction) } } - protected void RunTest(Action testAction) + protected void RunTest(Action testAction) { try { using (var mapper = Mapper.CreateNew()) { - testAction.Invoke(Context, mapper); + testAction.Invoke(mapper); } } finally diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index d5a2897d6..b2c48eb77 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -1,27 +1,51 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { + using System; using System.Linq; + using Infrastructure; + using MoreTestClasses; + using ObjectPopulation; + using Queryables.Api; using Shouldly; using TestClasses; using Xunit; - public class WhenViewingMappingPlans + public abstract class WhenViewingMappingPlans : OrmTestClassBase + where TOrmContext : ITestDbContext, new() { + protected WhenViewingMappingPlans(ITestContext context) + : base(context) + { + } + [Fact] - public void ShouldCreateAProjectionMappingPlan() + public void ShouldCreateAProjectionMappingPlanForASpecificQueryProvider() { - string plan = Mapper - .GetPlanFor() - .ProjectedTo(); + RunTest(mapper => + { + string plan = mapper + .GetPlanFor() + .ProjectedTo(GetQueryProviderType); + + plan.ShouldContain("Rule Set: Project"); + plan.ShouldContain("Source.Select("); + plan.ShouldContain("new ProductDto"); - plan.ShouldContain("Rule Set: Project"); - plan.ShouldContain("Source.Select("); - plan.ShouldContain("new ProductDto"); + var cachedMapper = (IObjectMapper)mapper.RootMapperCountShouldBeOne(); - var cachedMapper = Mapper.Default.Context.ObjectMapperFactory.RootMappers.ShouldHaveSingleItem(); + cachedMapper.MapperData.SourceType.ShouldBe(typeof(IQueryable)); + cachedMapper.MapperData.TargetType.ShouldBe(typeof(IQueryable)); - cachedMapper.MapperData.SourceType.ShouldBe(typeof(IQueryable)); - cachedMapper.MapperData.TargetType.ShouldBe(typeof(IQueryable)); + // Trigger a mapping: + Context.Products.ProjectTo().ShouldBeEmpty(); + + var usedMapper = (IObjectMapper)mapper.RootMapperCountShouldBeOne(); + + usedMapper.ShouldBe(cachedMapper); + + }); } + + protected abstract Type GetQueryProviderType(QueryProviderTypeSelector selector); } } diff --git a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs index 7264119a2..c0e915c27 100644 --- a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs +++ b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs @@ -4,6 +4,7 @@ using System.Linq.Expressions; using Configuration; using Plans; + using Queryables.Api; /// /// Provides options to create and compile mapping functions for a particular type of mapping from the @@ -100,10 +101,16 @@ MappingPlan Over( /// configured to the type specified by the type argument. /// /// The type of object for which to create the mapping plan. + /// + /// A func providing the Type of IQueryProvider implementation with which the projection caching should be + /// performed. When available, some provider-specific features are used for query projection - supplying the + /// IQueryProvider Type enables generation and caching of a plan specific to the provider you're using. + /// /// - /// A object detailing the function to be executed during a mapping. To see - /// a string representation of the function assign the result to a string variable, or call .ToString(). + /// A object detailing the function to be executed during a Query projection, using + /// the given IQueryProvider. To see a string representation of the function assign the result to a string + /// variable, or call .ToString(). /// - MappingPlan ProjectedTo(); + MappingPlan ProjectedTo(Func queryProviderTypeSelector); } } \ No newline at end of file diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index 08f35b5d4..bb7aac64d 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -1,33 +1,16 @@ namespace AgileObjects.AgileMapper.Api { using System; - using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using AgileMapper.Configuration.Inline; using Configuration; + using Members; using ObjectPopulation; using Plans; - - internal class MappingPlanPlaceholderQueryable : IQueryable - { - public static readonly IQueryable Default = new MappingPlanPlaceholderQueryable(); - - public Type ElementType => typeof(T); - - public Expression Expression => null; - - public IQueryProvider Provider => null; - - #region IEnumerable Members - - public IEnumerator GetEnumerator() => Enumerable.Empty.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - } + using Queryables; + using Queryables.Api; internal class PlanTargetTypeSelector : IPlanTargetTypeSelector, @@ -76,12 +59,20 @@ public MappingPlan Over( Expression>>[] configurations) => GetMappingPlan(_mapperContext.RuleSets.Overwrite, configurations); - public MappingPlan ProjectedTo() + public MappingPlan ProjectedTo(Func queryProviderTypeSelector) { IObjectMappingData CreateProjectionMappingData(IMappingContext planContext) { + var queryProviderType = queryProviderTypeSelector.Invoke(QueryProviderTypeSelector.Instance); + + var projectorKey = new QueryProjectorKey( + MappingTypes.Fixed, + queryProviderType, + planContext.MapperContext); + return ObjectMappingDataFactory.ForProjection( - MappingPlanPlaceholderQueryable.Default, + projectorKey, + default(IQueryable), planContext); } diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 99650330a..8e7e31dcd 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -11,7 +11,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members; using Members.Sources; using NetStandardPolyfills; - using Queryables; internal class ObjectMappingDataFactory : IObjectMappingDataFactoryBridge { @@ -24,14 +23,10 @@ public static ObjectMappingData ForRootFixedTypes, IQueryable> ForProjection( + ObjectMapperKeyBase projectorKey, IQueryable sourceQueryable, IMappingContext mappingContext) { - var projectorKey = new QueryProjectorKey( - MappingTypes.Fixed, - sourceQueryable.Provider, - mappingContext.MapperContext); - return ForRootFixedTypes( sourceQueryable, default(IQueryable), diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 9ad5d3d79..36d7a4204 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -4,8 +4,10 @@ using System.Linq; using System.Linq.Expressions; using Extensions; + using Members; using NetStandardPolyfills; using ObjectPopulation; + using Queryables; using Queryables.Api; /// @@ -93,7 +95,13 @@ internal static IQueryable ProjectQuery.Fixed, + sourceQueryable.Provider.GetType(), + mapperContext); + var rootMappingData = ObjectMappingDataFactory.ForProjection( + projectorKey, sourceQueryable, mapperContext.QueryProjectionMappingContext); diff --git a/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs b/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs new file mode 100644 index 000000000..86e564521 --- /dev/null +++ b/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs @@ -0,0 +1,32 @@ +namespace AgileObjects.AgileMapper.Queryables.Api +{ + using System; + using System.Linq; + + /// + /// Provides the option to specify a Type of IQueryProvider implementation to use in a query projection caching. + /// + public class QueryProviderTypeSelector + { + internal static readonly QueryProviderTypeSelector Instance = new QueryProviderTypeSelector(); + + private QueryProviderTypeSelector() + { + } + + /// + /// Use the given in this query projection caching. + /// + /// + /// The Type of the IQueryProvider implementation to use in this query projection caching. + /// + /// + /// The Type of the IQueryProvider implementation to use in this query projection caching. + /// + public Type Using() + where TQueryProvider : IQueryProvider + { + return typeof(TQueryProvider); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 2ad4e9b9a..45f8a5c60 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables { using System.Collections.Generic; - using System.Linq; using System.Linq.Expressions; using Extensions; using ObjectPopulation; @@ -21,8 +20,8 @@ public override bool IsFor(IObjectMappingData mappingData) protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; - var queryable = mappingData.GetSource(); - var providerSettings = QueryProviderSettings.For(queryable); + var queryProviderType = ((QueryProjectorKey)mappingData.MapperKey).QueryProviderType; + var providerSettings = QueryProviderSettings.For(queryProviderType); var queryProjection = mapperData .EnumerablePopulationBuilder diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs index 96a02e6cb..4dce11a5c 100644 --- a/AgileMapper/Queryables/QueryProjectorKey.cs +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -1,46 +1,38 @@ namespace AgileObjects.AgileMapper.Queryables { using System; - using System.Linq; using Members; using Members.Sources; using ObjectPopulation; internal class QueryProjectorKey : ObjectMapperKeyBase { - private readonly Type _queryProviderType; private readonly MapperContext _mapperContext; - public QueryProjectorKey( - MappingTypes mappingTypes, - IQueryProvider queryProvider, - MapperContext mapperContext) - : this(mappingTypes, queryProvider.GetType(), mapperContext) - { - } - public QueryProjectorKey( MappingTypes mappingTypes, Type queryProviderType, MapperContext mapperContext) : base(mappingTypes) { - _queryProviderType = queryProviderType; + QueryProviderType = queryProviderType; _mapperContext = mapperContext; } + public Type QueryProviderType { get; } + public override IMembersSource GetMembersSource(IObjectMappingData parentMappingData) => _mapperContext.RootMembersSource; protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTypes) - => new QueryProjectorKey(newMappingTypes, _queryProviderType, _mapperContext); + => new QueryProjectorKey(newMappingTypes, QueryProviderType, _mapperContext); public override bool Equals(object obj) { var otherKey = (QueryProjectorKey)obj; // ReSharper disable once PossibleNullReferenceException - return TypesMatch(otherKey) && (otherKey._queryProviderType == _queryProviderType); + return TypesMatch(otherKey) && (otherKey.QueryProviderType == QueryProviderType); } #region ExcludeFromCodeCoverage diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs index d3a7151e6..456df74a2 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs @@ -1,6 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { - using System.Linq; + using System; using NetStandardPolyfills; internal static class QueryProviderSettings @@ -10,14 +10,9 @@ internal static class QueryProviderSettings private static readonly IQueryProviderSettings _efCore2Settings = new EfCore2QueryProviderSettings(); private static readonly IQueryProviderSettings _defaultSettings = new DefaultQueryProviderSettings(); - public static IQueryProviderSettings For(IQueryable queryable) + public static IQueryProviderSettings For(Type queryProviderType) { - if (queryable.Provider == null) - { - return _defaultSettings; - } - - var queryableProviderAssemblyName = queryable.Provider.GetType().GetAssembly().GetName(); + var queryableProviderAssemblyName = queryProviderType.GetAssembly().GetName(); var queryableProviderName = queryableProviderAssemblyName.FullName; if (queryableProviderName.Contains("EntityFrameworkCore")) From 206af9bc37fa1ca9f79e47201494da739426aaf9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 12:22:10 +0000 Subject: [PATCH 070/176] Test coverage for projection query mapping plan caching in EF Core 1 --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 5 +++++ .../WhenViewingMappingPlans.cs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 25eeba102..23fae4caf 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -196,6 +196,10 @@ {66522d44-19f5-4af5-9d43-483a3cd6f958} AgileMapper.UnitTests.Orms + + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} + AgileMapper + @@ -215,6 +219,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs new file mode 100644 index 000000000..93353d3ae --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using System; + using Infrastructure; + using Microsoft.EntityFrameworkCore.Query.Internal; + using Queryables.Api; + + public class WhenViewingMappingPlans : WhenViewingMappingPlans + { + public WhenViewingMappingPlans(InMemoryEfCore1TestContext context) + : base(context) + { + } + + protected override Type GetQueryProviderType(QueryProviderTypeSelector selector) + => selector.Using(); + } +} From 97e377a2d36a20541d58bb90c2a9908502aa66e6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 12:55:06 +0000 Subject: [PATCH 071/176] Updating query projection plan caching to use an example IQueryable instance and a dedicated GetPlanForProjecting() method instead of having the user explicitly supply the IQueryProvider / Mapping plan viewing and caching tests passing for all EF versions --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenViewingMappingPlans.cs | 12 +++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenViewingMappingPlans.cs | 12 +++++++ .../WhenViewingMappingPlans.cs | 6 ---- .../WhenViewingMappingPlans.cs | 6 ---- .../WhenViewingMappingPlans.cs | 8 ++--- .../Api/IPlanTargetTypeAndRuleSetSelector.cs | 18 ----------- AgileMapper/Api/PlanTargetTypeSelector.cs | 31 +++++++----------- AgileMapper/IMapper.cs | 31 ++++++++++++------ AgileMapper/IMapperInternal.cs | 7 ++++ AgileMapper/Mapper.cs | 8 +++++ .../ObjectMappingDataFactory.cs | 7 +++- AgileMapper/ProjectionExtensions.cs | 8 ----- .../Api/IProjectionPlanTargetTypeSelector.cs | 27 ++++++++++++++++ .../Api/QueryProviderTypeSelector.cs | 32 ------------------- 16 files changed, 109 insertions(+), 106 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs create mode 100644 AgileMapper/IMapperInternal.cs create mode 100644 AgileMapper/Queryables/Api/IProjectionPlanTargetTypeSelector.cs delete mode 100644 AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 75b2fa85e..45a2e81ec 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -98,6 +98,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs new file mode 100644 index 000000000..b0eeafd3f --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + + public class WhenViewingMappingPlans : WhenViewingMappingPlans + { + public WhenViewingMappingPlans(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 8d43a4585..33cfddaec 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -101,6 +101,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs new file mode 100644 index 000000000..d6b352c63 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + + public class WhenViewingMappingPlans : WhenViewingMappingPlans + { + public WhenViewingMappingPlans(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs index 93353d3ae..8f1af7427 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs @@ -1,9 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { - using System; using Infrastructure; - using Microsoft.EntityFrameworkCore.Query.Internal; - using Queryables.Api; public class WhenViewingMappingPlans : WhenViewingMappingPlans { @@ -11,8 +8,5 @@ public WhenViewingMappingPlans(InMemoryEfCore1TestContext context) : base(context) { } - - protected override Type GetQueryProviderType(QueryProviderTypeSelector selector) - => selector.Using(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs index 5439e992b..1ba74b7ce 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs @@ -1,9 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { - using System; using Infrastructure; - using Microsoft.EntityFrameworkCore.Query.Internal; - using Queryables.Api; public class WhenViewingMappingPlans : WhenViewingMappingPlans { @@ -11,8 +8,5 @@ public WhenViewingMappingPlans(InMemoryEfCore2TestContext context) : base(context) { } - - protected override Type GetQueryProviderType(QueryProviderTypeSelector selector) - => selector.Using(); } } diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index b2c48eb77..ae7ec873d 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -1,11 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { - using System; using System.Linq; using Infrastructure; using MoreTestClasses; using ObjectPopulation; - using Queryables.Api; using Shouldly; using TestClasses; using Xunit; @@ -24,8 +22,8 @@ public void ShouldCreateAProjectionMappingPlanForASpecificQueryProvider() RunTest(mapper => { string plan = mapper - .GetPlanFor() - .ProjectedTo(GetQueryProviderType); + .GetPlanForProjecting(Context.Products) + .To(); plan.ShouldContain("Rule Set: Project"); plan.ShouldContain("Source.Select("); @@ -45,7 +43,5 @@ public void ShouldCreateAProjectionMappingPlanForASpecificQueryProvider() }); } - - protected abstract Type GetQueryProviderType(QueryProviderTypeSelector selector); } } diff --git a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs index c0e915c27..75c28be13 100644 --- a/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs +++ b/AgileMapper/Api/IPlanTargetTypeAndRuleSetSelector.cs @@ -4,7 +4,6 @@ using System.Linq.Expressions; using Configuration; using Plans; - using Queryables.Api; /// /// Provides options to create and compile mapping functions for a particular type of mapping from the @@ -95,22 +94,5 @@ MappingPlan OnTo( /// MappingPlan Over( params Expression>>[] configurations); - - /// - /// Create and compile a mapping function for a Queryable projection 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 func providing the Type of IQueryProvider implementation with which the projection caching should be - /// performed. When available, some provider-specific features are used for query projection - supplying the - /// IQueryProvider Type enables generation and caching of a plan specific to the provider you're using. - /// - /// - /// A object detailing the function to be executed during a Query projection, using - /// the given IQueryProvider. To see a string representation of the function assign the result to a string - /// variable, or call .ToString(). - /// - MappingPlan ProjectedTo(Func queryProviderTypeSelector); } } \ No newline at end of file diff --git a/AgileMapper/Api/PlanTargetTypeSelector.cs b/AgileMapper/Api/PlanTargetTypeSelector.cs index bb7aac64d..6ec324561 100644 --- a/AgileMapper/Api/PlanTargetTypeSelector.cs +++ b/AgileMapper/Api/PlanTargetTypeSelector.cs @@ -6,17 +6,23 @@ using System.Linq.Expressions; using AgileMapper.Configuration.Inline; using Configuration; - using Members; using ObjectPopulation; using Plans; - using Queryables; using Queryables.Api; internal class PlanTargetTypeSelector : IPlanTargetTypeSelector, - IPlanTargetTypeAndRuleSetSelector + IPlanTargetTypeAndRuleSetSelector, + IProjectionPlanTargetTypeSelector { private readonly MapperContext _mapperContext; + private readonly IQueryable _exampleQueryable; + + public PlanTargetTypeSelector(MapperContext mapperContext, IQueryable exampleQueryable) + : this(mapperContext) + { + _exampleQueryable = exampleQueryable; + } internal PlanTargetTypeSelector(MapperContext mapperContext) { @@ -59,26 +65,11 @@ public MappingPlan Over( Expression>>[] configurations) => GetMappingPlan(_mapperContext.RuleSets.Overwrite, configurations); - public MappingPlan ProjectedTo(Func queryProviderTypeSelector) + MappingPlan IProjectionPlanTargetTypeSelector.To() { - IObjectMappingData CreateProjectionMappingData(IMappingContext planContext) - { - var queryProviderType = queryProviderTypeSelector.Invoke(QueryProviderTypeSelector.Instance); - - var projectorKey = new QueryProjectorKey( - MappingTypes.Fixed, - queryProviderType, - planContext.MapperContext); - - return ObjectMappingDataFactory.ForProjection( - projectorKey, - default(IQueryable), - planContext); - } - return GetMappingPlan( _mapperContext.QueryProjectionMappingContext, - CreateProjectionMappingData); + planContext => ObjectMappingDataFactory.ForProjection(_exampleQueryable, planContext)); } private MappingPlan GetMappingPlan( diff --git a/AgileMapper/IMapper.cs b/AgileMapper/IMapper.cs index e2643dc4b..a1a4fa38a 100644 --- a/AgileMapper/IMapper.cs +++ b/AgileMapper/IMapper.cs @@ -1,14 +1,11 @@ namespace AgileObjects.AgileMapper { using System; + using System.Linq; using System.Linq.Expressions; using Api; using Api.Configuration; - - internal interface IMapperInternal : IMapper - { - MapperContext Context { get; } - } + using Queryables.Api; /// /// Provides mapping and mapping configuration services. @@ -22,7 +19,7 @@ public interface IMapper : IDisposable IMapper CloneSelf(); /// - /// Create and compile mapping functions for a particular type of mapping of the source type specified by + /// Create and compile a mapping function 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 . @@ -30,22 +27,38 @@ public interface IMapper : IDisposable /// An instance specifying the source type for which a mapping plan should be created. /// /// - /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the function for which /// should be cached. /// IPlanTargetTypeAndRuleSetSelector GetPlanFor(TSource exampleInstance); /// - /// Create and compile mapping functions for a particular type of mapping of the source type + /// Create and compile a mapping function for a particular type of mapping of the source type /// specified by the type argument. /// /// The source type for which to create the mapping functions. /// - /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the functions for which + /// An IPlanTargetTypeAndRuleSetSelector with which to specify the type of mapping the function for which /// should be cached. /// IPlanTargetTypeAndRuleSetSelector GetPlanFor(); + /// + /// Create and compile a query projection function from the source IQueryable Type specified by the given + /// . + /// + /// + /// The type of element contained in the source IQueryable from which the projection function to be created will project. + /// + /// + /// An IQueryable instance specifying the source IQueryable for which a query projection mapping plan should be created. + /// + /// + /// An IProjectionPlanTargetTypeSelector with which to specify the target Type to which the query projection function to + /// be created should be cached. + /// + IProjectionPlanTargetTypeSelector GetPlanForProjecting(IQueryable exampleQueryable); + /// /// Create and compile mapping functions for mapping from the source type specified by the given /// , for all mapping types (create new, merge, overwrite). Use this diff --git a/AgileMapper/IMapperInternal.cs b/AgileMapper/IMapperInternal.cs new file mode 100644 index 000000000..2c8af200f --- /dev/null +++ b/AgileMapper/IMapperInternal.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper +{ + internal interface IMapperInternal : IMapper + { + MapperContext Context { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Mapper.cs b/AgileMapper/Mapper.cs index 3d97fd877..dc7d54b43 100644 --- a/AgileMapper/Mapper.cs +++ b/AgileMapper/Mapper.cs @@ -1,10 +1,12 @@ namespace AgileObjects.AgileMapper { using System; + using System.Linq; using System.Linq.Expressions; using Api; using Api.Configuration; using Plans; + using Queryables.Api; using Validation; /// @@ -173,6 +175,12 @@ public static dynamic Flatten(TSource source) where TSource : class IPlanTargetTypeAndRuleSetSelector IMapper.GetPlanFor() => GetPlan(); + IProjectionPlanTargetTypeSelector IMapper.GetPlanForProjecting( + IQueryable exampleQueryable) + { + return new PlanTargetTypeSelector(Context, exampleQueryable); + } + IPlanTargetTypeSelector IMapper.GetPlansFor(TSource exampleInstance) => GetPlan(); IPlanTargetTypeSelector IMapper.GetPlansFor() => GetPlan(); diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index 8e7e31dcd..c3fb1b5ad 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -11,6 +11,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members; using Members.Sources; using NetStandardPolyfills; + using Queryables; internal class ObjectMappingDataFactory : IObjectMappingDataFactoryBridge { @@ -23,10 +24,14 @@ public static ObjectMappingData ForRootFixedTypes, IQueryable> ForProjection( - ObjectMapperKeyBase projectorKey, IQueryable sourceQueryable, IMappingContext mappingContext) { + var projectorKey = new QueryProjectorKey( + MappingTypes.Fixed, + sourceQueryable.Provider.GetType(), + mappingContext.MapperContext); + return ForRootFixedTypes( sourceQueryable, default(IQueryable), diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 36d7a4204..9ad5d3d79 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -4,10 +4,8 @@ using System.Linq; using System.Linq.Expressions; using Extensions; - using Members; using NetStandardPolyfills; using ObjectPopulation; - using Queryables; using Queryables.Api; /// @@ -95,13 +93,7 @@ internal static IQueryable ProjectQuery.Fixed, - sourceQueryable.Provider.GetType(), - mapperContext); - var rootMappingData = ObjectMappingDataFactory.ForProjection( - projectorKey, sourceQueryable, mapperContext.QueryProjectionMappingContext); diff --git a/AgileMapper/Queryables/Api/IProjectionPlanTargetTypeSelector.cs b/AgileMapper/Queryables/Api/IProjectionPlanTargetTypeSelector.cs new file mode 100644 index 000000000..6c64dc74a --- /dev/null +++ b/AgileMapper/Queryables/Api/IProjectionPlanTargetTypeSelector.cs @@ -0,0 +1,27 @@ +namespace AgileObjects.AgileMapper.Queryables.Api +{ + using Plans; + + /// + /// Provides options to create and compile a query projection function from the source type being configured + /// to a specified target type. + /// + /// + /// The type of element contained in the source IQueryable from which the projection function to be created will project. + /// + // ReSharper disable once UnusedTypeParameter + public interface IProjectionPlanTargetTypeSelector + { + /// + /// Create and compile a query projection function from the source type being configured to the type specified + /// by the type argument. + /// + /// The type of target object for which to create the query projection mapping plan. + /// + /// A object detailing the function to be executed during a query projection, using + /// the given source IQueryable. To see a string representation of the function assign the result to a string + /// variable, or call .ToString(). + /// + MappingPlan To(); + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs b/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs deleted file mode 100644 index 86e564521..000000000 --- a/AgileMapper/Queryables/Api/QueryProviderTypeSelector.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace AgileObjects.AgileMapper.Queryables.Api -{ - using System; - using System.Linq; - - /// - /// Provides the option to specify a Type of IQueryProvider implementation to use in a query projection caching. - /// - public class QueryProviderTypeSelector - { - internal static readonly QueryProviderTypeSelector Instance = new QueryProviderTypeSelector(); - - private QueryProviderTypeSelector() - { - } - - /// - /// Use the given in this query projection caching. - /// - /// - /// The Type of the IQueryProvider implementation to use in this query projection caching. - /// - /// - /// The Type of the IQueryProvider implementation to use in this query projection caching. - /// - public Type Using() - where TQueryProvider : IQueryProvider - { - return typeof(TQueryProvider); - } - } -} \ No newline at end of file From 293821051d7cbb0a274f217da1ab650d8c7821d5 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:24:56 +0000 Subject: [PATCH 072/176] Test coverage for inclusion of query projection mapping plans in result of GetPlansInCache() --- .../WhenViewingMappingPlans.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index ae7ec873d..96fdc4767 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -17,7 +17,7 @@ protected WhenViewingMappingPlans(ITestContext context) } [Fact] - public void ShouldCreateAProjectionMappingPlanForASpecificQueryProvider() + public void ShouldCreateAQueryProjectionPlanForASpecificQueryProvider() { RunTest(mapper => { @@ -40,7 +40,23 @@ public void ShouldCreateAProjectionMappingPlanForASpecificQueryProvider() var usedMapper = (IObjectMapper)mapper.RootMapperCountShouldBeOne(); usedMapper.ShouldBe(cachedMapper); + }); + } + + [Fact] + public void ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() + { + RunTest(mapper => + { + mapper.GetPlanForProjecting(Context.Products).To(); + mapper.GetPlanForProjecting(Context.StringItems).To(); + mapper.GetPlanForProjecting(Context.Persons).To(); + + var allPlans = mapper.GetPlansInCache(); + allPlans.ShouldContain("IQueryable -> IQueryable"); + allPlans.ShouldContain("IQueryable -> IQueryable"); + allPlans.ShouldContain("IQueryable -> IQueryable"); }); } } From 5e7c5d12275c510e35feaf0bae023c931a8ac01c Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:28:33 +0000 Subject: [PATCH 073/176] Updating to latest version of System.Security.Cryptography.X509Certificates --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 5 +++-- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 23fae4caf..babbcc800 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -164,8 +164,9 @@ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll + + ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll + True diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 2e7928658..9d6233984 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -12,7 +12,7 @@ - + @@ -53,7 +53,7 @@ - + From 71fc643d78bb441383e0dbbb05a51031425e72f1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:29:56 +0000 Subject: [PATCH 074/176] Updating to latest version of System.Security.Cryptography.Algorithms --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 5 +++-- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index babbcc800..7b48ba07e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -155,8 +155,9 @@ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll True - - ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll + + ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll + True ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 9d6233984..05c364ef0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -50,7 +50,7 @@ - + From ed48c1c86b965939410682bbb03e2e51632fca59 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:31:41 +0000 Subject: [PATCH 075/176] Updating to latest version of System.Net.Http --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 5 +++-- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 7b48ba07e..a893564e2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -129,8 +129,9 @@ ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll + + ..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll + True ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 05c364ef0..1ca55b231 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -35,7 +35,7 @@ - + From d2be7460cbc84aec4cbda85df43b7fb8c0af3b1b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:33:45 +0000 Subject: [PATCH 076/176] Updating to latest version of xunit.analyzers --- .../AgileMapper.UnitTests.NonParallel.csproj | 3 --- AgileMapper.UnitTests.NonParallel/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config | 8 ++++++++ AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef5/App.config | 8 ++++++++ AgileMapper.UnitTests.Orms.Ef5/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 3 --- AgileMapper.UnitTests.Orms.Ef6/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 3 --- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 3 --- AgileMapper.UnitTests.Orms.EfCore2/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.csproj | 3 --- AgileMapper.UnitTests.Orms/packages.config | 2 +- AgileMapper.UnitTests/AgileMapper.UnitTests.csproj | 3 --- AgileMapper.UnitTests/packages.config | 2 +- 20 files changed, 25 insertions(+), 36 deletions(-) diff --git a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj index 35d507c59..1f736295b 100644 --- a/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj +++ b/AgileMapper.UnitTests.NonParallel/AgileMapper.UnitTests.NonParallel.csproj @@ -108,9 +108,6 @@ - - - diff --git a/AgileMapper.UnitTests.NonParallel/packages.config b/AgileMapper.UnitTests.NonParallel/packages.config index 7250c09dd..d1e54e0c5 100644 --- a/AgileMapper.UnitTests.NonParallel/packages.config +++ b/AgileMapper.UnitTests.NonParallel/packages.config @@ -3,7 +3,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index b7282dc77..da78c6fc5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -100,9 +100,6 @@ - - - diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config index e5af0d11e..da5fa9f0e 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/App.config @@ -11,4 +11,12 @@ + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config index b38cae77f..ff833615c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config @@ -4,7 +4,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 45a2e81ec..f3ee93b1a 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -114,9 +114,6 @@ - - - diff --git a/AgileMapper.UnitTests.Orms.Ef5/App.config b/AgileMapper.UnitTests.Orms.Ef5/App.config index 62fb4da2a..3d5ee2c10 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/App.config +++ b/AgileMapper.UnitTests.Orms.Ef5/App.config @@ -14,4 +14,12 @@ + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/packages.config b/AgileMapper.UnitTests.Orms.Ef5/packages.config index c36b96b5f..52c92f5ea 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef5/packages.config @@ -5,7 +5,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj index 04a47790f..d9507c0f2 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj @@ -101,9 +101,6 @@ - - - diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config index 3222059d0..32c45b043 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config @@ -4,7 +4,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 33cfddaec..cf49e10ee 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -117,9 +117,6 @@ - - - diff --git a/AgileMapper.UnitTests.Orms.Ef6/packages.config b/AgileMapper.UnitTests.Orms.Ef6/packages.config index 84fd91cb1..c02338986 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef6/packages.config @@ -5,7 +5,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index a893564e2..93083e673 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -204,9 +204,6 @@ AgileMapper - - - diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index 1ca55b231..ee769940a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -64,7 +64,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 427604147..a1b1bd4fe 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -161,9 +161,6 @@ Designer - - - {049e1ee5-48ce-441a-b166-3cf6bec17957} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config index 905853b0e..c63923f24 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -31,7 +31,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index c909d16a6..cf49415df 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -131,9 +131,6 @@ - - - {049E1EE5-48CE-441A-B166-3CF6BEC17957} diff --git a/AgileMapper.UnitTests.Orms/packages.config b/AgileMapper.UnitTests.Orms/packages.config index 969ebba40..8a4b33309 100644 --- a/AgileMapper.UnitTests.Orms/packages.config +++ b/AgileMapper.UnitTests.Orms/packages.config @@ -3,7 +3,7 @@ - + diff --git a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj index 4ddad3801..5d1f22cb7 100644 --- a/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj +++ b/AgileMapper.UnitTests/AgileMapper.UnitTests.csproj @@ -263,9 +263,6 @@ AgileMapper - - - diff --git a/AgileMapper.UnitTests/packages.config b/AgileMapper.UnitTests/packages.config index 15b5f3e32..945c98c10 100644 --- a/AgileMapper.UnitTests/packages.config +++ b/AgileMapper.UnitTests/packages.config @@ -5,7 +5,7 @@ - + From 608f6e2f516331f748866546a00adb1183baafff Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:38:01 +0000 Subject: [PATCH 077/176] Updating to latest version of Microsoft.NETCore.Platforms --- AgileMapper.UnitTests.Orms.EfCore1/packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index ee769940a..f2c58b94f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -10,7 +10,7 @@ - + From 94cc2a1ce66f15951bcf47d36303a1fe267e978b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 25 Nov 2017 13:57:14 +0000 Subject: [PATCH 078/176] Extending use of Expression.GetConversionTo() --- AgileMapper.UnitTests/Members/MemberTestsBase.cs | 2 +- AgileMapper/Extensions/ExpressionExtensions.cs | 8 ++++++-- AgileMapper/Flattening/ObjectFlattener.cs | 2 +- AgileMapper/Queryables/DefaultExpressionConverter.cs | 2 +- .../Settings/QueryProviderSettingsExtensions.cs | 2 +- AgileMapper/TypeConversion/ToCharacterConverter.cs | 2 +- AgileMapper/TypeConversion/ToEnumConverter.cs | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/AgileMapper.UnitTests/Members/MemberTestsBase.cs b/AgileMapper.UnitTests/Members/MemberTestsBase.cs index cc384bc21..41b538b8a 100644 --- a/AgileMapper.UnitTests/Members/MemberTestsBase.cs +++ b/AgileMapper.UnitTests/Members/MemberTestsBase.cs @@ -17,7 +17,7 @@ internal IQualifiedMember SourceMemberFor(T sourceObject) var sourceParameter = Parameters.Create("source"); var sourceProperty = typeof(T).GetPublicInstanceProperties().First(); var sourcePropertyAccess = Expression.Property(sourceParameter, sourceProperty); - var sourcePropertyCastToObject = sourcePropertyAccess.GetConversionTo(typeof(object)); + var sourcePropertyCastToObject = sourcePropertyAccess.GetConversionToObject(); var sourcePropertyLambda = Expression.Lambda>(sourcePropertyCastToObject, sourceParameter); return SourceMemberFor(sourceObject, sourcePropertyLambda); diff --git a/AgileMapper/Extensions/ExpressionExtensions.cs b/AgileMapper/Extensions/ExpressionExtensions.cs index 73c5e9331..64d3d039f 100644 --- a/AgileMapper/Extensions/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/ExpressionExtensions.cs @@ -123,8 +123,8 @@ public static Expression GetIsNotDefaultComparison(this Expression expression) var objectEqualsCall = Expression.Call( null, objectEquals, - expression.GetConversionTo(typeof(object)), - typeDefault.GetConversionTo(typeof(object))); + expression.GetConversionToObject(), + typeDefault.GetConversionToObject()); return Expression.IsFalse(objectEqualsCall); } @@ -154,6 +154,10 @@ public static Expression GetValueOrDefaultCall(this Expression nullableExpressio return Expression.Call(nullableExpression, parameterlessGetValueOrDefault); } + [DebuggerStepThrough] + public static Expression GetConversionToObject(this Expression expression) + => GetConversionTo(expression); + [DebuggerStepThrough] public static Expression GetConversionTo(this Expression expression) => GetConversionTo(expression, typeof(T)); diff --git a/AgileMapper/Flattening/ObjectFlattener.cs b/AgileMapper/Flattening/ObjectFlattener.cs index 7a18164c5..11f2e43bc 100644 --- a/AgileMapper/Flattening/ObjectFlattener.cs +++ b/AgileMapper/Flattening/ObjectFlattener.cs @@ -92,7 +92,7 @@ private object GetValue(TSource source, Member member) if (member.Type.IsValueType()) { - valueAccess = valueAccess.GetConversionTo(typeof(object)); + valueAccess = valueAccess.GetConversionToObject(); } var valueLambda = Expression.Lambda>(valueAccess, sourceParameter); diff --git a/AgileMapper/Queryables/DefaultExpressionConverter.cs b/AgileMapper/Queryables/DefaultExpressionConverter.cs index 2e315217a..4ba6aaf81 100644 --- a/AgileMapper/Queryables/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/DefaultExpressionConverter.cs @@ -26,7 +26,7 @@ private static object GetDefaultValueFor(Type type) .Call(typeof(DefaultExpressionConverter) .GetNonPublicStaticMethod("GetDefaultValue") .MakeGenericMethod(t)) - .GetConversionTo(typeof(object)); + .GetConversionToObject(); var getDefaultValueLambda = Expression.Lambda>(getDefaultValueCall); diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 0561f18e0..add7907b0 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -50,7 +50,7 @@ public static Expression GetCreateDateTimeFromStringOrNull( GetDatePartCall(datePartMethod, "dd", sourceValue), GetDatePartCall(datePartMethod, "hh", sourceValue), GetDatePartCall(datePartMethod, "mi", sourceValue), - GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo(typeof(double?))); + GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo()); if (fallbackValue.NodeType == ExpressionType.Default) { diff --git a/AgileMapper/TypeConversion/ToCharacterConverter.cs b/AgileMapper/TypeConversion/ToCharacterConverter.cs index 383d61af8..839b81f10 100644 --- a/AgileMapper/TypeConversion/ToCharacterConverter.cs +++ b/AgileMapper/TypeConversion/ToCharacterConverter.cs @@ -47,7 +47,7 @@ public override Expression GetConversion(Expression sourceValue, Type targetType if (sourceValue.Type.GetNonNullableType().IsEnum()) { - sourceValue = sourceValue.GetConversionTo(typeof(int)); + sourceValue = sourceValue.GetConversionTo(); } return GetFromNumericConversion(sourceValue, targetType); diff --git a/AgileMapper/TypeConversion/ToEnumConverter.cs b/AgileMapper/TypeConversion/ToEnumConverter.cs index cf8776a32..d6b198f40 100644 --- a/AgileMapper/TypeConversion/ToEnumConverter.cs +++ b/AgileMapper/TypeConversion/ToEnumConverter.cs @@ -83,7 +83,7 @@ private static Expression GetParseSuccessBranch( null, typeof(Enum).GetPublicStaticMethod("IsDefined"), valueVariable.Type.ToConstantExpression(), - valueVariable.GetConversionTo(typeof(object))); + valueVariable.GetConversionToObject()); var definedValueOrDefault = Expression.Condition(isDefinedCall, successfulParseReturnValue, defaultValue); From b42b9a88cb8316d8765660b029ce2468ece44fd8 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 26 Nov 2017 11:35:49 +0000 Subject: [PATCH 079/176] Support for projecting types with one-to-one circular relationships - EF Core 2 test passing / Extending use of NetStandardPolyfills GetPublicInstanceMethod --- .../Infrastructure/Ef5TestDbContext.cs | 10 +++ .../Infrastructure/Ef6TestDbContext.cs | 10 +++ .../Infrastructure/EfCore1TestDbContext.cs | 21 ++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 21 ++++++ .../WhenMappingCircularReferences.cs | 13 ++++ .../AgileMapper.UnitTests.Orms.csproj | 6 ++ .../Infrastructure/ITestDbContext.cs | 4 ++ .../Infrastructure/OrmTestClassBase.cs | 2 + .../TestClasses/Address.cs | 2 + .../TestClasses/AddressDto.cs | 13 ++++ .../TestClasses/Company.cs | 20 ++++++ .../TestClasses/CompanyDto.cs | 13 ++++ .../TestClasses/Employee.cs | 24 +++++++ .../TestClasses/EmployeeDto.cs | 17 +++++ .../WhenMappingCircularReferences.cs | 69 +++++++++++++++++++ .../Extensions/ExpressionExtensions.cs | 4 +- AgileMapper/MappingRuleSetCollection.cs | 12 +++- AgileMapper/MappingRuleSetSettings.cs | 4 ++ AgileMapper/Members/DictionaryTargetMember.cs | 3 +- .../Members/MemberMapperDataExtensions.cs | 12 ++-- .../ComplexTypeMappingExpressionFactory.cs | 12 ++-- .../PopulationExpressionFactoryBase.cs | 8 ++- .../TargetObjectResolutionFactory.cs | 1 + .../EnumerablePopulationBuilder.cs | 4 +- .../EnumerableSourcePopulationLoopData.cs | 7 +- ...rceElementsDictionaryPopulationLoopData.cs | 6 +- .../ObjectPopulation/MappingFactory.cs | 8 ++- .../ObjectMappingDataFactory.cs | 9 +-- 29 files changed, 299 insertions(+), 37 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/AddressDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Company.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/CompanyDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Employee.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/EmployeeDto.cs create mode 100644 AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 2c88fc45a..b42eae189 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -18,6 +18,10 @@ protected Ef5TestDbContext(DbConnection dbConnection) { } + public DbSet Companies { get; set; } + + public DbSet Employees { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -44,6 +48,12 @@ protected Ef5TestDbContext(DbConnection dbConnection) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Companies + => new Ef5DbSetWrapper(Companies); + + IDbSetWrapper ITestDbContext.Employees + => new Ef5DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 3c3af6c23..f47ef4048 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -18,6 +18,10 @@ protected Ef6TestDbContext(DbConnection dbConnection) { } + public DbSet Companies { get; set; } + + public DbSet Employees { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -44,6 +48,12 @@ protected Ef6TestDbContext(DbConnection dbConnection) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Companies + => new Ef6DbSetWrapper(Companies); + + IDbSetWrapper ITestDbContext.Employees + => new Ef6DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 0382d7c59..4d5507060 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -16,6 +16,10 @@ public EfCore1TestDbContext() { } + public DbSet Companies { get; set; } + + public DbSet Employees { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -40,8 +44,25 @@ public EfCore1TestDbContext() public DbSet StringItems { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasOne(c => c.Ceo) + .WithOne(e => e.Company) + .HasForeignKey(e => e.CompanyId); + + base.OnModelCreating(modelBuilder); + } + #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Companies + => new EfCore1DbSetWrapper(Companies); + + IDbSetWrapper ITestDbContext.Employees + => new EfCore1DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index a1b1bd4fe..d91fc49f2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -153,6 +153,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index d3bc06d4c..1e0e4b9ad 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -24,6 +24,10 @@ protected EfCore2TestDbContext(DbContextOptions options) { } + public DbSet Companies { get; set; } + + public DbSet Employees { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -48,8 +52,25 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet StringItems { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasOne(c => c.Ceo) + .WithOne(e => e.Company) + .HasForeignKey(e => e.CompanyId); + + base.OnModelCreating(modelBuilder); + } + #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Companies + => new EfCore2DbSetWrapper(Companies); + + IDbSetWrapper ITestDbContext.Employees + => new EfCore2DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs new file mode 100644 index 000000000..f4720ad8f --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + + public class WhenMappingCircularReferences : + WhenMappingCircularReferences + { + public WhenMappingCircularReferences(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index cf49415df..f4104ea71 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -95,6 +95,11 @@ + + + + + @@ -123,6 +128,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 9eba06f45..c669e538c 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -5,6 +5,10 @@ public interface ITestDbContext : IDisposable { + IDbSetWrapper Companies { get; } + + IDbSetWrapper Employees { get; } + IDbSetWrapper Products { get; } IDbSetWrapper Persons { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 41b54d484..7bae67e08 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -53,6 +53,8 @@ protected void RunTest(Action testAction) private void EmptyDbContext() { + Context.Companies.Clear(); + Context.Employees.Clear(); Context.Products.Clear(); Context.Addresses.Clear(); Context.Persons.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Address.cs b/AgileMapper.UnitTests.Orms/TestClasses/Address.cs index 418caee4e..b93b6a978 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Address.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Address.cs @@ -10,5 +10,7 @@ public class Address public string Line1 { get; set; } public string Line2 { get; set; } + + public string Postcode { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AddressDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/AddressDto.cs new file mode 100644 index 000000000..32c8a7241 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/AddressDto.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class AddressDto + { + public int Id { get; set; } + + public string Line1 { get; set; } + + public string Line2 { get; set; } + + public string Postcode { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Company.cs b/AgileMapper.UnitTests.Orms/TestClasses/Company.cs new file mode 100644 index 000000000..09f40534b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Company.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class Company + { + [Key] + public int Id { get; set; } + + public string Name { get; set; } + + public int HeadOfficeId { get; set; } + + public Address HeadOffice { get; set; } + + public int? CeoId { get; set; } + + public Employee Ceo { get; set; } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/CompanyDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/CompanyDto.cs new file mode 100644 index 000000000..7a0980bf5 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/CompanyDto.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class CompanyDto + { + public int Id { get; set; } + + public string Name { get; set; } + + public AddressDto HeadOffice { get; set; } + + public EmployeeDto Ceo { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Employee.cs b/AgileMapper.UnitTests.Orms/TestClasses/Employee.cs new file mode 100644 index 000000000..0484a852d --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Employee.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.ComponentModel.DataAnnotations; + + public class Employee + { + [Key] + public int Id { get; set; } + + public DateTime DateOfBirth { get; set; } + + public string Name { get; set; } + + public int CompanyId { get; set; } + + [Required] + public Company Company { get; set; } + + public int AddressId { get; set; } + + public Address Address { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/EmployeeDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/EmployeeDto.cs new file mode 100644 index 000000000..4f557258e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/EmployeeDto.cs @@ -0,0 +1,17 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + + public class EmployeeDto + { + public int Id { get; set; } + + public DateTime DateOfBirth { get; set; } + + public string Name { get; set; } + + public CompanyDto Company { get; set; } + + public AddressDto Address { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs new file mode 100644 index 000000000..613ab9735 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs @@ -0,0 +1,69 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenMappingCircularReferences : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenMappingCircularReferences(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAOneToOneRelationship() + { + RunTest(context => + { + var company = new Company + { + Name = "Acme", + HeadOffice = new Address { Line1 = "Acme Park", Postcode = "AC3 3ME" } + }; + + context.Companies.Add(company); + context.SaveChanges(); + + var ceo = new Employee + { + Name = "Mr Ceo", + DateOfBirth = DateTime.Today.AddYears(-21), + Address = new Address { Line1 = "Ceo Towers", Postcode = "CE0 0EC" }, + CompanyId = company.Id, + Company = company + }; + + context.Employees.Add(ceo); + context.SaveChanges(); + + company.CeoId = ceo.Id; + company.Ceo = ceo; + company.HeadOfficeId = company.HeadOffice.AddressId; + + context.SaveChanges(); + + var companyDto = context.Companies.ProjectTo().ShouldHaveSingleItem(); + + companyDto.Id.ShouldBe(company.Id); + companyDto.Name.ShouldBe(company.Name); + + companyDto.HeadOffice.ShouldNotBeNull(); + companyDto.HeadOffice.Line1.ShouldBe("Acme Park"); + companyDto.HeadOffice.Postcode.ShouldBe("AC3 3ME"); + + companyDto.Ceo.ShouldNotBeNull(); + companyDto.Ceo.Name.ShouldBe("Mr Ceo"); + companyDto.Ceo.DateOfBirth.ShouldBe(ceo.DateOfBirth); + companyDto.Ceo.Company.ShouldBeNull(); + + companyDto.Ceo.Address.ShouldNotBeNull(); + companyDto.Ceo.Address.Line1.ShouldBe("Ceo Towers"); + companyDto.Ceo.Address.Postcode.ShouldBe("CE0 0EC"); + }); + } + } +} diff --git a/AgileMapper/Extensions/ExpressionExtensions.cs b/AgileMapper/Extensions/ExpressionExtensions.cs index 64d3d039f..fc77a9230 100644 --- a/AgileMapper/Extensions/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/ExpressionExtensions.cs @@ -203,7 +203,7 @@ private static bool TryGetWrapperMethod( return false; } - method = wrapperType.GetMethod(methodName); + method = wrapperType.GetPublicInstanceMethod(methodName); return true; } @@ -225,7 +225,7 @@ public static Expression WithToReadOnlyCollectionCall(this Expression enumerable if (typeHelper.IsList) { - return Expression.Call(enumerable, typeHelper.ListType.GetMethod("AsReadOnly")); + return Expression.Call(enumerable, typeHelper.ListType.GetPublicInstanceMethod("AsReadOnly")); } if (typeHelper.HasListInterface) diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 45fe8078a..e0fcbc2f0 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -16,7 +16,9 @@ internal class MappingRuleSetCollection { SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true + GuardMemberAccesses = true, + AllowObjectTracking = true, + AllowRecursion = true }, CopySourceEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, @@ -29,7 +31,9 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true + GuardMemberAccesses = true, + AllowObjectTracking = true, + AllowRecursion = true }, MergeEnumerablePopulationStrategy.Instance, PreserveExistingValueMemberPopulationGuardFactory.Instance, @@ -42,7 +46,9 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true + GuardMemberAccesses = true, + AllowObjectTracking = true, + AllowRecursion = true }, OverwriteEnumerablePopulationStrategy.Instance, NullMemberPopulationGuardFactory.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index 5ec75fe61..ca77dd14c 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -15,5 +15,9 @@ internal class MappingRuleSetSettings public bool GuardMemberAccesses { get; set; } public bool AllowEnumerableAssignment { get; set; } + + public bool AllowObjectTracking { get; set; } + + public bool AllowRecursion { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Members/DictionaryTargetMember.cs b/AgileMapper/Members/DictionaryTargetMember.cs index de3c8f523..511ece3b2 100644 --- a/AgileMapper/Members/DictionaryTargetMember.cs +++ b/AgileMapper/Members/DictionaryTargetMember.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.Members using System.Reflection; #endif using Extensions; + using NetStandardPolyfills; using ReadableExpressions.Extensions; internal class DictionaryTargetMember : QualifiedMember @@ -212,7 +213,7 @@ public override BlockExpression GetAccessChecked(IMemberMapperData mapperData) private Expression GetTryGetValueCall(IMemberMapperData mapperData, out ParameterExpression valueVariable) { var dictionaryAccess = GetDictionaryAccess(mapperData); - var tryGetValueMethod = dictionaryAccess.Type.GetMethod("TryGetValue"); + var tryGetValueMethod = dictionaryAccess.Type.GetPublicInstanceMethod("TryGetValue"); var key = GetKey(mapperData); valueVariable = Expression.Variable(ValueType, "existingValue"); diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index b55f66a03..6d82f7e40 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -418,24 +418,24 @@ public static Expression GetAsCall(this Expression subject, params Type[] contex if (subject.Type == typeof(IMappingData)) { - return GetAsCall(subject, typeof(IMappingData).GetMethod("As"), contextTypes); + return GetAsCall(subject, typeof(IMappingData).GetPublicInstanceMethod("As"), contextTypes); } var sourceIsStruct = contextTypes[0].IsValueType(); if (sourceIsStruct) { - return GetAsCall(subject, subject.Type.GetMethod("WithTargetType"), contextTypes[1]); + return GetAsCall(subject, subject.Type.GetPublicInstanceMethod("WithTargetType"), contextTypes[1]); } var targetIsStruct = contextTypes[1].IsValueType(); if (targetIsStruct) { - return GetAsCall(subject, subject.Type.GetMethod("WithSourceType"), contextTypes[0]); + return GetAsCall(subject, subject.Type.GetPublicInstanceMethod("WithSourceType"), contextTypes[0]); } - return GetAsCall(subject, typeof(IObjectMappingDataUntyped).GetMethod("As"), contextTypes); + return GetAsCall(subject, typeof(IObjectMappingDataUntyped).GetPublicInstanceMethod("As"), contextTypes); } private static Expression GetAsCall( @@ -498,12 +498,12 @@ private static Expression GetAccess( return Expression.Property(contextAccess, property); } - private static readonly MethodInfo _getSourceMethod = typeof(IMappingData).GetMethod("GetSource"); + private static readonly MethodInfo _getSourceMethod = typeof(IMappingData).GetPublicInstanceMethod("GetSource"); private static Expression GetSourceAccess(Expression subject, Type sourceType) => GetAccess(subject, _getSourceMethod, sourceType); - private static readonly MethodInfo _getTargetMethod = typeof(IMappingData).GetMethod("GetTarget"); + private static readonly MethodInfo _getTargetMethod = typeof(IMappingData).GetPublicInstanceMethod("GetTarget"); public static Expression GetTargetAccess(Expression subject, Type targetType) => GetAccess(subject, _getTargetMethod, targetType); diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 8c34dd4cb..e3f19ff96 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -3,9 +3,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -#if NET_STANDARD - using System.Reflection; -#endif using Extensions; using Members; using NetStandardPolyfills; @@ -68,6 +65,7 @@ protected override IEnumerable GetShortCircuitReturns(GotoExpression } var alreadyMappedShortCircuit = GetAlreadyMappedObjectShortCircuitOrNull(mapperData); + if (alreadyMappedShortCircuit != null) { yield return alreadyMappedShortCircuit; @@ -101,13 +99,15 @@ private static bool SourceObjectCouldBeNull(IMemberMapperData mapperData) private static Expression GetAlreadyMappedObjectShortCircuitOrNull(ObjectMapperData mapperData) { - if (!mapperData.CacheMappedObjects || mapperData.TargetTypeHasNotYetBeenMapped) + if (!mapperData.RuleSet.Settings.AllowObjectTracking || + !mapperData.CacheMappedObjects || + mapperData.TargetTypeHasNotYetBeenMapped) { return null; } - // ReSharper disable once PossibleNullReferenceException - var tryGetMethod = typeof(IObjectMappingDataUntyped).GetMethod("TryGet") + var tryGetMethod = typeof(IObjectMappingDataUntyped) + .GetPublicInstanceMethod("TryGet") .MakeGenericMethod(mapperData.SourceType, mapperData.TargetType); var tryGetCall = Expression.Call( diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs index a4a90cca0..1c489bcaf 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs @@ -3,10 +3,10 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - using System.Reflection; using Extensions; using Members; using Members.Population; + using NetStandardPolyfills; using static CallbackPosition; internal abstract class PopulationExpressionFactoryBase @@ -97,13 +97,15 @@ protected virtual Expression GetNewObjectCreation( private static Expression GetObjectRegistrationCallOrNull(ObjectMapperData mapperData) { - if (!mapperData.CacheMappedObjects || mapperData.TargetTypeWillNotBeMappedAgain) + if (!mapperData.RuleSet.Settings.AllowObjectTracking || + !mapperData.CacheMappedObjects || + mapperData.TargetTypeWillNotBeMappedAgain) { return null; } var registerMethod = typeof(IObjectMappingDataUntyped) - .GetMethod("Register") + .GetPublicInstanceMethod("Register") .MakeGenericMethod(mapperData.SourceType, mapperData.TargetType); return Expression.Call( diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs index 7cc585a1f..38f9492c7 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs @@ -98,6 +98,7 @@ private static bool MemberPopulationsExist(IEnumerable populationsAn private static Expression AddExistingTargetCheckIfAppropriate(Expression value, IObjectMappingData mappingData) { if ((value.NodeType == ExpressionType.Default) || + mappingData.MapperData.RuleSet.Settings.UseSingleRootMappingExpression || mappingData.MapperData.TargetMemberIsUserStruct() || mappingData.MapperData.TargetIsDefinitelyUnpopulated()) { diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index dcbd7f953..64cf4af44 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -661,8 +661,8 @@ public Expression GetEnumerableConversion(Expression value) private Expression GetTargetMethodCall(string methodName, Expression argument = null) { - var method = TargetTypeHelper.CollectionInterfaceType.GetMethod(methodName) - ?? TargetVariable.Type.GetMethod(methodName); + var method = TargetTypeHelper.CollectionInterfaceType.GetPublicInstanceMethod(methodName) + ?? TargetVariable.Type.GetPublicInstanceMethod(methodName); return (argument != null) ? Expression.Call(TargetVariable, method, argument) diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableSourcePopulationLoopData.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableSourcePopulationLoopData.cs index 63af8b064..3ca5e3f33 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableSourcePopulationLoopData.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableSourcePopulationLoopData.cs @@ -6,11 +6,12 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables using System.Linq.Expressions; using System.Reflection; using Extensions; + using NetStandardPolyfills; internal class EnumerableSourcePopulationLoopData : IPopulationLoopData { - private static readonly MethodInfo _enumeratorMoveNextMethod = typeof(IEnumerator).GetMethod("MoveNext"); - private static readonly MethodInfo _disposeMethod = typeof(IDisposable).GetMethod("Dispose"); + private static readonly MethodInfo _enumeratorMoveNextMethod = typeof(IEnumerator).GetPublicInstanceMethod("MoveNext"); + private static readonly MethodInfo _disposeMethod = typeof(IDisposable).GetPublicInstanceMethod("Dispose"); private readonly Expression _enumerableSubject; private readonly MethodInfo _getEnumeratorMethod; @@ -29,7 +30,7 @@ public EnumerableSourcePopulationLoopData( Builder = builder; _enumerableSubject = enumerableSubject; - _getEnumeratorMethod = typeof(IEnumerable<>).MakeGenericType(elementType).GetMethod("GetEnumerator"); + _getEnumeratorMethod = typeof(IEnumerable<>).MakeGenericType(elementType).GetPublicInstanceMethod("GetEnumerator"); _enumerator = Expression.Variable(_getEnumeratorMethod.ReturnType, "enumerator"); ContinueLoopTarget = Expression.Label(typeof(void), "Continue"); diff --git a/AgileMapper/ObjectPopulation/Enumerables/SourceElementsDictionaryPopulationLoopData.cs b/AgileMapper/ObjectPopulation/Enumerables/SourceElementsDictionaryPopulationLoopData.cs index f924c3dbb..aad2b1e2f 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/SourceElementsDictionaryPopulationLoopData.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/SourceElementsDictionaryPopulationLoopData.cs @@ -3,12 +3,10 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables using System; using System.Collections.Generic; using System.Linq.Expressions; -#if NET_STANDARD - using System.Reflection; -#endif using DataSources; using Extensions; using Members; + using NetStandardPolyfills; internal class SourceElementsDictionaryPopulationLoopData : IPopulationLoopData { @@ -73,7 +71,7 @@ private Expression GetRootLoopExitCheck() private Expression GetContainsRootElementKeyCall() { - var containsKeyMethod = MapperData.SourceObject.Type.GetMethod("ContainsKey"); + var containsKeyMethod = MapperData.SourceObject.Type.GetPublicInstanceMethod("ContainsKey"); var containsKeyCall = Expression.Call(MapperData.SourceObject, containsKeyMethod, _targetElementKey); return containsKeyCall; diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 9f1694de8..511968eb4 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -54,6 +54,11 @@ public static Expression GetChildMapping( if (childMapperData.TargetMemberEverRecurses()) { + if (!childMapperData.RuleSet.Settings.AllowRecursion) + { + return Constants.EmptyExpression; + } + childMapperData.CacheMappedObjects = childMapperData.SourceIsNotFlatObject(); var mapRecursionCall = GetMapRecursionCallFor( @@ -236,7 +241,8 @@ private static bool ShouldUseLocalSourceValueVariable( IMemberMapperData mapperData) { return (sourceValue.NodeType != ExpressionType.Parameter) && - SourceAccessFinder.MultipleAccessesExist(mapperData, mapping); + !mapperData.RuleSet.Settings.UseMemberInitialisation && + SourceAccessFinder.MultipleAccessesExist(mapperData, mapping); } private static string GetSourceValueVariableName(IMemberMapperData mapperData, Type sourceType = null) diff --git a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs index c3fb1b5ad..d3a62de87 100644 --- a/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs +++ b/AgileMapper/ObjectPopulation/ObjectMappingDataFactory.cs @@ -4,9 +4,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Linq; using System.Linq.Expressions; using Enumerables; -#if NET_STANDARD - using System.Reflection; -#endif using Extensions; using Members; using Members.Sources; @@ -96,7 +93,7 @@ public static IObjectMappingData ForChild( var parentParameter = Expression.Parameter(typeof(object), "parent"); var typedForChildMethod = bridgeParameter.Type - .GetMethod("ForChild") + .GetPublicInstanceMethod("ForChild") .MakeGenericMethod(k.SourceType, k.TargetType); var typedForChildCall = Expression.Call( @@ -179,7 +176,7 @@ public static IObjectMappingData ForElement( var parentParameter = Expression.Parameter(typeof(object), "parent"); var typedForElementMethod = bridgeParameter.Type - .GetMethod("ForElement") + .GetPublicInstanceMethod("ForElement") .MakeGenericMethod(k.SourceType, k.TargetType); var typedForElementCall = Expression.Call( @@ -285,7 +282,7 @@ private static IObjectMappingData Create( var parentParameter = Expression.Parameter(typeof(object), "parent"); var createMethod = bridgeParameter.Type - .GetMethod("CreateMappingData") + .GetPublicInstanceMethod("CreateMappingData") .MakeGenericMethod( k.DeclaredSourceType, k.DeclaredTargetType, From 3d737655df128b6bca88832e7024c65311473e70 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 26 Nov 2017 16:45:41 +0000 Subject: [PATCH 080/176] Test coverage for one-to-one circular relationship projection in EF Core 1, Ef5 + EF6 --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenProjectingCircularReferences.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenProjectingCircularReferences.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenProjectingCircularReferences.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- .../WhenMappingCircularReferences.cs | 13 ------------- .../WhenProjectingCircularReferences.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.csproj | 2 +- ...ences.cs => WhenProjectingCircularReferences.cs} | 4 ++-- 11 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs rename AgileMapper.UnitTests.Orms/{WhenMappingCircularReferences.cs => WhenProjectingCircularReferences.cs} (91%) diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index f3ee93b1a..79705cc2d 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -95,6 +95,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs new file mode 100644 index 000000000..6d7ab95e0 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + + public class WhenProjectingCircularReferences : + WhenProjectingCircularReferences + { + public WhenProjectingCircularReferences(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index cf49e10ee..c4295c4fb 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -98,6 +98,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs new file mode 100644 index 000000000..73b142c68 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + + public class WhenProjectingCircularReferences : + WhenProjectingCircularReferences + { + public WhenProjectingCircularReferences(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 93083e673..48134382c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -216,6 +216,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs new file mode 100644 index 000000000..b5f9367a4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + + public class WhenProjectingCircularReferences : + WhenProjectingCircularReferences + { + public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index d91fc49f2..ff42151d0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -153,7 +153,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs deleted file mode 100644 index f4720ad8f..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenMappingCircularReferences.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 -{ - using Infrastructure; - - public class WhenMappingCircularReferences : - WhenMappingCircularReferences - { - public WhenMappingCircularReferences(InMemoryEfCore2TestContext context) - : base(context) - { - } - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs new file mode 100644 index 000000000..5f63927a4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + + public class WhenProjectingCircularReferences : + WhenProjectingCircularReferences + { + public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index f4104ea71..ba549ed4f 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -128,7 +128,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs similarity index 91% rename from AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs rename to AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs index 613ab9735..4f27a2924 100644 --- a/AgileMapper.UnitTests.Orms/WhenMappingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs @@ -6,10 +6,10 @@ using TestClasses; using Xunit; - public abstract class WhenMappingCircularReferences : OrmTestClassBase + public abstract class WhenProjectingCircularReferences : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenMappingCircularReferences(ITestContext context) + protected WhenProjectingCircularReferences(ITestContext context) : base(context) { } From 8b99b59c01b0ef6424d475bad18914f128ac2883 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 26 Nov 2017 17:51:38 +0000 Subject: [PATCH 081/176] Test coverage for one-to-many circular relationship projection - EF Core 1 + 2 tests passing, EF5 + EF6 not --- .../Infrastructure/Ef5TestDbContext.cs | 16 +++++ .../Infrastructure/Ef6TestDbContext.cs | 16 +++++ .../Infrastructure/EfCore1TestDbContext.cs | 11 ++++ .../Infrastructure/EfCore2TestDbContext.cs | 11 ++++ .../AgileMapper.UnitTests.Orms.csproj | 3 + .../Infrastructure/ITestDbContext.cs | 2 + .../Infrastructure/OrmTestClassBase.cs | 1 + .../TestClasses/Category.cs | 24 +++++++ .../TestClasses/CategoryDto.cs | 17 +++++ AgileMapper.UnitTests.Orms/TestExtensions.cs | 12 ++++ .../WhenProjectingCircularReferences.cs | 64 +++++++++++++++++++ AgileMapper.UnitTests/TestExtensions.cs | 30 ++------- 12 files changed, 183 insertions(+), 24 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Category.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestExtensions.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index b42eae189..1f1414243 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -22,6 +22,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet Employees { get; set; } + public DbSet Categories { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -46,6 +48,17 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet StringItems { get; set; } + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasMany(c => c.SubCategories) + .WithOptional(c => c.ParentCategory) + .HasForeignKey(c => c.ParentCategoryId); + + base.OnModelCreating(modelBuilder); + } + #region ITestDbContext Members IDbSetWrapper ITestDbContext.Companies @@ -54,6 +67,9 @@ IDbSetWrapper ITestDbContext.Companies IDbSetWrapper ITestDbContext.Employees => new Ef5DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Categories + => new Ef5DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index f47ef4048..e089036f0 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -22,6 +22,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet Employees { get; set; } + public DbSet Categories { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -46,6 +48,17 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet StringItems { get; set; } + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasMany(c => c.SubCategories) + .WithOptional(c => c.ParentCategory) + .HasForeignKey(c => c.ParentCategoryId); + + base.OnModelCreating(modelBuilder); + } + #region ITestDbContext Members IDbSetWrapper ITestDbContext.Companies @@ -54,6 +67,9 @@ IDbSetWrapper ITestDbContext.Companies IDbSetWrapper ITestDbContext.Employees => new Ef6DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Categories + => new Ef6DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 4d5507060..3d252d58f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -20,6 +20,8 @@ public EfCore1TestDbContext() public DbSet Employees { get; set; } + public DbSet Categories { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -52,6 +54,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithOne(e => e.Company) .HasForeignKey(e => e.CompanyId); + modelBuilder + .Entity() + .HasOne(c => c.ParentCategory) + .WithMany(c => c.SubCategories) + .HasForeignKey(c => c.ParentCategoryId); + base.OnModelCreating(modelBuilder); } @@ -63,6 +71,9 @@ IDbSetWrapper ITestDbContext.Companies IDbSetWrapper ITestDbContext.Employees => new EfCore1DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Categories + => new EfCore1DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 1e0e4b9ad..a4c0573c2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -28,6 +28,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet Employees { get; set; } + public DbSet Categories { get; set; } + public DbSet Products { get; set; } public DbSet Persons { get; set; } @@ -60,6 +62,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithOne(e => e.Company) .HasForeignKey(e => e.CompanyId); + modelBuilder + .Entity() + .HasOne(c => c.ParentCategory) + .WithMany(c => c.SubCategories) + .HasForeignKey(c => c.ParentCategoryId); + base.OnModelCreating(modelBuilder); } @@ -71,6 +79,9 @@ IDbSetWrapper ITestDbContext.Companies IDbSetWrapper ITestDbContext.Employees => new EfCore2DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Categories + => new EfCore2DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(Products); diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index ba549ed4f..f6e38bb3e 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -96,6 +96,8 @@ + + @@ -128,6 +130,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index c669e538c..6496e9fac 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -9,6 +9,8 @@ public interface ITestDbContext : IDisposable IDbSetWrapper Employees { get; } + IDbSetWrapper Categories { get; } + IDbSetWrapper Products { get; } IDbSetWrapper Persons { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 7bae67e08..77ec21977 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -55,6 +55,7 @@ private void EmptyDbContext() { Context.Companies.Clear(); Context.Employees.Clear(); + Context.Categories.Clear(); Context.Products.Clear(); Context.Addresses.Clear(); Context.Persons.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Category.cs b/AgileMapper.UnitTests.Orms/TestClasses/Category.cs new file mode 100644 index 000000000..d2d7c34fb --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Category.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + + public class Category + { + public Category() + { + SubCategories = new List(); + } + + [Key] + public int Id { get; set; } + + public int? ParentCategoryId { get; set; } + + public Category ParentCategory { get; set; } + + public string Name { get; set; } + + public ICollection SubCategories { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs new file mode 100644 index 000000000..c03abdb01 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs @@ -0,0 +1,17 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.Collections.Generic; + + public class CategoryDto + { + public int Id { get; set; } + + public int ParentCategoryId { get; set; } + + public CategoryDto ParentCategory { get; set; } + + public string Name { get; set; } + + public IEnumerable SubCategories { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestExtensions.cs b/AgileMapper.UnitTests.Orms/TestExtensions.cs new file mode 100644 index 000000000..7aa34117b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestExtensions.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Collections.Generic; + using System.Linq; + + internal static class TestExtensions + { + public static T Second(this IEnumerable items) => items.ElementAt(1); + + public static T Third(this IEnumerable items) => items.ElementAt(2); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs index 4f27a2924..437c4eeb4 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { using System; + using System.Linq; using Infrastructure; using Shouldly; using TestClasses; @@ -65,5 +66,68 @@ public void ShouldProjectAOneToOneRelationship() companyDto.Ceo.Address.Postcode.ShouldBe("CE0 0EC"); }); } + + [Fact] + public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + { + RunTest(context => + { + var topLevel = new Category { Name = "Top Level" }; + var child1 = new Category { Name = "Top > One", ParentCategory = topLevel }; + var child2 = new Category { Name = "Top > Two", ParentCategory = topLevel }; + var child3 = new Category { Name = "Top > Three", ParentCategory = topLevel }; + + var grandChild11 = new Category { Name = "Top > One > One", ParentCategory = child1 }; + var grandChild12 = new Category { Name = "Top > One > Two", ParentCategory = child1 }; + + var grandChild21 = new Category { Name = "Top > Two > One", ParentCategory = child2 }; + var grandChild22 = new Category { Name = "Top > Two > Two", ParentCategory = child2 }; + var grandChild23 = new Category { Name = "Top > Two > Three", ParentCategory = child2 }; + + var grandChild31 = new Category { Name = "Top > Three > One", ParentCategory = child3 }; + + var greatGrandchild221 = new Category { Name = "Top > Two > Two > One", ParentCategory = grandChild22 }; + var greatGrandchild222 = new Category { Name = "Top > Two > Two > Two", ParentCategory = grandChild22 }; + + context.Categories.Add(topLevel); + context.Categories.Add(child1); + context.Categories.Add(child2); + context.Categories.Add(child3); + context.Categories.Add(grandChild11); + context.Categories.Add(grandChild12); + context.Categories.Add(grandChild21); + context.Categories.Add(grandChild22); + context.Categories.Add(grandChild23); + context.Categories.Add(grandChild31); + context.Categories.Add(greatGrandchild221); + context.Categories.Add(greatGrandchild222); + + context.SaveChanges(); + + var topLevelDto = context + .Categories + .ProjectTo() + .OrderBy(c => c.Id) + .First(c => c.Name == "Top Level"); + + topLevelDto.Id.ShouldBe(topLevel.Id); + topLevelDto.ParentCategoryId.ShouldBe(default(int)); + topLevelDto.ParentCategory.ShouldBeNull(); + + topLevelDto.SubCategories.Count().ShouldBe(3); + + topLevelDto.SubCategories.First().Id.ShouldBe(child1.Id); + topLevelDto.SubCategories.First().Name.ShouldBe("Top > One"); + topLevelDto.SubCategories.First().ParentCategoryId.ShouldBe(topLevel.Id); + + topLevelDto.SubCategories.Second().Id.ShouldBe(child2.Id); + topLevelDto.SubCategories.Second().Name.ShouldBe("Top > Two"); + topLevelDto.SubCategories.Second().ParentCategoryId.ShouldBe(topLevel.Id); + + topLevelDto.SubCategories.Third().Id.ShouldBe(child3.Id); + topLevelDto.SubCategories.Third().Name.ShouldBe("Top > Three"); + topLevelDto.SubCategories.Third().ParentCategoryId.ShouldBe(topLevel.Id); + }); + } } } diff --git a/AgileMapper.UnitTests/TestExtensions.cs b/AgileMapper.UnitTests/TestExtensions.cs index 1cf1d5385..fd48b68c2 100644 --- a/AgileMapper.UnitTests/TestExtensions.cs +++ b/AgileMapper.UnitTests/TestExtensions.cs @@ -14,35 +14,17 @@ public static string ToCurrentCultureString(this DateTime? dateTime) public static string ToCurrentCultureString(this DateTime dateTime) => dateTime.ToString(CultureInfo.CurrentCulture); - public static T Second(this IEnumerable items) - { - return items.ElementAt(1); - } + public static T Second(this IEnumerable items) => items.ElementAt(1); - public static T Third(this IEnumerable items) - { - return items.ElementAt(2); - } + public static T Third(this IEnumerable items) => items.ElementAt(2); - public static void ShouldBeDefault(this T value) - { - value.ShouldBe(default(T)); - } + public static void ShouldBeDefault(this T value) => value.ShouldBe(default(T)); - public static void ShouldNotBeDefault(this T value) - { - value.ShouldNotBe(default(T)); - } + public static void ShouldNotBeDefault(this T value) => value.ShouldNotBe(default(T)); - public static void ShouldBeTrue(this bool? value) - { - value.GetValueOrDefault().ShouldBeTrue(); - } + public static void ShouldBeTrue(this bool? value) => value.GetValueOrDefault().ShouldBeTrue(); - public static void ShouldBeFalse(this bool? value) - { - value.GetValueOrDefault().ShouldBeFalse(); - } + public static void ShouldBeFalse(this bool? value) => value.GetValueOrDefault().ShouldBeFalse(); public static void ShouldBe(this TActual? value, TExpected expectedValue) where TActual : struct From a7577f9019922670d4d3e63f6a1479090309ea2e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 28 Nov 2017 18:13:17 +0000 Subject: [PATCH 082/176] Using GetValueOrDefault and skipping null check when mapping from nullable T to T --- AgileMapper.UnitTests/WhenViewingMappingPlans.cs | 1 + AgileMapper/Extensions/ExpressionExtensions.cs | 14 +++++++++++++- AgileMapper/Members/ExpressionInfoFinder.cs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs index 5c93a99da..ed73f4a79 100644 --- a/AgileMapper.UnitTests/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests/WhenViewingMappingPlans.cs @@ -265,6 +265,7 @@ public void ShouldNotRangeCheckNullableToNonNullableValues() string plan = Mapper.GetPlanFor>().ToANew>(); plan.ShouldNotContain("int.MinValue"); + plan.ShouldNotContain("Value.HasValue"); } [Fact] diff --git a/AgileMapper/Extensions/ExpressionExtensions.cs b/AgileMapper/Extensions/ExpressionExtensions.cs index fc77a9230..c45aed3cc 100644 --- a/AgileMapper/Extensions/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/ExpressionExtensions.cs @@ -164,7 +164,19 @@ public static Expression GetConversionTo(this Expression expression) [DebuggerStepThrough] public static Expression GetConversionTo(this Expression expression, Type targetType) - => (expression.Type != targetType) ? Expression.Convert(expression, targetType) : expression; + { + if (expression.Type == targetType) + { + return expression; + } + + if (expression.Type.GetNonNullableType() == targetType) + { + return expression.GetValueOrDefaultCall(); + } + + return Expression.Convert(expression, targetType); + } public static Expression WithToArrayCall(this Expression enumerable, Type elementType) { diff --git a/AgileMapper/Members/ExpressionInfoFinder.cs b/AgileMapper/Members/ExpressionInfoFinder.cs index 17e9f3e38..b01c3724c 100644 --- a/AgileMapper/Members/ExpressionInfoFinder.cs +++ b/AgileMapper/Members/ExpressionInfoFinder.cs @@ -149,6 +149,11 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) if ((methodCall.Object != _mappingDataObject) && (methodCall.Method.DeclaringType != typeof(IMappingData))) { + if (IsNullableGetValueOrDefaultCall(methodCall)) + { + AddExistingNullCheck(methodCall.Object); + } + AddStringMemberAccessSubjectIfAppropriate(methodCall.Object); AddInvocationIfNecessary(methodCall); AddMemberAccessIfAppropriate(methodCall); @@ -157,6 +162,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return base.VisitMethodCall(methodCall); } + private static bool IsNullableGetValueOrDefaultCall(MethodCallExpression methodCall) + { + return (methodCall.Object != null) && + (methodCall.Method.Name == "GetValueOrDefault") && + (methodCall.Object.Type.IsNullableType()); + } + private void AddExistingNullCheck(Expression checkedAccess) { _nullCheckSubjects.Add(checkedAccess.ToString()); From d7aa92b79bb1ed218978655f41aae433de0c07bd Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 28 Nov 2017 18:33:13 +0000 Subject: [PATCH 083/176] Populating short-circuited recursive members with empty collections - working in EF Core, not in EF 5 + 6 --- .../WhenProjectingToEnumerableMembers.cs | 5 +-- .../WhenProjectingCircularReferences.cs | 34 ++++++++++++------- .../WhenProjectingFlatTypes.cs | 6 +++- .../WhenProjectingToComplexTypeMembers.cs | 5 ++- .../Configuration/UserConfigurationSet.cs | 2 +- .../Members/MemberMapperDataExtensions.cs | 8 +++-- .../EnumerablePopulationBuilder.cs | 2 +- .../Enumerables/EnumerableTypeHelper.cs | 6 ++++ .../ObjectPopulation/MappingFactory.cs | 23 ++++++++++++- AgileMapper/Queryables/EmptyArrayConverter.cs | 31 +++++++++++++++++ .../Queryables/GetValueOrDefaultConverter.cs | 33 ++++++++++++++++++ .../Queryables/QueryProjectionModifier.cs | 15 ++++++++ .../Settings/DefaultQueryProviderSettings.cs | 11 +++++- .../Settings/Ef5QueryProviderSettings.cs | 8 +++++ .../Settings/Ef6QueryProviderSettings.cs | 4 ++- .../Settings/EfCore2QueryProviderSettings.cs | 2 -- .../Settings/IQueryProviderSettings.cs | 8 +++++ 17 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 AgileMapper/Queryables/EmptyArrayConverter.cs create mode 100644 AgileMapper/Queryables/GetValueOrDefaultConverter.cs diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index fb9b7ed26..c4d0d075e 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -73,10 +73,11 @@ protected void ProjectToComplexTypeCollectionMember(TOrmContext context) rotaDto.Entries.Count.ShouldBe(rota.Entries.Count()); var i = 0; + var rotaEntryDtos = rotaDto.Entries.OrderBy(re => re.Id).ToArray(); - foreach (var rotaEntry in rota.Entries) + foreach (var rotaEntry in rota.Entries.OrderBy(re => re.Id)) { - var rotaEntryDto = rotaDto.Entries.ElementAt(i); + var rotaEntryDto = rotaEntryDtos.ElementAt(i); rotaEntryDto.Id.ShouldBe(rotaEntry.Id); rotaEntryDto.DayOfWeek.ShouldBe(rotaEntry.DayOfWeek); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs index 437c4eeb4..9cafa314b 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs @@ -114,19 +114,27 @@ public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() topLevelDto.ParentCategoryId.ShouldBe(default(int)); topLevelDto.ParentCategory.ShouldBeNull(); - topLevelDto.SubCategories.Count().ShouldBe(3); - - topLevelDto.SubCategories.First().Id.ShouldBe(child1.Id); - topLevelDto.SubCategories.First().Name.ShouldBe("Top > One"); - topLevelDto.SubCategories.First().ParentCategoryId.ShouldBe(topLevel.Id); - - topLevelDto.SubCategories.Second().Id.ShouldBe(child2.Id); - topLevelDto.SubCategories.Second().Name.ShouldBe("Top > Two"); - topLevelDto.SubCategories.Second().ParentCategoryId.ShouldBe(topLevel.Id); - - topLevelDto.SubCategories.Third().Id.ShouldBe(child3.Id); - topLevelDto.SubCategories.Third().Name.ShouldBe("Top > Three"); - topLevelDto.SubCategories.Third().ParentCategoryId.ShouldBe(topLevel.Id); + var topLevelSubCategoryDtos = topLevelDto + .SubCategories + .OrderBy(sc => sc.Id) + .ToArray(); + + topLevelSubCategoryDtos.Length.ShouldBe(3); + + topLevelSubCategoryDtos.First().Id.ShouldBe(child1.Id); + topLevelSubCategoryDtos.First().Name.ShouldBe("Top > One"); + topLevelSubCategoryDtos.First().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.First().SubCategories.ShouldBeEmpty(); + + topLevelSubCategoryDtos.Second().Id.ShouldBe(child2.Id); + topLevelSubCategoryDtos.Second().Name.ShouldBe("Top > Two"); + topLevelSubCategoryDtos.Second().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.Second().SubCategories.ShouldBeEmpty(); + + topLevelSubCategoryDtos.Third().Id.ShouldBe(child3.Id); + topLevelSubCategoryDtos.Third().Name.ShouldBe("Top > Three"); + topLevelSubCategoryDtos.Third().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.Third().SubCategories.ShouldBeEmpty(); }); } } diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 15d4bdc2f..712b11b21 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -26,7 +26,11 @@ public void ShouldProjectAFlatTypeToAnArray() context.Products.Add(product2); context.SaveChanges(); - var productDtos = context.Products.ProjectTo().ToArray(); + var productDtos = context + .Products + .ProjectTo() + .OrderBy(p => p.ProductId) + .ToArray(); productDtos.Length.ShouldBe(2); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 75caeb308..0584b9c03 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -22,7 +22,6 @@ public void ShouldProjectToAComplexTypeMember() var person = new Person { Name = "Test Db", - AddressId = 1, Address = new Address { Line1 = "Test Db Line 1", @@ -35,9 +34,9 @@ public void ShouldProjectToAComplexTypeMember() var personDto = context.Persons.ProjectTo().First(); - personDto.Id.ShouldBe(1); + personDto.Id.ShouldBe(person.PersonId); personDto.Name.ShouldBe("Test Db"); - personDto.AddressId.ShouldBe(1); + personDto.AddressId.ShouldBe(person.Address.AddressId); personDto.AddressLine1.ShouldBe("Test Db Line 1"); personDto.AddressLine2.ShouldBe("Test Db Line 2"); }); diff --git a/AgileMapper/Configuration/UserConfigurationSet.cs b/AgileMapper/Configuration/UserConfigurationSet.cs index 2c6a44a07..059bfc254 100644 --- a/AgileMapper/Configuration/UserConfigurationSet.cs +++ b/AgileMapper/Configuration/UserConfigurationSet.cs @@ -33,7 +33,7 @@ public UserConfigurationSet(MapperContext mapperContext) public bool ValidateMappingPlans { get; set; } - #region Mapped Object Caching Settings + #region MappedObjectCachingSettings private List MappedObjectCachingSettings => _mappedObjectCachingSettings ?? (_mappedObjectCachingSettings = new List()); diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 6d82f7e40..29ea2ad38 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -22,6 +22,9 @@ public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) public static bool UseMemberInitialisation(this IMemberMapperData mapperData) => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.Context.IsPartOfUserStructMapping(); + public static bool MapToNullCollections(this IMemberMapperData mapperData) + => mapperData.MapperContext.UserConfigurations.MapToNullCollections(mapperData); + public static IMemberMapperData GetRootMapperData(this IMemberMapperData mapperData) { while (!mapperData.IsRoot) @@ -225,7 +228,6 @@ private static bool TargetMemberIsRecursionWithin( public static Expression GetFallbackCollectionValue(this IMemberMapperData mapperData) { var targetMember = mapperData.TargetMember; - var mapToNullCollections = mapperData.MapperContext.UserConfigurations.MapToNullCollections(mapperData); Expression emptyEnumerable; @@ -233,7 +235,7 @@ public static Expression GetFallbackCollectionValue(this IMemberMapperData mappe { var existingValue = mapperData.GetTargetMemberAccess(); - if (mapToNullCollections) + if (mapperData.MapToNullCollections()) { return existingValue; } @@ -243,7 +245,7 @@ public static Expression GetFallbackCollectionValue(this IMemberMapperData mappe return Expression.Coalesce(existingValue, emptyEnumerable); } - if (mapToNullCollections) + if (mapperData.MapToNullCollections()) { return targetMember.Type.ToDefaultExpression(); } diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 64cf4af44..23cfbb38c 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -59,7 +59,7 @@ public EnumerablePopulationBuilder(ObjectMapperData mapperData) Context = new EnumerablePopulationContext(mapperData); _sourceItemsSelector = new SourceItemsSelector(this); _sourceElementParameter = Context.SourceElementType.GetOrCreateParameter(); - TargetTypeHelper = new EnumerableTypeHelper(mapperData.TargetType, mapperData.TargetMember.ElementType); + TargetTypeHelper = new EnumerableTypeHelper(mapperData.TargetMember); _sourceAdapter = SourceEnumerableAdapterFactory.GetAdapterFor(this); _populationExpressions = new List(); diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 424041301..cda5cae6d 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -8,6 +8,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables using System.Reflection; #endif using Extensions; + using Members; using NetStandardPolyfills; internal class EnumerableTypeHelper @@ -20,6 +21,11 @@ internal class EnumerableTypeHelper private Type _collectionInterfaceType; private Type _enumerableInterfaceType; + public EnumerableTypeHelper(QualifiedMember member) + : this(member.Type, member.ElementType) + { + } + public EnumerableTypeHelper(Type enumerableType, Type elementType) { EnumerableType = enumerableType; diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 511968eb4..840ed2c16 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -1,9 +1,12 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System; + using System.Linq; using System.Linq.Expressions; + using Enumerables; using Extensions; using Members; + using NetStandardPolyfills; internal static class MappingFactory { @@ -56,7 +59,7 @@ public static Expression GetChildMapping( { if (!childMapperData.RuleSet.Settings.AllowRecursion) { - return Constants.EmptyExpression; + return GetRecursionShortCircuit(childMapperData); } childMapperData.CacheMappedObjects = childMapperData.SourceIsNotFlatObject(); @@ -78,6 +81,24 @@ public static Expression GetChildMapping( return inlineMappingBlock; } + private static Expression GetRecursionShortCircuit(IBasicMapperData childMapperData) + { + if (childMapperData.TargetMember.IsComplex) + { + return Constants.EmptyExpression; + } + + var emptyArray = Expression.NewArrayBounds( + childMapperData.TargetMember.ElementType, + 0.ToConstantExpression()); + + var helper = new EnumerableTypeHelper(childMapperData.TargetMember); + + return helper.GetEnumerableConversion( + emptyArray, + childMapperData.RuleSet.Settings.AllowEnumerableAssignment); + } + private static Expression GetMapRecursionCallFor( IObjectMappingData childMappingData, Expression sourceValue, diff --git a/AgileMapper/Queryables/EmptyArrayConverter.cs b/AgileMapper/Queryables/EmptyArrayConverter.cs new file mode 100644 index 000000000..5798c2008 --- /dev/null +++ b/AgileMapper/Queryables/EmptyArrayConverter.cs @@ -0,0 +1,31 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq.Expressions; + using Extensions; + using Settings; + + internal static class EmptyArrayConverter + { + public static bool TryConvert( + NewArrayExpression newArray, + IQueryProviderSettings settings, + out Expression converted) + { + if (settings.SupportsEmptyArrayCreation || IsNotEmptyArrayCreation(newArray)) + { + converted = null; + return false; + } + + converted = settings.ConvertEmptyArrayCreation(newArray); + return true; + } + + private static bool IsNotEmptyArrayCreation(NewArrayExpression newArray) + { + return !newArray.Expressions.HasOne() || + (newArray.Expressions[0].NodeType != ExpressionType.Constant) || + (((ConstantExpression)newArray.Expressions[0]).Value != (object)0); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/GetValueOrDefaultConverter.cs new file mode 100644 index 000000000..b5d5467a5 --- /dev/null +++ b/AgileMapper/Queryables/GetValueOrDefaultConverter.cs @@ -0,0 +1,33 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq.Expressions; + using Extensions; + using ReadableExpressions.Extensions; + using Settings; + + internal static class GetValueOrDefaultConverter + { + public static bool TryConvert( + MethodCallExpression methodCall, + IQueryProviderSettings settings, + out Expression converted) + { + if (settings.SupportsGetValueOrDefault || IsNotGetValueOrDefaultCall(methodCall)) + { + converted = null; + return false; + } + + converted = settings.ConvertGetValueOrDefaultCall(methodCall); + return true; + } + + private static bool IsNotGetValueOrDefaultCall(MethodCallExpression methodCall) + { + return methodCall.Arguments.Any() || + methodCall.Method.IsStatic || + !methodCall.Object.Type.IsNullableType() || + (methodCall.Method.Name != "GetValueOrDefault"); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index a5db846b5..07ec4f754 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -50,6 +50,11 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return converted; } + if (GetValueOrDefaultConverter.TryConvert(methodCall, _settings, out converted)) + { + return converted; + } + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _settings, out converted)) { return converted; @@ -60,5 +65,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) protected override Expression VisitDefault(DefaultExpression defaultExpression) => DefaultExpressionConverter.Convert(defaultExpression); + + protected override Expression VisitNewArray(NewArrayExpression newArray) + { + if (EmptyArrayConverter.TryConvert(newArray, _settings, out var converted)) + { + return converted; + } + + return base.VisitNewArray(newArray); + } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 42a4382f3..952f824f0 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -28,11 +28,18 @@ public DefaultQueryProviderSettings() #endif public virtual bool SupportsStringEqualsIgnoreCase => false; - public virtual bool SupportsToString => false; + public virtual bool SupportsToString => true; + + public virtual bool SupportsGetValueOrDefault => true; + + public virtual bool SupportsEmptyArrayCreation => true; public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); + public Expression ConvertGetValueOrDefaultCall(MethodCallExpression call) + => Expression.Convert(call.Object, call.Type); + public Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) { if (call.Method.DeclaringType == typeof(Guid)) @@ -62,6 +69,8 @@ public Expression ConvertTryParseCall(MethodCallExpression call, Expression fall return conversion; } + public virtual Expression ConvertEmptyArrayCreation(NewArrayExpression newEmptyArray) => newEmptyArray; + protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => null; diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 26fa03b0f..66baf6b73 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -9,6 +9,14 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings { +#if NET_STANDARD + public override bool SupportsToString => false; +#endif + + public override bool SupportsGetValueOrDefault => false; + + public override bool SupportsEmptyArrayCreation => false; + #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.EntityFunctions"); diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 726e525a8..d3262d18f 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -7,7 +7,9 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { - public override bool SupportsToString => true; + public override bool SupportsGetValueOrDefault => false; + + public override bool SupportsEmptyArrayCreation => false; #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() diff --git a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs index ac8a2591c..0fad1bc02 100644 --- a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs @@ -3,7 +3,5 @@ internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsStringEqualsIgnoreCase => true; - - public override bool SupportsToString => true; } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index ed46fb6e4..416e2ade8 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -16,8 +16,16 @@ internal interface IQueryProviderSettings bool SupportsToString { get; } + bool SupportsGetValueOrDefault { get; } + + bool SupportsEmptyArrayCreation { get; } + Expression ConvertToStringCall(MethodCallExpression call); + Expression ConvertGetValueOrDefaultCall(MethodCallExpression call); + Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); + + Expression ConvertEmptyArrayCreation(NewArrayExpression newEmptyArray); } } \ No newline at end of file From 4cb2d5e2dced45acdd4c4cc6555fe92bc87983d4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 28 Nov 2017 18:35:25 +0000 Subject: [PATCH 084/176] Organising query projection converters --- .../Queryables/{ => Converters}/DefaultExpressionConverter.cs | 2 +- AgileMapper/Queryables/{ => Converters}/EmptyArrayConverter.cs | 2 +- .../Queryables/{ => Converters}/GetValueOrDefaultConverter.cs | 2 +- .../{ => Converters}/StringEqualsIgnoreCaseConverter.cs | 2 +- AgileMapper/Queryables/{ => Converters}/ToStringConverter.cs | 2 +- .../Queryables/{ => Converters}/TryParseAssignmentConverter.cs | 2 +- AgileMapper/Queryables/QueryProjectionModifier.cs | 1 + AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs | 1 + .../Queryables/Settings/QueryProviderSettingsExtensions.cs | 2 +- 9 files changed, 9 insertions(+), 7 deletions(-) rename AgileMapper/Queryables/{ => Converters}/DefaultExpressionConverter.cs (95%) rename AgileMapper/Queryables/{ => Converters}/EmptyArrayConverter.cs (93%) rename AgileMapper/Queryables/{ => Converters}/GetValueOrDefaultConverter.cs (94%) rename AgileMapper/Queryables/{ => Converters}/StringEqualsIgnoreCaseConverter.cs (95%) rename AgileMapper/Queryables/{ => Converters}/ToStringConverter.cs (93%) rename AgileMapper/Queryables/{ => Converters}/TryParseAssignmentConverter.cs (96%) diff --git a/AgileMapper/Queryables/DefaultExpressionConverter.cs b/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs similarity index 95% rename from AgileMapper/Queryables/DefaultExpressionConverter.cs rename to AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs index 4ba6aaf81..786161251 100644 --- a/AgileMapper/Queryables/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System; using System.Linq.Expressions; diff --git a/AgileMapper/Queryables/EmptyArrayConverter.cs b/AgileMapper/Queryables/Converters/EmptyArrayConverter.cs similarity index 93% rename from AgileMapper/Queryables/EmptyArrayConverter.cs rename to AgileMapper/Queryables/Converters/EmptyArrayConverter.cs index 5798c2008..3332219f0 100644 --- a/AgileMapper/Queryables/EmptyArrayConverter.cs +++ b/AgileMapper/Queryables/Converters/EmptyArrayConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using Extensions; diff --git a/AgileMapper/Queryables/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs similarity index 94% rename from AgileMapper/Queryables/GetValueOrDefaultConverter.cs rename to AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index b5d5467a5..c0032a9e6 100644 --- a/AgileMapper/Queryables/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using Extensions; diff --git a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs similarity index 95% rename from AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs rename to AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs index 6bdd75fc8..91f517f20 100644 --- a/AgileMapper/Queryables/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using NetStandardPolyfills; diff --git a/AgileMapper/Queryables/ToStringConverter.cs b/AgileMapper/Queryables/Converters/ToStringConverter.cs similarity index 93% rename from AgileMapper/Queryables/ToStringConverter.cs rename to AgileMapper/Queryables/Converters/ToStringConverter.cs index 08004ef8a..6b5d1b312 100644 --- a/AgileMapper/Queryables/ToStringConverter.cs +++ b/AgileMapper/Queryables/Converters/ToStringConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using Extensions; diff --git a/AgileMapper/Queryables/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs similarity index 96% rename from AgileMapper/Queryables/TryParseAssignmentConverter.cs rename to AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs index c13081142..4b29a939b 100644 --- a/AgileMapper/Queryables/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using Extensions; diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 07ec4f754..ca0709eb6 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables { using System.Linq.Expressions; + using Converters; using Extensions; using Settings; diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 952f824f0..8647fe808 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Linq.Expressions; + using Converters; using Extensions; using NetStandardPolyfills; diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index add7907b0..11f4542d6 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -1,10 +1,10 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { #if !NET_STANDARD - using System; using System.Linq.Expressions; using System.Linq; using System.Reflection; + using Converters; using Extensions; using NetStandardPolyfills; #endif From 5b686b0b8970efe16ceef7131c0775acdeb231f4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 29 Nov 2017 18:19:31 +0000 Subject: [PATCH 085/176] Moving recursion management into a rule set strategy / Fixing EF5 local DB int -> ToString projection --- .../Configuration/MappingConfigInfo.cs | 2 +- AgileMapper/MappingRuleSet.cs | 5 ++ AgileMapper/MappingRuleSetCollection.cs | 22 +++---- AgileMapper/MappingRuleSetSettings.cs | 2 - ...istingValueMemberPopulationGuardFactory.cs | 2 - .../CopySourceEnumerablePopulationStrategy.cs | 2 - .../MergeEnumerablePopulationStrategy.cs | 2 - ...ojectSourceEnumerablePopulationStrategy.cs | 2 - AgileMapper/ObjectPopulation/IObjectMapper.cs | 1 + .../ObjectPopulation/MappingFactory.cs | 59 +++---------------- AgileMapper/ObjectPopulation/ObjectMapper.cs | 1 + .../{ => Recursion}/IRecursionMapperFunc.cs | 2 +- .../IRecursiveMemberMappingStrategy.cs | 13 ++++ ...rsionCallRecursiveMemberMappingStrategy.cs | 30 ++++++++++ ...apToDepthRecursiveMemberMappingStrategy.cs | 33 +++++++++++ .../{ => Recursion}/RecursionMapperFunc.cs | 2 +- .../RecursionMapperMappingPlanFunction.cs | 2 +- .../Settings/Ef5QueryProviderSettings.cs | 2 - 18 files changed, 107 insertions(+), 77 deletions(-) rename AgileMapper/ObjectPopulation/{ => Recursion}/IRecursionMapperFunc.cs (72%) create mode 100644 AgileMapper/ObjectPopulation/Recursion/IRecursiveMemberMappingStrategy.cs create mode 100644 AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs create mode 100644 AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs rename AgileMapper/ObjectPopulation/{ => Recursion}/RecursionMapperFunc.cs (93%) diff --git a/AgileMapper/Configuration/MappingConfigInfo.cs b/AgileMapper/Configuration/MappingConfigInfo.cs index 2ccd07ec3..8cc753908 100644 --- a/AgileMapper/Configuration/MappingConfigInfo.cs +++ b/AgileMapper/Configuration/MappingConfigInfo.cs @@ -15,7 +15,7 @@ internal class MappingConfigInfo { private static readonly Type _allSourceTypes = typeof(MappingConfigInfo); - private static readonly MappingRuleSet _allRuleSets = new MappingRuleSet("*", null, null, null, null); + private static readonly MappingRuleSet _allRuleSets = new MappingRuleSet("*", null, null, null, null, null); private ConfiguredLambdaInfo _conditionLambda; private bool _negateCondition; diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index df2b2cef8..de03e7d11 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -3,6 +3,7 @@ namespace AgileObjects.AgileMapper using DataSources; using Members.Population; using ObjectPopulation.Enumerables; + using ObjectPopulation.Recursion; internal class MappingRuleSet { @@ -10,12 +11,14 @@ public MappingRuleSet( string name, MappingRuleSetSettings settings, IEnumerablePopulationStrategy enumerablePopulationStrategy, + IRecursiveMemberMappingStrategy recursiveMemberMappingStrategy, IMemberPopulationGuardFactory populationGuardFactory, IDataSourceFactory fallbackDataSourceFactory) { Name = name; Settings = settings; EnumerablePopulationStrategy = enumerablePopulationStrategy; + RecursiveMemberMappingStrategy = recursiveMemberMappingStrategy; PopulationGuardFactory = populationGuardFactory; FallbackDataSourceFactory = fallbackDataSourceFactory; } @@ -26,6 +29,8 @@ public MappingRuleSet( public IEnumerablePopulationStrategy EnumerablePopulationStrategy { get; } + public IRecursiveMemberMappingStrategy RecursiveMemberMappingStrategy { get; } + public IMemberPopulationGuardFactory PopulationGuardFactory { get; } public IDataSourceFactory FallbackDataSourceFactory { get; } diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index e0fcbc2f0..1b36215ff 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -5,6 +5,7 @@ namespace AgileObjects.AgileMapper using Members.Population; using ObjectPopulation; using ObjectPopulation.Enumerables; + using ObjectPopulation.Recursion; internal class MappingRuleSetCollection { @@ -17,10 +18,10 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = true, - AllowObjectTracking = true, - AllowRecursion = true + AllowObjectTracking = true }, - CopySourceEnumerablePopulationStrategy.Instance, + new CopySourceEnumerablePopulationStrategy(), + MapRecursionCallRecursiveMemberMappingStrategy.Instance, NullMemberPopulationGuardFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); @@ -32,11 +33,11 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = true, - AllowObjectTracking = true, - AllowRecursion = true + AllowObjectTracking = true }, - MergeEnumerablePopulationStrategy.Instance, - PreserveExistingValueMemberPopulationGuardFactory.Instance, + new MergeEnumerablePopulationStrategy(), + MapRecursionCallRecursiveMemberMappingStrategy.Instance, + new PreserveExistingValueMemberPopulationGuardFactory(), ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _overwrite = new MappingRuleSet( @@ -47,10 +48,10 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = true, - AllowObjectTracking = true, - AllowRecursion = true + AllowObjectTracking = true }, OverwriteEnumerablePopulationStrategy.Instance, + MapRecursionCallRecursiveMemberMappingStrategy.Instance, NullMemberPopulationGuardFactory.Instance, DefaultValueDataSourceFactory.Instance); @@ -62,7 +63,8 @@ internal class MappingRuleSetCollection UseSingleRootMappingExpression = true, AllowEnumerableAssignment = true }, - ProjectSourceEnumerablePopulationStrategy.Instance, + new ProjectSourceEnumerablePopulationStrategy(), + new MapToDepthRecursiveMemberMappingStrategy(), NullMemberPopulationGuardFactory.Instance, DefaultValueDataSourceFactory.Instance); diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index ca77dd14c..482177564 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -17,7 +17,5 @@ internal class MappingRuleSetSettings public bool AllowEnumerableAssignment { get; set; } public bool AllowObjectTracking { get; set; } - - public bool AllowRecursion { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs b/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs index dc544e92f..fb1e9dc31 100644 --- a/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs +++ b/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs @@ -5,8 +5,6 @@ namespace AgileObjects.AgileMapper.Members.Population internal class PreserveExistingValueMemberPopulationGuardFactory : IMemberPopulationGuardFactory { - public static readonly IMemberPopulationGuardFactory Instance = new PreserveExistingValueMemberPopulationGuardFactory(); - public Expression GetPopulationGuardOrNull(IMemberMapperData mapperData) { if (SkipPopulateCondition(mapperData)) diff --git a/AgileMapper/ObjectPopulation/Enumerables/CopySourceEnumerablePopulationStrategy.cs b/AgileMapper/ObjectPopulation/Enumerables/CopySourceEnumerablePopulationStrategy.cs index a0f0a849c..ec5ae7172 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/CopySourceEnumerablePopulationStrategy.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/CopySourceEnumerablePopulationStrategy.cs @@ -4,8 +4,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables internal class CopySourceEnumerablePopulationStrategy : EnumerablePopulationStrategyBase { - public static readonly IEnumerablePopulationStrategy Instance = new CopySourceEnumerablePopulationStrategy(); - protected override Expression GetEnumerablePopulation( EnumerablePopulationBuilder builder, IObjectMappingData mappingData) diff --git a/AgileMapper/ObjectPopulation/Enumerables/MergeEnumerablePopulationStrategy.cs b/AgileMapper/ObjectPopulation/Enumerables/MergeEnumerablePopulationStrategy.cs index 9de18430f..5af7a48c5 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/MergeEnumerablePopulationStrategy.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/MergeEnumerablePopulationStrategy.cs @@ -4,8 +4,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables internal class MergeEnumerablePopulationStrategy : EnumerablePopulationStrategyBase { - public static readonly IEnumerablePopulationStrategy Instance = new MergeEnumerablePopulationStrategy(); - protected override Expression GetEnumerablePopulation( EnumerablePopulationBuilder builder, IObjectMappingData mappingData) diff --git a/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs index 4d9b21e65..ebc021d83 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/ProjectSourceEnumerablePopulationStrategy.cs @@ -4,8 +4,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Enumerables internal class ProjectSourceEnumerablePopulationStrategy : EnumerablePopulationStrategyBase { - public static readonly IEnumerablePopulationStrategy Instance = new ProjectSourceEnumerablePopulationStrategy(); - protected override Expression GetEnumerablePopulation( EnumerablePopulationBuilder builder, IObjectMappingData mappingData) diff --git a/AgileMapper/ObjectPopulation/IObjectMapper.cs b/AgileMapper/ObjectPopulation/IObjectMapper.cs index 1ef3d9548..4d36f4d51 100644 --- a/AgileMapper/ObjectPopulation/IObjectMapper.cs +++ b/AgileMapper/ObjectPopulation/IObjectMapper.cs @@ -2,6 +2,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System.Collections.Generic; using System.Linq.Expressions; + using Recursion; internal interface IObjectMapper : IObjectMapperFunc { diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 840ed2c16..c67417a06 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -1,12 +1,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System; - using System.Linq; using System.Linq.Expressions; - using Enumerables; using Extensions; using Members; - using NetStandardPolyfills; internal static class MappingFactory { @@ -57,18 +54,14 @@ public static Expression GetChildMapping( if (childMapperData.TargetMemberEverRecurses()) { - if (!childMapperData.RuleSet.Settings.AllowRecursion) - { - return GetRecursionShortCircuit(childMapperData); - } - - childMapperData.CacheMappedObjects = childMapperData.SourceIsNotFlatObject(); - - var mapRecursionCall = GetMapRecursionCallFor( - childMappingData, - mappingValues.SourceValue, - dataSourceIndex, - declaredTypeMapperData); + var mapRecursionCall = childMapperData + .RuleSet + .RecursiveMemberMappingStrategy + .GetMapRecursionCallFor( + childMappingData, + mappingValues.SourceValue, + dataSourceIndex, + declaredTypeMapperData); return mapRecursionCall; } @@ -81,42 +74,6 @@ public static Expression GetChildMapping( return inlineMappingBlock; } - private static Expression GetRecursionShortCircuit(IBasicMapperData childMapperData) - { - if (childMapperData.TargetMember.IsComplex) - { - return Constants.EmptyExpression; - } - - var emptyArray = Expression.NewArrayBounds( - childMapperData.TargetMember.ElementType, - 0.ToConstantExpression()); - - var helper = new EnumerableTypeHelper(childMapperData.TargetMember); - - return helper.GetEnumerableConversion( - emptyArray, - childMapperData.RuleSet.Settings.AllowEnumerableAssignment); - } - - private static Expression GetMapRecursionCallFor( - IObjectMappingData childMappingData, - Expression sourceValue, - int dataSourceIndex, - ObjectMapperData declaredTypeMapperData) - { - var childMapperData = childMappingData.MapperData; - - childMapperData.RegisterRequiredMapperFunc(childMappingData); - - var mapRecursionCall = declaredTypeMapperData.GetMapRecursionCall( - sourceValue, - childMapperData.TargetMember, - dataSourceIndex); - - return mapRecursionCall; - } - public static Expression GetElementMapping( Expression sourceElementValue, Expression targetElementValue, diff --git a/AgileMapper/ObjectPopulation/ObjectMapper.cs b/AgileMapper/ObjectPopulation/ObjectMapper.cs index a59d0eabf..d52caa6c8 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapper.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapper.cs @@ -7,6 +7,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using System.Reflection; #endif using Caching; + using Recursion; internal class ObjectMapper : IObjectMapper { diff --git a/AgileMapper/ObjectPopulation/IRecursionMapperFunc.cs b/AgileMapper/ObjectPopulation/Recursion/IRecursionMapperFunc.cs similarity index 72% rename from AgileMapper/ObjectPopulation/IRecursionMapperFunc.cs rename to AgileMapper/ObjectPopulation/Recursion/IRecursionMapperFunc.cs index f0f0b2554..92fd4d0b5 100644 --- a/AgileMapper/ObjectPopulation/IRecursionMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/Recursion/IRecursionMapperFunc.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.ObjectPopulation +namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion { using System; diff --git a/AgileMapper/ObjectPopulation/Recursion/IRecursiveMemberMappingStrategy.cs b/AgileMapper/ObjectPopulation/Recursion/IRecursiveMemberMappingStrategy.cs new file mode 100644 index 000000000..53f62cd54 --- /dev/null +++ b/AgileMapper/ObjectPopulation/Recursion/IRecursiveMemberMappingStrategy.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion +{ + using System.Linq.Expressions; + + internal interface IRecursiveMemberMappingStrategy + { + Expression GetMapRecursionCallFor( + IObjectMappingData childMappingData, + Expression sourceValue, + int dataSourceIndex, + ObjectMapperData declaredTypeMapperData); + } +} diff --git a/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs b/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs new file mode 100644 index 000000000..758972f5d --- /dev/null +++ b/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs @@ -0,0 +1,30 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion +{ + using System.Linq.Expressions; + using Members; + + internal class MapRecursionCallRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy + { + public static IRecursiveMemberMappingStrategy Instance = new MapRecursionCallRecursiveMemberMappingStrategy(); + + public Expression GetMapRecursionCallFor( + IObjectMappingData childMappingData, + Expression sourceValue, + int dataSourceIndex, + ObjectMapperData declaredTypeMapperData) + { + var childMapperData = childMappingData.MapperData; + + childMapperData.CacheMappedObjects = childMapperData.SourceIsNotFlatObject(); + + childMapperData.RegisterRequiredMapperFunc(childMappingData); + + var mapRecursionCall = declaredTypeMapperData.GetMapRecursionCall( + sourceValue, + childMapperData.TargetMember, + dataSourceIndex); + + return mapRecursionCall; + } + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs new file mode 100644 index 000000000..c952d3448 --- /dev/null +++ b/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -0,0 +1,33 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion +{ + using System.Linq.Expressions; + using Enumerables; + using Extensions; + + internal class MapToDepthRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy + { + public Expression GetMapRecursionCallFor( + IObjectMappingData childMappingData, + Expression sourceValue, + int dataSourceIndex, + ObjectMapperData declaredTypeMapperData) + { + var childMapperData = childMappingData.MapperData; + + if (childMapperData.TargetMember.IsComplex) + { + return Constants.EmptyExpression; + } + + var emptyArray = Expression.NewArrayBounds( + childMapperData.TargetMember.ElementType, + 0.ToConstantExpression()); + + var helper = new EnumerableTypeHelper(childMapperData.TargetMember); + + return helper.GetEnumerableConversion( + emptyArray, + childMapperData.RuleSet.Settings.AllowEnumerableAssignment); + } + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/RecursionMapperFunc.cs b/AgileMapper/ObjectPopulation/Recursion/RecursionMapperFunc.cs similarity index 93% rename from AgileMapper/ObjectPopulation/RecursionMapperFunc.cs rename to AgileMapper/ObjectPopulation/Recursion/RecursionMapperFunc.cs index 11b9366e5..6fc249b36 100644 --- a/AgileMapper/ObjectPopulation/RecursionMapperFunc.cs +++ b/AgileMapper/ObjectPopulation/Recursion/RecursionMapperFunc.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.ObjectPopulation +namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion { using System; using System.Linq.Expressions; diff --git a/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs b/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs index fabe317d9..fa784ccb7 100644 --- a/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs +++ b/AgileMapper/Plans/RecursionMapperMappingPlanFunction.cs @@ -2,7 +2,7 @@ { using System; using System.Linq.Expressions; - using ObjectPopulation; + using ObjectPopulation.Recursion; using ReadableExpressions; using ReadableExpressions.Extensions; diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 66baf6b73..3a4f91469 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -9,9 +9,7 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings { -#if NET_STANDARD public override bool SupportsToString => false; -#endif public override bool SupportsGetValueOrDefault => false; From 686cc6b0a70e6f1571d8b9d2ef396f5dd778a814 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 29 Nov 2017 19:36:25 +0000 Subject: [PATCH 086/176] Updating EF5 + EF6 tests to expect failure projecting a one-to-many recursive relationship --- .../WhenProjectingCircularReferences.cs | 9 +- .../WhenProjectingCircularReferences.cs | 9 +- .../WhenProjectingCircularReferences.cs | 9 +- .../WhenProjectingCircularReferences.cs | 9 +- .../AgileMapper.UnitTests.Orms.csproj | 4 +- ...OneToManyRecursionProjectionFailureTest.cs | 7 + .../IOneToManyRecursionProjectorTest.cs | 7 + .../WhenProjectingCircularReferences.cs | 147 ++++++++++++++++++ .../WhenProjectingCircularReferences.cs | 141 ----------------- AgileMapper/MappingRuleSetCollection.cs | 1 + ...apToDepthRecursiveMemberMappingStrategy.cs | 33 ---- .../Converters/EmptyArrayConverter.cs | 31 ---- .../QueryProjectionExpressionFactory.cs | 4 +- .../Queryables/QueryProjectionModifier.cs | 10 -- ...apToDepthRecursiveMemberMappingStrategy.cs | 32 ++++ .../Settings/DefaultQueryProviderSettings.cs | 2 +- .../Settings/Ef5QueryProviderSettings.cs | 2 +- .../Settings/Ef6QueryProviderSettings.cs | 2 +- .../Settings/IQueryProviderSettings.cs | 2 +- .../QueryProviderSettingsExtensions.cs | 14 ++ 20 files changed, 249 insertions(+), 226 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs create mode 100644 AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs create mode 100644 AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs delete mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs delete mode 100644 AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs delete mode 100644 AgileMapper/Queryables/Converters/EmptyArrayConverter.cs create mode 100644 AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs index 6d7ab95e0..42629bf59 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { using Infrastructure; + using Recursion; + using Xunit; public class WhenProjectingCircularReferences : - WhenProjectingCircularReferences + WhenProjectingCircularReferences, + IOneToManyRecursionProjectionFailureTest { public WhenProjectingCircularReferences(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs index 73b142c68..5cdff0b45 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { using Infrastructure; + using Recursion; + using Xunit; public class WhenProjectingCircularReferences : - WhenProjectingCircularReferences + WhenProjectingCircularReferences, + IOneToManyRecursionProjectionFailureTest { public WhenProjectingCircularReferences(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs index b5f9367a4..f1c7372e3 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { using Infrastructure; + using Recursion; + using Xunit; public class WhenProjectingCircularReferences : - WhenProjectingCircularReferences + WhenProjectingCircularReferences, + IOneToManyRecursionProjectorTest { public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs index 5f63927a4..f33baeceb 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs @@ -1,13 +1,20 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { using Infrastructure; + using Recursion; + using Xunit; public class WhenProjectingCircularReferences : - WhenProjectingCircularReferences + WhenProjectingCircularReferences, + IOneToManyRecursionProjectorTest { public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index f6e38bb3e..b05846dad 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -86,6 +86,8 @@ + + @@ -131,7 +133,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs new file mode 100644 index 000000000..781d7b0eb --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion +{ + public interface IOneToManyRecursionProjectionFailureTest + { + void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs new file mode 100644 index 000000000..4a76ca865 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion +{ + public interface IOneToManyRecursionProjectorTest + { + void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs new file mode 100644 index 000000000..42a112894 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -0,0 +1,147 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion +{ + using System; + using System.Linq; + using Infrastructure; + using Shouldly; + using TestClasses; + using Xunit; + + public abstract class WhenProjectingCircularReferences : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingCircularReferences(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAOneToOneRelationship() + { + RunTest(context => + { + var company = new Company + { + Name = "Acme", + HeadOffice = new Address { Line1 = "Acme Park", Postcode = "AC3 3ME" } + }; + + context.Companies.Add(company); + context.SaveChanges(); + + var ceo = new Employee + { + Name = "Mr Ceo", + DateOfBirth = DateTime.Today.AddYears(-21), + Address = new Address { Line1 = "Ceo Towers", Postcode = "CE0 0EC" }, + CompanyId = company.Id, + Company = company + }; + + context.Employees.Add(ceo); + context.SaveChanges(); + + company.CeoId = ceo.Id; + company.Ceo = ceo; + company.HeadOfficeId = company.HeadOffice.AddressId; + + context.SaveChanges(); + + var companyDto = context.Companies.ProjectTo().ShouldHaveSingleItem(); + + companyDto.Id.ShouldBe(company.Id); + companyDto.Name.ShouldBe(company.Name); + + companyDto.HeadOffice.ShouldNotBeNull(); + companyDto.HeadOffice.Line1.ShouldBe("Acme Park"); + companyDto.HeadOffice.Postcode.ShouldBe("AC3 3ME"); + + companyDto.Ceo.ShouldNotBeNull(); + companyDto.Ceo.Name.ShouldBe("Mr Ceo"); + companyDto.Ceo.DateOfBirth.ShouldBe(ceo.DateOfBirth); + companyDto.Ceo.Company.ShouldBeNull(); + + companyDto.Ceo.Address.ShouldNotBeNull(); + companyDto.Ceo.Address.Line1.ShouldBe("Ceo Towers"); + companyDto.Ceo.Address.Postcode.ShouldBe("CE0 0EC"); + }); + } + + #region Project One-to-Many + + protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => RunTest(ProjectAOneToManyRelationshipToFirstRecursionDepth); + + protected void DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() + => RunTestAndExpectThrow(ProjectAOneToManyRelationshipToFirstRecursionDepth); + + protected void ProjectAOneToManyRelationshipToFirstRecursionDepth(TOrmContext context) + { + var topLevel = new Category { Name = "Top Level" }; + var child1 = new Category { Name = "Top > One", ParentCategory = topLevel }; + var child2 = new Category { Name = "Top > Two", ParentCategory = topLevel }; + var child3 = new Category { Name = "Top > Three", ParentCategory = topLevel }; + + var grandChild11 = new Category { Name = "Top > One > One", ParentCategory = child1 }; + var grandChild12 = new Category { Name = "Top > One > Two", ParentCategory = child1 }; + + var grandChild21 = new Category { Name = "Top > Two > One", ParentCategory = child2 }; + var grandChild22 = new Category { Name = "Top > Two > Two", ParentCategory = child2 }; + var grandChild23 = new Category { Name = "Top > Two > Three", ParentCategory = child2 }; + + var grandChild31 = new Category { Name = "Top > Three > One", ParentCategory = child3 }; + + var greatGrandchild221 = new Category { Name = "Top > Two > Two > One", ParentCategory = grandChild22 }; + var greatGrandchild222 = new Category { Name = "Top > Two > Two > Two", ParentCategory = grandChild22 }; + + context.Categories.Add(topLevel); + context.Categories.Add(child1); + context.Categories.Add(child2); + context.Categories.Add(child3); + context.Categories.Add(grandChild11); + context.Categories.Add(grandChild12); + context.Categories.Add(grandChild21); + context.Categories.Add(grandChild22); + context.Categories.Add(grandChild23); + context.Categories.Add(grandChild31); + context.Categories.Add(greatGrandchild221); + context.Categories.Add(greatGrandchild222); + + context.SaveChanges(); + + var topLevelDto = context + .Categories + .ProjectTo() + .OrderBy(c => c.Id) + .First(c => c.Name == "Top Level"); + + topLevelDto.Id.ShouldBe(topLevel.Id); + topLevelDto.ParentCategoryId.ShouldBe(default(int)); + topLevelDto.ParentCategory.ShouldBeNull(); + + var topLevelSubCategoryDtos = topLevelDto + .SubCategories + .OrderBy(sc => sc.Id) + .ToArray(); + + topLevelSubCategoryDtos.Length.ShouldBe(3); + + topLevelSubCategoryDtos.First().Id.ShouldBe(child1.Id); + topLevelSubCategoryDtos.First().Name.ShouldBe("Top > One"); + topLevelSubCategoryDtos.First().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.First().SubCategories.ShouldBeEmpty(); + + topLevelSubCategoryDtos.Second().Id.ShouldBe(child2.Id); + topLevelSubCategoryDtos.Second().Name.ShouldBe("Top > Two"); + topLevelSubCategoryDtos.Second().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.Second().SubCategories.ShouldBeEmpty(); + + topLevelSubCategoryDtos.Third().Id.ShouldBe(child3.Id); + topLevelSubCategoryDtos.Third().Name.ShouldBe("Top > Three"); + topLevelSubCategoryDtos.Third().ParentCategoryId.ShouldBe(topLevel.Id); + topLevelSubCategoryDtos.Third().SubCategories.ShouldBeEmpty(); + } + + #endregion + } +} diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs deleted file mode 100644 index 9cafa314b..000000000 --- a/AgileMapper.UnitTests.Orms/WhenProjectingCircularReferences.cs +++ /dev/null @@ -1,141 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms -{ - using System; - using System.Linq; - using Infrastructure; - using Shouldly; - using TestClasses; - using Xunit; - - public abstract class WhenProjectingCircularReferences : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenProjectingCircularReferences(ITestContext context) - : base(context) - { - } - - [Fact] - public void ShouldProjectAOneToOneRelationship() - { - RunTest(context => - { - var company = new Company - { - Name = "Acme", - HeadOffice = new Address { Line1 = "Acme Park", Postcode = "AC3 3ME" } - }; - - context.Companies.Add(company); - context.SaveChanges(); - - var ceo = new Employee - { - Name = "Mr Ceo", - DateOfBirth = DateTime.Today.AddYears(-21), - Address = new Address { Line1 = "Ceo Towers", Postcode = "CE0 0EC" }, - CompanyId = company.Id, - Company = company - }; - - context.Employees.Add(ceo); - context.SaveChanges(); - - company.CeoId = ceo.Id; - company.Ceo = ceo; - company.HeadOfficeId = company.HeadOffice.AddressId; - - context.SaveChanges(); - - var companyDto = context.Companies.ProjectTo().ShouldHaveSingleItem(); - - companyDto.Id.ShouldBe(company.Id); - companyDto.Name.ShouldBe(company.Name); - - companyDto.HeadOffice.ShouldNotBeNull(); - companyDto.HeadOffice.Line1.ShouldBe("Acme Park"); - companyDto.HeadOffice.Postcode.ShouldBe("AC3 3ME"); - - companyDto.Ceo.ShouldNotBeNull(); - companyDto.Ceo.Name.ShouldBe("Mr Ceo"); - companyDto.Ceo.DateOfBirth.ShouldBe(ceo.DateOfBirth); - companyDto.Ceo.Company.ShouldBeNull(); - - companyDto.Ceo.Address.ShouldNotBeNull(); - companyDto.Ceo.Address.Line1.ShouldBe("Ceo Towers"); - companyDto.Ceo.Address.Postcode.ShouldBe("CE0 0EC"); - }); - } - - [Fact] - public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() - { - RunTest(context => - { - var topLevel = new Category { Name = "Top Level" }; - var child1 = new Category { Name = "Top > One", ParentCategory = topLevel }; - var child2 = new Category { Name = "Top > Two", ParentCategory = topLevel }; - var child3 = new Category { Name = "Top > Three", ParentCategory = topLevel }; - - var grandChild11 = new Category { Name = "Top > One > One", ParentCategory = child1 }; - var grandChild12 = new Category { Name = "Top > One > Two", ParentCategory = child1 }; - - var grandChild21 = new Category { Name = "Top > Two > One", ParentCategory = child2 }; - var grandChild22 = new Category { Name = "Top > Two > Two", ParentCategory = child2 }; - var grandChild23 = new Category { Name = "Top > Two > Three", ParentCategory = child2 }; - - var grandChild31 = new Category { Name = "Top > Three > One", ParentCategory = child3 }; - - var greatGrandchild221 = new Category { Name = "Top > Two > Two > One", ParentCategory = grandChild22 }; - var greatGrandchild222 = new Category { Name = "Top > Two > Two > Two", ParentCategory = grandChild22 }; - - context.Categories.Add(topLevel); - context.Categories.Add(child1); - context.Categories.Add(child2); - context.Categories.Add(child3); - context.Categories.Add(grandChild11); - context.Categories.Add(grandChild12); - context.Categories.Add(grandChild21); - context.Categories.Add(grandChild22); - context.Categories.Add(grandChild23); - context.Categories.Add(grandChild31); - context.Categories.Add(greatGrandchild221); - context.Categories.Add(greatGrandchild222); - - context.SaveChanges(); - - var topLevelDto = context - .Categories - .ProjectTo() - .OrderBy(c => c.Id) - .First(c => c.Name == "Top Level"); - - topLevelDto.Id.ShouldBe(topLevel.Id); - topLevelDto.ParentCategoryId.ShouldBe(default(int)); - topLevelDto.ParentCategory.ShouldBeNull(); - - var topLevelSubCategoryDtos = topLevelDto - .SubCategories - .OrderBy(sc => sc.Id) - .ToArray(); - - topLevelSubCategoryDtos.Length.ShouldBe(3); - - topLevelSubCategoryDtos.First().Id.ShouldBe(child1.Id); - topLevelSubCategoryDtos.First().Name.ShouldBe("Top > One"); - topLevelSubCategoryDtos.First().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.First().SubCategories.ShouldBeEmpty(); - - topLevelSubCategoryDtos.Second().Id.ShouldBe(child2.Id); - topLevelSubCategoryDtos.Second().Name.ShouldBe("Top > Two"); - topLevelSubCategoryDtos.Second().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.Second().SubCategories.ShouldBeEmpty(); - - topLevelSubCategoryDtos.Third().Id.ShouldBe(child3.Id); - topLevelSubCategoryDtos.Third().Name.ShouldBe("Top > Three"); - topLevelSubCategoryDtos.Third().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.Third().SubCategories.ShouldBeEmpty(); - }); - } - } -} diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 1b36215ff..9fbdead36 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -6,6 +6,7 @@ namespace AgileObjects.AgileMapper using ObjectPopulation; using ObjectPopulation.Enumerables; using ObjectPopulation.Recursion; + using Queryables.Recursion; internal class MappingRuleSetCollection { diff --git a/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs deleted file mode 100644 index c952d3448..000000000 --- a/AgileMapper/ObjectPopulation/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion -{ - using System.Linq.Expressions; - using Enumerables; - using Extensions; - - internal class MapToDepthRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy - { - public Expression GetMapRecursionCallFor( - IObjectMappingData childMappingData, - Expression sourceValue, - int dataSourceIndex, - ObjectMapperData declaredTypeMapperData) - { - var childMapperData = childMappingData.MapperData; - - if (childMapperData.TargetMember.IsComplex) - { - return Constants.EmptyExpression; - } - - var emptyArray = Expression.NewArrayBounds( - childMapperData.TargetMember.ElementType, - 0.ToConstantExpression()); - - var helper = new EnumerableTypeHelper(childMapperData.TargetMember); - - return helper.GetEnumerableConversion( - emptyArray, - childMapperData.RuleSet.Settings.AllowEnumerableAssignment); - } - } -} \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/EmptyArrayConverter.cs b/AgileMapper/Queryables/Converters/EmptyArrayConverter.cs deleted file mode 100644 index 3332219f0..000000000 --- a/AgileMapper/Queryables/Converters/EmptyArrayConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace AgileObjects.AgileMapper.Queryables.Converters -{ - using System.Linq.Expressions; - using Extensions; - using Settings; - - internal static class EmptyArrayConverter - { - public static bool TryConvert( - NewArrayExpression newArray, - IQueryProviderSettings settings, - out Expression converted) - { - if (settings.SupportsEmptyArrayCreation || IsNotEmptyArrayCreation(newArray)) - { - converted = null; - return false; - } - - converted = settings.ConvertEmptyArrayCreation(newArray); - return true; - } - - private static bool IsNotEmptyArrayCreation(NewArrayExpression newArray) - { - return !newArray.Expressions.HasOne() || - (newArray.Expressions[0].NodeType != ExpressionType.Constant) || - (((ConstantExpression)newArray.Expressions[0]).Value != (object)0); - } - } -} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 45f8a5c60..c5f79615a 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -20,8 +20,6 @@ public override bool IsFor(IObjectMappingData mappingData) protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; - var queryProviderType = ((QueryProjectorKey)mappingData.MapperKey).QueryProviderType; - var providerSettings = QueryProviderSettings.For(queryProviderType); var queryProjection = mapperData .EnumerablePopulationBuilder @@ -32,6 +30,8 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); + var providerSettings = mappingData.GetQueryProviderSettings(); + queryProjection = QueryProjectionModifier.Modify(queryProjection, providerSettings); yield return queryProjection; diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index ca0709eb6..7f6fc9069 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -66,15 +66,5 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) protected override Expression VisitDefault(DefaultExpression defaultExpression) => DefaultExpressionConverter.Convert(defaultExpression); - - protected override Expression VisitNewArray(NewArrayExpression newArray) - { - if (EmptyArrayConverter.TryConvert(newArray, _settings, out var converted)) - { - return converted; - } - - return base.VisitNewArray(newArray); - } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs new file mode 100644 index 000000000..00a0af55f --- /dev/null +++ b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -0,0 +1,32 @@ +namespace AgileObjects.AgileMapper.Queryables.Recursion +{ + using System.Linq.Expressions; + using ObjectPopulation; + using ObjectPopulation.Enumerables; + using ObjectPopulation.Recursion; + + internal class MapToDepthRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy + { + public Expression GetMapRecursionCallFor( + IObjectMappingData childMappingData, + Expression sourceValue, + int dataSourceIndex, + ObjectMapperData declaredTypeMapperData) + { + var targetMember = childMappingData.MapperData.TargetMember; + + if (targetMember.IsComplex) + { + return Constants.EmptyExpression; + } + + var helper = new EnumerableTypeHelper(targetMember); + + var emptyCollection = helper.IsList + ? Expression.ListInit(Expression.New(helper.ListType), Enumerable.EmptyArray) + : (Expression)Expression.NewArrayInit(targetMember.ElementType); + + return helper.GetEnumerableConversion(emptyCollection, allowEnumerableAssignment: true); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 8647fe808..90b44bfe5 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -33,7 +33,7 @@ public DefaultQueryProviderSettings() public virtual bool SupportsGetValueOrDefault => true; - public virtual bool SupportsEmptyArrayCreation => true; + public virtual bool SupportsEmptyEnumerableCreation => true; public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 3a4f91469..6c4c6308a 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -13,7 +13,7 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsGetValueOrDefault => false; - public override bool SupportsEmptyArrayCreation => false; + public override bool SupportsEmptyEnumerableCreation => false; #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index d3262d18f..6c37e5f8b 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -9,7 +9,7 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsGetValueOrDefault => false; - public override bool SupportsEmptyArrayCreation => false; + public override bool SupportsEmptyEnumerableCreation => false; #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 416e2ade8..494f6c16b 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -18,7 +18,7 @@ internal interface IQueryProviderSettings bool SupportsGetValueOrDefault { get; } - bool SupportsEmptyArrayCreation { get; } + bool SupportsEmptyEnumerableCreation { get; } Expression ConvertToStringCall(MethodCallExpression call); diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 11f4542d6..bd3a336a0 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -8,9 +8,23 @@ using Extensions; using NetStandardPolyfills; #endif + using ObjectPopulation; internal static class QueryProviderSettingsExtensions { + public static IQueryProviderSettings GetQueryProviderSettings(this IObjectMappingData mappingData) + { + while (!mappingData.IsRoot) + { + mappingData = mappingData.Parent; + } + + var queryProviderType = ((QueryProjectorKey)mappingData.MapperKey).QueryProviderType; + var providerSettings = QueryProviderSettings.For(queryProviderType); + + return providerSettings; + } + #if !NET_STANDARD public static Expression GetCreateDateTimeFromStringOrNull( this IQueryProviderSettings settings, From c1f780046cf69ecbbd5f7e353bf0ccb8e266dad9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 30 Nov 2017 08:52:43 +0000 Subject: [PATCH 087/176] Simplifying projection recursion strategy --- .../MapToDepthRecursiveMemberMappingStrategy.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs index 00a0af55f..c934afcc6 100644 --- a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs +++ b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -2,7 +2,6 @@ { using System.Linq.Expressions; using ObjectPopulation; - using ObjectPopulation.Enumerables; using ObjectPopulation.Recursion; internal class MapToDepthRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy @@ -13,20 +12,15 @@ public Expression GetMapRecursionCallFor( int dataSourceIndex, ObjectMapperData declaredTypeMapperData) { - var targetMember = childMappingData.MapperData.TargetMember; - - if (targetMember.IsComplex) + if (childMappingData.MapperData.TargetMember.IsComplex) { return Constants.EmptyExpression; } - var helper = new EnumerableTypeHelper(targetMember); - - var emptyCollection = helper.IsList - ? Expression.ListInit(Expression.New(helper.ListType), Enumerable.EmptyArray) - : (Expression)Expression.NewArrayInit(targetMember.ElementType); + var helper = childMappingData.MapperData.EnumerablePopulationBuilder.TargetTypeHelper; + var emptyArray = Expression.NewArrayInit(helper.ElementType); - return helper.GetEnumerableConversion(emptyCollection, allowEnumerableAssignment: true); + return helper.GetEnumerableConversion(emptyArray, allowEnumerableAssignment: true); } } } \ No newline at end of file From 73b739482f0956ae142689447823343b0bb87c4b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 20 Dec 2017 18:38:43 +0000 Subject: [PATCH 088/176] Updating to NetStandardPolyfills v1.2.1 --- .../AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 4 ++-- AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj | 4 ++-- AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index 2c8f0bcad..84cbdc848 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -45,8 +45,8 @@ ..\AgileMapper.snk - - ..\packages\AgileObjects.NetStandardPolyfills.1.2.0\lib\net40\AgileObjects.NetStandardPolyfills.dll + + ..\packages\AgileObjects.NetStandardPolyfills.1.2.1\lib\net40\AgileObjects.NetStandardPolyfills.dll ..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config index f8c4ddcdf..0542ac6f8 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/packages.config @@ -1,6 +1,6 @@  - + diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj index bc4fdaed2..c2b415d6f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj @@ -45,8 +45,8 @@ ..\AgileMapper.snk - - ..\packages\AgileObjects.NetStandardPolyfills.1.2.0\lib\net40\AgileObjects.NetStandardPolyfills.dll + + ..\packages\AgileObjects.NetStandardPolyfills.1.2.1\lib\net40\AgileObjects.NetStandardPolyfills.dll ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config index 5693d559d..8a0e4ea4d 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/packages.config @@ -1,6 +1,6 @@  - + From e82b4f6c1f60493bd4b55674433efb564e6ef3f7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 21 Dec 2017 22:35:50 +0000 Subject: [PATCH 089/176] Adding Queryable.Linq package to .NET Standard 1.3 target / Readding missing file --- AgileMapper/AgileMapper.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/AgileMapper/AgileMapper.csproj b/AgileMapper/AgileMapper.csproj index 7eed2270a..7206ec1e4 100644 --- a/AgileMapper/AgileMapper.csproj +++ b/AgileMapper/AgileMapper.csproj @@ -26,6 +26,9 @@ + + 4.3.0 + @@ -34,6 +37,9 @@ + + 4.3.0 + @@ -46,10 +52,4 @@ - - - 4.3.0 - - - From 0651e5711a20db824d37fc50028383d1ba599c06 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 30 Jan 2018 18:55:49 +0000 Subject: [PATCH 090/176] Updating project description --- AgileMapper/Properties/AssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AgileMapper/Properties/AssemblyInfo.cs b/AgileMapper/Properties/AssemblyInfo.cs index b52b25aea..7876615f9 100644 --- a/AgileMapper/Properties/AssemblyInfo.cs +++ b/AgileMapper/Properties/AssemblyInfo.cs @@ -4,7 +4,7 @@ using System.Security; [assembly: AssemblyTitle("AgileObjects.AgileMapper")] -[assembly: AssemblyDescription("A zero-configuration, highly-configurable object-object mapper with viewable execution plans. Performs deep clones, updates and merges via a static or instance API. Conforms to .NET Standard 1.0.")] +[assembly: AssemblyDescription("A zero-configuration, highly-configurable object-object mapper with viewable execution plans. Performs deep clones, updates and merges via extension methods, or a static or instance API. Targets .NET Standard 1.0 and .NET 4.0.")] [assembly: AllowPartiallyTrustedCallers] [assembly: CLSCompliant(true)] From 56f02057d5802a38b98d36ff02e3b305606e4ea0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 2 Feb 2018 20:35:23 +0000 Subject: [PATCH 091/176] Differentiating between GetNewInstanceCreation and GetEmptyInstanceCreation for enumerables / Reusing EnumerableTypeHelper when creating empty enumerable instances where possible --- .../Internal/ExpressionExtensions.cs | 10 +++++-- .../Members/MemberMapperDataExtensions.cs | 21 -------------- .../DictionaryMappingExpressionFactory.cs | 4 +-- .../SourceObjectDictionaryAdapter.cs | 4 +-- .../EnumerablePopulationBuilder.cs | 2 +- .../Enumerables/EnumerableTypeHelper.cs | 16 ++++++---- .../MappingDataCreationFactory.cs | 3 ++ .../ObjectPopulation/MappingFactory.cs | 5 ---- ...rsionCallRecursiveMemberMappingStrategy.cs | 29 +++++++++++++++++++ ...apToDepthRecursiveMemberMappingStrategy.cs | 3 +- 10 files changed, 56 insertions(+), 41 deletions(-) diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index 5eaa0268f..a079bd98b 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -286,7 +286,10 @@ private static Expression GetToEnumerableCall(Expression enumerable, MethodInfo return Expression.Call(typedToEnumerableMethod, enumerable); } - public static Expression GetEmptyInstanceCreation(this Type enumerableType, Type elementType) + public static Expression GetEmptyInstanceCreation( + this Type enumerableType, + Type elementType, + EnumerableTypeHelper typeHelper = null) { if (enumerableType.IsArray) { @@ -298,7 +301,10 @@ public static Expression GetEmptyInstanceCreation(this Type enumerableType, Type return Expression.New(enumerableType); } - var typeHelper = new EnumerableTypeHelper(enumerableType, elementType); + if (typeHelper == null) + { + typeHelper = new EnumerableTypeHelper(enumerableType, elementType); + } if (typeHelper.IsEnumerableInterface) { diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 8bbb63229..ff8c52326 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -132,27 +132,6 @@ public static ExpressionInfoFinder.ExpressionInfo GetExpressionInfoFor( targetCanBeNull); } - public static bool DoNotMapRecursion(this IMemberMapperData mapperData) - { - if (mapperData.SourceType.IsDictionary()) - { - return true; - } - - while (mapperData != null) - { - if (mapperData.TargetType.Name.EndsWith("Dto", StringComparison.Ordinal) || - mapperData.TargetType.Name.EndsWith("DataTransferObject", StringComparison.Ordinal)) - { - return true; - } - - mapperData = mapperData.Parent; - } - - return false; - } - public static bool SourceMemberIsStringKeyedDictionary( this IMemberMapperData mapperData, out DictionarySourceMember dictionarySourceMember) diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index f7c0551fe..5c326165f 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -420,8 +420,8 @@ private static bool UseParameterlessConstructor( private static Expression GetParameterlessDictionaryAssignment(IObjectMappingData mappingData) { - var valueType = mappingData.MapperData.EnumerablePopulationBuilder.TargetTypeHelper.ElementType; - var newDictionary = mappingData.MapperData.TargetType.GetEmptyInstanceCreation(valueType); + var helper = mappingData.MapperData.EnumerablePopulationBuilder.TargetTypeHelper; + var newDictionary = helper.GetEmptyInstanceCreation(); return GetDictionaryAssignment(newDictionary, mappingData); } diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryAdapter.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryAdapter.cs index 77fa4b35d..efec1e291 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryAdapter.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceObjectDictionaryAdapter.cs @@ -18,9 +18,7 @@ public SourceObjectDictionaryAdapter( : base(builder) { _instanceDictionaryAdapter = new SourceInstanceDictionaryAdapter(sourceMember, builder); - - var targetEnumerableType = TargetTypeHelper.EnumerableInterfaceType; - _emptyTarget = targetEnumerableType.GetEmptyInstanceCreation(TargetTypeHelper.ElementType); + _emptyTarget = TargetTypeHelper.GetEmptyInstanceCreation(TargetTypeHelper.EnumerableInterfaceType); } public override Expression GetSourceValues() diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index 42a765419..c6c4a0432 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -306,7 +306,7 @@ private Expression GetTargetVariableValue() { if (!MapperData.TargetMemberHasInitAccessibleValue()) { - return TargetTypeHelper.GetEmptyInstanceCreation(); + return TargetTypeHelper.GetNewInstanceCreation(); } if (MapperData.TargetIsDefinitelyUnpopulated()) diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 9634844d5..5caba4554 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -83,19 +83,25 @@ private Type GetEnumerableType(ref Type typeField, Type openGenericEnumerableTyp public Type WrapperType => typeof(ReadOnlyCollectionWrapper<>).MakeGenericType(ElementType); - public Expression GetEmptyInstanceCreation() + public Expression GetNewInstanceCreation() { - if (IsReadOnly || EnumerableType.IsInterface()) + return IsReadOnly || EnumerableType.IsInterface() + ? Expression.New(ListType) + : GetEmptyInstanceCreation(); + } + + public Expression GetEmptyInstanceCreation(Type enumerableType = null) + { + if ((enumerableType == EnumerableType) || (enumerableType == null)) { - return Expression.New(ListType); + return EnumerableType.GetEmptyInstanceCreation(ElementType, this); } - return EnumerableType.GetEmptyInstanceCreation(ElementType); + return enumerableType.GetEmptyInstanceCreation(ElementType); } public Expression GetWrapperConstruction(Expression existingItems, Expression newItemsCount) { - // ReSharper disable once AssignNullToNotNullAttribute return Expression.New( WrapperType.GetPublicInstanceConstructor(ListInterfaceType, typeof(int)), existingItems, diff --git a/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs b/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs index f14ebd795..9ff279caa 100644 --- a/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { + using System.Diagnostics; using System.Linq.Expressions; using Extensions.Internal; using Members; @@ -27,6 +28,7 @@ private static bool UseAsConversion(ObjectMapperData childMapperData, out Expres return false; } + [DebuggerStepThrough] public static Expression ForChild( MappingValues mappingValues, int dataSourceIndex, @@ -56,6 +58,7 @@ public static Expression ForChild( return createCall; } + [DebuggerStepThrough] public static Expression ForElement( MappingValues mappingValues, Expression enumerableMappingDataObject, diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index 169d38a41..f8f38a414 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -54,11 +54,6 @@ public static Expression GetChildMapping( if (childMapperData.TargetMemberEverRecurses()) { - if (childMapperData.DoNotMapRecursion()) - { - return Constants.EmptyExpression; - } - var mapRecursionCall = childMapperData .RuleSet .RecursiveMemberMappingStrategy diff --git a/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs b/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs index e8e855eae..79b59d444 100644 --- a/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs +++ b/AgileMapper/ObjectPopulation/Recursion/MapRecursionCallRecursiveMemberMappingStrategy.cs @@ -1,6 +1,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.Recursion { + using System; using System.Linq.Expressions; + using Extensions.Internal; + using Members; internal class MapRecursionCallRecursiveMemberMappingStrategy : IRecursiveMemberMappingStrategy { @@ -14,6 +17,11 @@ public Expression GetMapRecursionCallFor( { var childMapperData = childMappingData.MapperData; + if (DoNotMapRecursion(childMapperData)) + { + return Constants.EmptyExpression; + } + childMapperData.CacheMappedObjects = true; childMapperData.RegisterRequiredMapperFunc(childMappingData); @@ -25,5 +33,26 @@ public Expression GetMapRecursionCallFor( return mapRecursionCall; } + + private static bool DoNotMapRecursion(IMemberMapperData mapperData) + { + if (mapperData.SourceType.IsDictionary()) + { + return true; + } + + while (mapperData != null) + { + if (mapperData.TargetType.Name.EndsWith("Dto", StringComparison.Ordinal) || + mapperData.TargetType.Name.EndsWith("DataTransferObject", StringComparison.Ordinal)) + { + return true; + } + + mapperData = mapperData.Parent; + } + + return false; + } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs index c934afcc6..26fac7f75 100644 --- a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs +++ b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -18,9 +18,8 @@ public Expression GetMapRecursionCallFor( } var helper = childMappingData.MapperData.EnumerablePopulationBuilder.TargetTypeHelper; - var emptyArray = Expression.NewArrayInit(helper.ElementType); - return helper.GetEnumerableConversion(emptyArray, allowEnumerableAssignment: true); + return helper.GetEmptyInstanceCreation(); } } } \ No newline at end of file From 8481c6d60db3e4361e3c3d360949d7199852603d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 10:16:21 +0000 Subject: [PATCH 092/176] Organising one-to-many circular references test --- .../WhenProjectingCircularReferences.cs | 4 + .../WhenProjectingCircularReferences.cs | 134 ++++++++++++------ .../TestClasses/Category.cs | 25 +++- .../TestClasses/CategoryDto.cs | 2 +- 4 files changed, 119 insertions(+), 46 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs index f33baeceb..64459c9d9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs @@ -16,5 +16,9 @@ public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) [Fact] public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + + [Fact] + public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index 42a112894..11a0e1c22 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -70,42 +70,48 @@ public void ShouldProjectAOneToOneRelationship() #region Project One-to-Many protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() - => RunTest(ProjectAOneToManyRelationshipToFirstRecursionDepth); + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); protected void DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() - => RunTestAndExpectThrow(ProjectAOneToManyRelationshipToFirstRecursionDepth); + => RunTestAndExpectThrow(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); - protected void ProjectAOneToManyRelationshipToFirstRecursionDepth(TOrmContext context) + protected void DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(2, context)); + + protected void ProjectAOneToManyRelationshipToRecursionDepth( + int depth, + TOrmContext context) { var topLevel = new Category { Name = "Top Level" }; - var child1 = new Category { Name = "Top > One", ParentCategory = topLevel }; - var child2 = new Category { Name = "Top > Two", ParentCategory = topLevel }; - var child3 = new Category { Name = "Top > Three", ParentCategory = topLevel }; - var grandChild11 = new Category { Name = "Top > One > One", ParentCategory = child1 }; - var grandChild12 = new Category { Name = "Top > One > Two", ParentCategory = child1 }; + topLevel.AddSubCategories( + new Category { Name = "Top > One" }, + new Category { Name = "Top > Two" }, + new Category { Name = "Top > Three" }); + + context.Categories.Add(topLevel); - var grandChild21 = new Category { Name = "Top > Two > One", ParentCategory = child2 }; - var grandChild22 = new Category { Name = "Top > Two > Two", ParentCategory = child2 }; - var grandChild23 = new Category { Name = "Top > Two > Three", ParentCategory = child2 }; + if (depth > 1) + { + topLevel.SubCategories.First().AddSubCategories( + new Category { Name = "Top > One > One" }, + new Category { Name = "Top > One > Two" }); - var grandChild31 = new Category { Name = "Top > Three > One", ParentCategory = child3 }; + topLevel.SubCategories.Second().AddSubCategories( + new Category { Name = "Top > Two > One" }, + new Category { Name = "Top > Two > Two" }, + new Category { Name = "Top > Two > Three" }); - var greatGrandchild221 = new Category { Name = "Top > Two > Two > One", ParentCategory = grandChild22 }; - var greatGrandchild222 = new Category { Name = "Top > Two > Two > Two", ParentCategory = grandChild22 }; + topLevel.SubCategories.Third().AddSubCategories( + new Category { Name = "Top > Three > One" }); - context.Categories.Add(topLevel); - context.Categories.Add(child1); - context.Categories.Add(child2); - context.Categories.Add(child3); - context.Categories.Add(grandChild11); - context.Categories.Add(grandChild12); - context.Categories.Add(grandChild21); - context.Categories.Add(grandChild22); - context.Categories.Add(grandChild23); - context.Categories.Add(grandChild31); - context.Categories.Add(greatGrandchild221); - context.Categories.Add(greatGrandchild222); + if (depth > 2) + { + topLevel.SubCategories.Second().SubCategories.Second().AddSubCategories( + new Category { Name = "Top > Two > Two > One" }, + new Category { Name = "Top > Two > Two > Two" }); + } + } context.SaveChanges(); @@ -116,30 +122,70 @@ protected void ProjectAOneToManyRelationshipToFirstRecursionDepth(TOrmContext co .First(c => c.Name == "Top Level"); topLevelDto.Id.ShouldBe(topLevel.Id); - topLevelDto.ParentCategoryId.ShouldBe(default(int)); + topLevelDto.ParentCategoryId.ShouldBeNull(); topLevelDto.ParentCategory.ShouldBeNull(); - var topLevelSubCategoryDtos = topLevelDto - .SubCategories - .OrderBy(sc => sc.Id) - .ToArray(); + var depth1Dtos = GetOrderedSubCategories(topLevelDto); + + depth1Dtos.Length.ShouldBe(3); + + var child1 = topLevel.SubCategories.First(); + var child2 = topLevel.SubCategories.Second(); + var child3 = topLevel.SubCategories.Third(); + + Verify(depth1Dtos.First(), child1); + Verify(depth1Dtos.Second(), child2); + Verify(depth1Dtos.Third(), child3); + + if (!(depth > 1)) + { + depth1Dtos.First().SubCategories.ShouldBeEmpty(); + depth1Dtos.Second().SubCategories.ShouldBeEmpty(); + depth1Dtos.Third().SubCategories.ShouldBeEmpty(); + return; + } + + var depth11Dtos = GetOrderedSubCategories(depth1Dtos.First()); - topLevelSubCategoryDtos.Length.ShouldBe(3); + depth11Dtos.Length.ShouldBe(2); - topLevelSubCategoryDtos.First().Id.ShouldBe(child1.Id); - topLevelSubCategoryDtos.First().Name.ShouldBe("Top > One"); - topLevelSubCategoryDtos.First().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.First().SubCategories.ShouldBeEmpty(); + Verify(depth11Dtos.First(), child1.SubCategories.First()); + Verify(depth11Dtos.Second(), child1.SubCategories.Second()); - topLevelSubCategoryDtos.Second().Id.ShouldBe(child2.Id); - topLevelSubCategoryDtos.Second().Name.ShouldBe("Top > Two"); - topLevelSubCategoryDtos.Second().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.Second().SubCategories.ShouldBeEmpty(); + var depth12Dtos = GetOrderedSubCategories(depth1Dtos.Second()); - topLevelSubCategoryDtos.Third().Id.ShouldBe(child3.Id); - topLevelSubCategoryDtos.Third().Name.ShouldBe("Top > Three"); - topLevelSubCategoryDtos.Third().ParentCategoryId.ShouldBe(topLevel.Id); - topLevelSubCategoryDtos.Third().SubCategories.ShouldBeEmpty(); + depth12Dtos.Length.ShouldBe(3); + + Verify(depth12Dtos.First(), child2.SubCategories.First()); + Verify(depth12Dtos.Second(), child2.SubCategories.Second()); + + var depth13Dtos = GetOrderedSubCategories(depth1Dtos.Third()); + + depth13Dtos.ShouldHaveSingleItem(); + + Verify(depth13Dtos.First(), child3.SubCategories.First()); + + if (!(depth > 2)) + { + depth11Dtos.First().SubCategories.ShouldBeEmpty(); + depth11Dtos.Second().SubCategories.ShouldBeEmpty(); + + depth12Dtos.First().SubCategories.ShouldBeEmpty(); + depth12Dtos.Second().SubCategories.ShouldBeEmpty(); + depth12Dtos.Third().SubCategories.ShouldBeEmpty(); + + depth13Dtos.First().SubCategories.ShouldBeEmpty(); + } + } + + private static CategoryDto[] GetOrderedSubCategories(CategoryDto parentDto) + => parentDto.SubCategories.OrderBy(sc => sc.Id).ToArray(); + + private static void Verify(CategoryDto result, Category source) + { + result.Id.ShouldBe(source.Id); + result.Name.ShouldBe(source.Name); + result.ParentCategoryId.ShouldBe(source.ParentCategoryId); } #endregion diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Category.cs b/AgileMapper.UnitTests.Orms/TestClasses/Category.cs index d2d7c34fb..d750e7661 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Category.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Category.cs @@ -5,6 +5,8 @@ public class Category { + private Category _parentCategory; + public Category() { SubCategories = new List(); @@ -15,10 +17,31 @@ public Category() public int? ParentCategoryId { get; set; } - public Category ParentCategory { get; set; } + public Category ParentCategory + { + get => _parentCategory; + set + { + _parentCategory = value; + + if (value != null) + { + ParentCategoryId = _parentCategory.Id; + } + } + } public string Name { get; set; } + public void AddSubCategories(params Category[] subCategories) + { + foreach (var subCategory in subCategories) + { + subCategory.ParentCategory = this; + SubCategories.Add(subCategory); + } + } + public ICollection SubCategories { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs index c03abdb01..62dd1a50c 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/CategoryDto.cs @@ -6,7 +6,7 @@ public class CategoryDto { public int Id { get; set; } - public int ParentCategoryId { get; set; } + public int? ParentCategoryId { get; set; } public CategoryDto ParentCategory { get; set; } From b89bad99cae0f6636e4999d3627139be06c9a6a6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 10:29:49 +0000 Subject: [PATCH 093/176] Removing Shouldly use from ORM tests --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 4 ++++ .../WhenCreatingProjections.cs | 1 - .../packages.config | 1 - .../AgileMapper.UnitTests.Orms.csproj | 7 ++++--- .../WhenProjectingToEnumerableMembers.cs | 1 - .../Infrastructure/OrmTestClassBase.cs | 1 - .../WhenProjectingCircularReferences.cs | 1 - .../WhenConvertingToBools.cs | 1 - .../WhenConvertingToDateTimes.cs | 1 - .../WhenConvertingToDoubles.cs | 1 - .../WhenConvertingToGuids.cs | 1 - .../WhenConvertingToInts.cs | 1 - .../WhenConvertingToStrings.cs | 1 - .../WhenProjectingFlatTypes.cs | 1 - .../WhenProjectingToComplexTypeMembers.cs | 1 - .../WhenViewingMappingPlans.cs | 1 - AgileMapper.UnitTests.Orms/packages.config | 1 - AgileMapper.UnitTests/Should.cs | 2 +- AgileMapper.UnitTests/ShouldExtensions.cs | 19 ++++++++++++++++++- 19 files changed, 27 insertions(+), 20 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index ff42151d0..c7995e40d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -171,6 +171,10 @@ {66522d44-19f5-4af5-9d43-483a3cd6f958} AgileMapper.UnitTests.Orms + + {a3f2d405-8c0b-4033-9ec5-1b64007593fb} + AgileMapper.UnitTests + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} AgileMapper diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index 034451f38..1a5062f3b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -4,7 +4,6 @@ using Infrastructure; using MoreTestClasses; using Orms.Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config index c63923f24..63a62f333 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -12,7 +12,6 @@ - diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index b05846dad..2d1ca2bf7 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -45,9 +45,6 @@ ..\AgileMapper.snk - - ..\packages\Shouldly.2.8.3\lib\net451\Shouldly.dll - @@ -147,6 +144,10 @@ {049E1EE5-48CE-441A-B166-3CF6BEC17957} AgileMapper.UnitTests.MoreTestClasses + + {a3f2d405-8c0b-4033-9ec5-1b64007593fb} + AgileMapper.UnitTests + {46d95c53-b4cb-4ee7-9573-5d3ef96099c0} AgileMapper diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index 10e745d61..73679e415 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; public abstract class WhenProjectingToEnumerableMembers : OrmTestClassBase diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 77ec21977..7343a1c95 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; - using Shouldly; using Xunit; [Collection(TestConstants.OrmCollectionName)] diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index 11a0e1c22..d7e2756b1 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index dab12cd34..bb703a250 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index a9cabd570..e1ac86db9 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; public abstract class WhenConvertingToDateTimes : OrmTestClassBase diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs index fb29b2400..0a7d7fa2d 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index df1d70e13..5b944252b 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; public abstract class WhenConvertingToGuids : OrmTestClassBase diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 3a54842ad..e9360756b 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index 9279d3940..b2d542c65 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 712b11b21..0e6b98280 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 0584b9c03..733e85ab7 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -2,7 +2,6 @@ { using System.Linq; using Infrastructure; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index 96fdc4767..efc502b7c 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -4,7 +4,6 @@ using Infrastructure; using MoreTestClasses; using ObjectPopulation; - using Shouldly; using TestClasses; using Xunit; diff --git a/AgileMapper.UnitTests.Orms/packages.config b/AgileMapper.UnitTests.Orms/packages.config index 8a4b33309..470f8bf96 100644 --- a/AgileMapper.UnitTests.Orms/packages.config +++ b/AgileMapper.UnitTests.Orms/packages.config @@ -1,6 +1,5 @@  - diff --git a/AgileMapper.UnitTests/Should.cs b/AgileMapper.UnitTests/Should.cs index 9b4300d45..bf0123696 100644 --- a/AgileMapper.UnitTests/Should.cs +++ b/AgileMapper.UnitTests/Should.cs @@ -2,7 +2,7 @@ { using System; - internal static class Should + public static class Should { public static TException Throw(Action testAction) where TException : Exception diff --git a/AgileMapper.UnitTests/ShouldExtensions.cs b/AgileMapper.UnitTests/ShouldExtensions.cs index 13e568041..6f975d8bd 100644 --- a/AgileMapper.UnitTests/ShouldExtensions.cs +++ b/AgileMapper.UnitTests/ShouldExtensions.cs @@ -6,12 +6,29 @@ using System.Reflection; using NetStandardPolyfills; - internal static class ShouldExtensions + public static class ShouldExtensions { public static void ShouldBeDefault(this T value) => value.ShouldBe(default(T)); public static void ShouldNotBeDefault(this T value) => value.ShouldNotBe(default(T)); + public static void ShouldBe(this DateTime value, DateTime expectedValue, TimeSpan tolerance) + { + var minimumExpectedValue = expectedValue.Subtract(tolerance); + + if (value < minimumExpectedValue) + { + Asplode($"a DateTime greater than {minimumExpectedValue}", $"{value}"); + } + + var maximumExpectedValue = expectedValue.Add(tolerance); + + if (value > maximumExpectedValue) + { + Asplode($"a DateTime less than {maximumExpectedValue}", $"{value}"); + } + } + public static void ShouldBe(this TActual? value, TExpected expectedValue) where TActual : struct { From b79b42b6b262c2fa00880f264c3d4018ee84019c Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 10:59:40 +0000 Subject: [PATCH 094/176] Switching to typed-Queryable Project().To() extension method to support typed configuration --- .../WhenCreatingProjections.cs | 4 +- .../WhenProjectingToEnumerableMembers.cs | 4 +- .../WhenProjectingCircularReferences.cs | 4 +- .../WhenConvertingToBools.cs | 18 ++-- .../WhenConvertingToDateTimes.cs | 6 +- .../WhenConvertingToDoubles.cs | 10 +- .../WhenConvertingToGuids.cs | 4 +- .../WhenConvertingToInts.cs | 14 +-- .../WhenConvertingToStrings.cs | 4 +- .../WhenProjectingFlatTypes.cs | 4 +- .../WhenProjectingToComplexTypeMembers.cs | 2 +- .../WhenViewingMappingPlans.cs | 2 +- AgileMapper/Api/ProjectionResultSpecifier.cs | 82 +++++++++++++++ AgileMapper/ProjectionExtensions.cs | 99 ++----------------- 14 files changed, 129 insertions(+), 128 deletions(-) create mode 100644 AgileMapper/Api/ProjectionResultSpecifier.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index 1a5062f3b..4cfa43e92 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -23,7 +23,7 @@ public void ShouldReuseACachedProjectionMapper() { var stringDtos = context1 .StringItems - .ProjectTo(c => c.Using(mapper)) + .Project().To(c => c.Using(mapper)) .ToArray(); stringDtos.ShouldBeEmpty(); @@ -36,7 +36,7 @@ public void ShouldReuseACachedProjectionMapper() var moreStringDtos = context2 .StringItems - .ProjectTo(c => c.Using(mapper)) + .Project().To(c => c.Using(mapper)) .ToArray(); moreStringDtos.ShouldHaveSingleItem(); diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index 73679e415..fb1e92db4 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -64,7 +64,7 @@ protected void ProjectToComplexTypeCollectionMember(TOrmContext context) context.Rotas.Add(rota); context.SaveChanges(); - var rotaDto = context.Rotas.Where(r => r.Id == 1).ProjectTo().First(); + var rotaDto = context.Rotas.Where(r => r.Id == 1).Project().To().First(); rotaDto.Id.ShouldBe(1); rotaDto.StartDate.ShouldBe(rota.StartDate); @@ -116,7 +116,7 @@ protected void ProjectToComplexTypeEnumerableMember(TOrmContext context) context.Orders.Add(order); context.SaveChanges(); - var rotaDto = context.Orders.Where(r => r.Id == 1).ProjectTo().First(); + var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); rotaDto.Id.ShouldBe(1); rotaDto.DatePlaced.ShouldBe(order.DatePlaced); diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index d7e2756b1..fb7d42dd8 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -46,7 +46,7 @@ public void ShouldProjectAOneToOneRelationship() context.SaveChanges(); - var companyDto = context.Companies.ProjectTo().ShouldHaveSingleItem(); + var companyDto = context.Companies.Project().To().ShouldHaveSingleItem(); companyDto.Id.ShouldBe(company.Id); companyDto.Name.ShouldBe(company.Name); @@ -116,7 +116,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( var topLevelDto = context .Categories - .ProjectTo() + .Project().To() .OrderBy(c => c.Id) .First(c => c.Name == "Top Level"); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index bb703a250..10e139510 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -21,7 +21,7 @@ public void ShouldProjectAnIntOneToTrue() context.IntItems.Add(new PublicInt { Value = 1 }); context.SaveChanges(); - var boolItem = context.IntItems.ProjectTo().First(); + var boolItem = context.IntItems.Project().To().First(); boolItem.Value.ShouldBeTrue(); }); @@ -35,7 +35,7 @@ public void ShouldProjectAnIntZeroToFalse() context.IntItems.Add(new PublicInt { Value = 0 }); context.SaveChanges(); - var boolItem = context.IntItems.ProjectTo().First(); + var boolItem = context.IntItems.Project().To().First(); boolItem.Value.ShouldBeFalse(); }); @@ -49,7 +49,7 @@ public void ShouldProjectAStringTrueToTrue() context.StringItems.Add(new PublicString { Value = "true" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeTrue(); }); @@ -63,7 +63,7 @@ public void ShouldProjectAStringTrueToTrueIgnoringCase() context.StringItems.Add(new PublicString { Value = "tRuE" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeTrue(); }); @@ -77,7 +77,7 @@ public void ShouldProjectAStringOneToTrue() context.StringItems.Add(new PublicString { Value = "1" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeTrue(); }); @@ -91,7 +91,7 @@ public void ShouldProjectAStringFalseToFalse() context.StringItems.Add(new PublicString { Value = "false" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeFalse(); }); @@ -105,7 +105,7 @@ public void ShouldProjectAStringZeroToFalse() context.StringItems.Add(new PublicString { Value = "0" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeFalse(); }); @@ -119,7 +119,7 @@ public void ShouldProjectAStringNonBooleanValueToFalse() context.StringItems.Add(new PublicString { Value = "uokyujhygt" }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeFalse(); }); @@ -133,7 +133,7 @@ public void ShouldProjectAStringNullToFalse() context.StringItems.Add(new PublicString { Value = null }); context.SaveChanges(); - var boolItem = context.StringItems.ProjectTo().First(); + var boolItem = context.StringItems.Project().To().First(); boolItem.Value.ShouldBeFalse(); }); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index e1ac86db9..c8ad53387 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -28,7 +28,7 @@ private static void ProjectParseableStringToADateTime(TOrmContext context) context.StringItems.Add(new PublicString { Value = now.ToString("s") }); context.SaveChanges(); - var dateTimeItem = context.StringItems.ProjectTo().First(); + var dateTimeItem = context.StringItems.Project().To().First(); dateTimeItem.Value.ShouldBe(now, TimeSpan.FromSeconds(1)); } @@ -48,7 +48,7 @@ private static void ProjectANullStringToADateTime(TOrmContext context) context.StringItems.Add(new PublicString { Value = default(string) }); context.SaveChanges(); - var dateTimeItem = context.StringItems.ProjectTo().First(); + var dateTimeItem = context.StringItems.Project().To().First(); dateTimeItem.Value.ShouldBe(default(DateTime)); } @@ -68,7 +68,7 @@ private static void ProjectAnUnparseableStringToADateTime(TOrmContext context) context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); context.SaveChanges(); - var dateTimeItem = context.StringItems.ProjectTo().First(); + var dateTimeItem = context.StringItems.Project().To().First(); dateTimeItem.Value.ShouldBe(default(DateTime)); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs index 0a7d7fa2d..5f8654182 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -21,7 +21,7 @@ public void ShouldProjectAShortToADouble() context.ShortItems.Add(new PublicShort { Value = 123 }); context.SaveChanges(); - var doubleItem = context.ShortItems.ProjectTo().First(); + var doubleItem = context.ShortItems.Project().To().First(); doubleItem.Value.ShouldBe(123d); }); @@ -35,7 +35,7 @@ public void ShouldProjectALongToADouble() context.LongItems.Add(new PublicLong { Value = 12345L }); context.SaveChanges(); - var doubleItem = context.LongItems.ProjectTo().First(); + var doubleItem = context.LongItems.Project().To().First(); doubleItem.Value.ShouldBe(12345d); }); @@ -54,7 +54,7 @@ private static void ProjectParseableStringToDouble(TOrmContext context) context.StringItems.Add(new PublicString { Value = "738.01" }); context.SaveChanges(); - var doubleItem = context.StringItems.ProjectTo().First(); + var doubleItem = context.StringItems.Project().To().First(); doubleItem.Value.ShouldBe(738.01); } @@ -74,7 +74,7 @@ private static void ProjectNullStringToDouble(TOrmContext context) context.StringItems.Add(new PublicString { Value = default(string) }); context.SaveChanges(); - var doubleItem = context.StringItems.ProjectTo().First(); + var doubleItem = context.StringItems.Project().To().First(); doubleItem.Value.ShouldBe(default(double)); } @@ -95,7 +95,7 @@ private static void ProjectUnparseableStringToDouble(TOrmContext context) context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - context.StringItems.ProjectTo().First(); + context.StringItems.Project().To().First(); } #endregion diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index 5b944252b..dbe258630 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -28,7 +28,7 @@ private static void ProjectAParseableStringToAGuid(TOrmContext context) context.StringItems.Add(new PublicString { Value = guid.ToString() }); context.SaveChanges(); - var guidItem = context.StringItems.ProjectTo().First(); + var guidItem = context.StringItems.Project().To().First(); guidItem.Value.ShouldBe(guid); } @@ -48,7 +48,7 @@ private static void ProjectANullStringToAGuid(TOrmContext context) context.StringItems.Add(new PublicString { Value = default(string) }); context.SaveChanges(); - var guidItem = context.StringItems.ProjectTo().First(); + var guidItem = context.StringItems.Project().To().First(); guidItem.Value.ShouldBe(default(Guid)); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index e9360756b..60faa7e36 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -21,7 +21,7 @@ public void ShouldProjectAShortToAnInt() context.ShortItems.Add(new PublicShort { Value = 123 }); context.SaveChanges(); - var intItem = context.ShortItems.ProjectTo().First(); + var intItem = context.ShortItems.Project().To().First(); intItem.Value.ShouldBe(123); }); @@ -35,7 +35,7 @@ public void ShouldProjectAnInRangeLongToAnInt() context.LongItems.Add(new PublicLong { Value = 12345L }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.Project().To().First(); intItem.Value.ShouldBe(12345); }); @@ -49,7 +49,7 @@ public void ShouldProjectATooBigLongToAnInt() context.LongItems.Add(new PublicLong { Value = long.MaxValue }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.Project().To().First(); intItem.Value.ShouldBe(0); }); @@ -63,7 +63,7 @@ public void ShouldProjectATooSmallLongToAnInt() context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); context.SaveChanges(); - var intItem = context.LongItems.ProjectTo().First(); + var intItem = context.LongItems.Project().To().First(); intItem.Value.ShouldBe(0); }); @@ -82,7 +82,7 @@ private static void ProjectParseableStringToInt(TOrmContext context) context.StringItems.Add(new PublicString { Value = "738" }); context.SaveChanges(); - var intItem = context.StringItems.ProjectTo().First(); + var intItem = context.StringItems.Project().To().First(); intItem.Value.ShouldBe(738); } @@ -102,7 +102,7 @@ private static void ProjectNullStringToInt(TOrmContext context) context.StringItems.Add(new PublicString { Value = default(string) }); context.SaveChanges(); - var intItem = context.StringItems.ProjectTo().First(); + var intItem = context.StringItems.Project().To().First(); intItem.Value.ShouldBe(default(int)); } @@ -123,7 +123,7 @@ private static void ProjectUnparseableStringToInt(TOrmContext context) context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - context.StringItems.ProjectTo().First(); + context.StringItems.Project().To().First(); } #endregion diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index b2d542c65..50e70b5ee 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -21,7 +21,7 @@ public void ShouldProjectAnIntToAString() context.IntItems.Add(new PublicInt { Value = 763483 }); context.SaveChanges(); - var stringItem = context.IntItems.ProjectTo().First(); + var stringItem = context.IntItems.Project().To().First(); stringItem.Value.ShouldBe("763483"); }); @@ -35,7 +35,7 @@ public void ShouldProjectABoolToAString() context.BoolItems.Add(new PublicBool { Value = true }); context.SaveChanges(); - var stringItem = context.BoolItems.ProjectTo().First(); + var stringItem = context.BoolItems.Project().To().First(); stringItem.Value.ShouldBe("true"); }); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 0e6b98280..49ba548b3 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -27,7 +27,7 @@ public void ShouldProjectAFlatTypeToAnArray() var productDtos = context .Products - .ProjectTo() + .Project().To() .OrderBy(p => p.ProductId) .ToArray(); @@ -51,7 +51,7 @@ public void ShouldProjectAFlatTypeToANonMatchingTypeList() context.Products.Add(product); context.SaveChanges(); - var productDtos = context.Products.ProjectTo().ToList(); + var productDtos = context.Products.Project().To().ToList(); productDtos.ShouldHaveSingleItem(); productDtos[0].Id.ShouldBe(product.ProductId); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 733e85ab7..29aec3461 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -31,7 +31,7 @@ public void ShouldProjectToAComplexTypeMember() context.Persons.Add(person); context.SaveChanges(); - var personDto = context.Persons.ProjectTo().First(); + var personDto = context.Persons.Project().To().First(); personDto.Id.ShouldBe(person.PersonId); personDto.Name.ShouldBe("Test Db"); diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index efc502b7c..2792388b1 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -34,7 +34,7 @@ public void ShouldCreateAQueryProjectionPlanForASpecificQueryProvider() cachedMapper.MapperData.TargetType.ShouldBe(typeof(IQueryable)); // Trigger a mapping: - Context.Products.ProjectTo().ShouldBeEmpty(); + Context.Products.Project().To().ShouldBeEmpty(); var usedMapper = (IObjectMapper)mapper.RootMapperCountShouldBeOne(); diff --git a/AgileMapper/Api/ProjectionResultSpecifier.cs b/AgileMapper/Api/ProjectionResultSpecifier.cs new file mode 100644 index 000000000..ad1ab13c5 --- /dev/null +++ b/AgileMapper/Api/ProjectionResultSpecifier.cs @@ -0,0 +1,82 @@ +namespace AgileObjects.AgileMapper.Api +{ + using System; + using System.Linq; + using ObjectPopulation; + using Queryables.Api; + + /// + /// Provides options for specifying the query projection result Type. + /// + /// + /// The Type of object contained in the source IQueryable{T} which should be projected + /// to a result Type. + /// + public class ProjectionResultSpecifier + { + private readonly IQueryable _sourceQueryable; + + internal ProjectionResultSpecifier(IQueryable sourceQueryable) + { + _sourceQueryable = sourceQueryable; + } + + /// + /// Project the elements of the source IQueryable{T} to instances of the given + /// , using the default mapper. + /// + /// + /// The target Type to which the elements of the source IQueryable{T} should be projected. + /// + /// + /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given + /// . The projection is not performed until the Queryable is + /// enumerated by a call to .ToArray() or similar. + /// + public IQueryable To() + where TResultElement : class + { + return ProjectQuery(Mapper.Default); + } + + /// + /// Project the elements of the source IQueryable{T} to instances of the given + /// , using the mapper specified by the given + /// . + /// + /// A func providing the mapper with which the projection should be performed. + /// + /// The target Type to which the elements of the source IQueryable{T} should be projected. + /// + /// + /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given + /// . The projection is not performed until the Queryable is + /// enumerated by a call to .ToArray() or similar. + /// + public IQueryable To(Func mapperSelector) + { + var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); + + return ProjectQuery(mapper); + } + + //public static IQueryable To( + // Expression>> configuration) + //{ + + //} + + private IQueryable ProjectQuery(IMapper mapper) + { + var mapperContext = ((IMapperInternal)mapper).Context; + + var rootMappingData = ObjectMappingDataFactory.ForProjection( + _sourceQueryable, + mapperContext.QueryProjectionMappingContext); + + var queryProjection = rootMappingData.MapStart(); + + return queryProjection; + } + } +} diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 463b67f98..141f69b9b 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -1,12 +1,7 @@ namespace AgileObjects.AgileMapper { - using System; using System.Linq; - using System.Linq.Expressions; - using Extensions.Internal; - using NetStandardPolyfills; - using ObjectPopulation; - using Queryables.Api; + using Api; /// /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. @@ -14,92 +9,16 @@ public static class ProjectionExtensions { /// - /// Project the elements of the given to instances of the given - /// . + /// Project the elements of the given to instances of a specified + /// result Type. The projection operation is performed entirely on the data source. /// - /// The source collection on which to perform the projection. - /// - /// The target Type to which the elements of the given should be projected. - /// - /// - /// An IQueryable of the given to instances of the given - /// . The projection is not performed until the Queryable is enumerated - /// by a call to .ToArray() or similar. - /// - public static IQueryable ProjectTo(this IQueryable sourceQueryable) - where TResultElement : class + /// The Type of the elements to project to a new result Type. + /// The source IQueryable{T} on which to perform the projection. + /// A ProjectionResultSpecifier with which to specify the type of query projection to perform. + public static ProjectionResultSpecifier Project( + this IQueryable sourceQueryable) { - return ProjectTo(sourceQueryable, Mapper.Default); - } - - /// - /// Project the elements of the given to instances of the given - /// . - /// - /// The source collection on which to perform the projection. - /// A func providing the mapper with which the projection should be performed. - /// - /// The target Type to which the elements of the given should be projected. - /// - /// - /// An IQueryable of the given to instances of the given - /// . The projection is not performed until the Queryable is enumerated - /// by a call to .ToArray() or similar. - /// - public static IQueryable ProjectTo( - this IQueryable sourceQueryable, - Func mapperSelector) - where TResultElement : class - { - var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); - - return ProjectTo(sourceQueryable, mapper); - } - - private static IQueryable ProjectTo( - IQueryable sourceQueryable, - IMapper mapper) - where TResultElement : class - { - var projectCaller = GlobalContext.Instance.Cache.GetOrAdd( - new SourceAndTargetTypesKey(sourceQueryable.ElementType, typeof(TResultElement)), - key => - { - var projectQueryMethod = typeof(ProjectionExtensions) - .GetNonPublicStaticMethod("ProjectQuery") - .MakeGenericMethod(key.SourceType, key.TargetType); - - var typedSourceQueryable = typeof(IQueryable<>).MakeGenericType(key.SourceType); - - var projectQueryCall = Expression.Call( - projectQueryMethod, - Parameters.Queryable.GetConversionTo(typedSourceQueryable), - Parameters.Mapper); - - var projectQueryLambda = Expression.Lambda>>( - projectQueryCall, - Parameters.Queryable, - Parameters.Mapper); - - return projectQueryLambda.Compile(); - }); - - return projectCaller.Invoke(sourceQueryable, mapper); - } - - internal static IQueryable ProjectQuery( - IQueryable sourceQueryable, - IMapper mapper) - { - var mapperContext = ((IMapperInternal)mapper).Context; - - var rootMappingData = ObjectMappingDataFactory.ForProjection( - sourceQueryable, - mapperContext.QueryProjectionMappingContext); - - var queryProjection = rootMappingData.MapStart(); - - return queryProjection; + return new ProjectionResultSpecifier(sourceQueryable); } } } From aaf7dbb6f2062e08d582c855b5e4151c5697ebf7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 11:49:12 +0000 Subject: [PATCH 095/176] Start of support for inline query projection configuration --- .../Api/Configuration/MappingConfigurator.cs | 4 +- .../IFullProjectionInlineConfigurator.cs | 11 +++ ...ifier.cs => IProjectionResultSpecifier.cs} | 72 ++++++++----------- .../Configuration/Inline/IInlineMapperKey.cs | 3 + .../Inline/InlineMapperContextSet.cs | 29 +++++++- .../Configuration/Inline/InlineMapperKey.cs | 26 ++++--- .../Inline/InlineMappingConfigurator.cs | 24 +++++-- AgileMapper/ProjectionExecutor.cs | 67 +++++++++++++++++ AgileMapper/ProjectionExtensions.cs | 6 +- 9 files changed, 180 insertions(+), 62 deletions(-) create mode 100644 AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs rename AgileMapper/Api/{ProjectionResultSpecifier.cs => IProjectionResultSpecifier.cs} (50%) create mode 100644 AgileMapper/ProjectionExecutor.cs diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 65eb7ea12..a093207cd 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -8,11 +8,13 @@ using Dynamics; using Extensions.Internal; using Members; + using Projection; using Validation; internal class MappingConfigurator : IFullMappingInlineConfigurator, - IConditionalRootMappingConfigurator + IConditionalRootMappingConfigurator, + IFullProjectionInlineConfigurator { public MappingConfigurator(MappingConfigInfo configInfo) { diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs new file mode 100644 index 000000000..4ffe43fc7 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for configuring query projections from and to given source and target element Types, inline. + /// + /// The source element Type to which the configuration should apply. + /// The result element Type to which the configuration should apply. + public interface IFullProjectionInlineConfigurator + { + } +} diff --git a/AgileMapper/Api/ProjectionResultSpecifier.cs b/AgileMapper/Api/IProjectionResultSpecifier.cs similarity index 50% rename from AgileMapper/Api/ProjectionResultSpecifier.cs rename to AgileMapper/Api/IProjectionResultSpecifier.cs index ad1ab13c5..eb12f5270 100644 --- a/AgileMapper/Api/ProjectionResultSpecifier.cs +++ b/AgileMapper/Api/IProjectionResultSpecifier.cs @@ -2,7 +2,8 @@ { using System; using System.Linq; - using ObjectPopulation; + using System.Linq.Expressions; + using Configuration.Projection; using Queryables.Api; /// @@ -12,32 +13,22 @@ /// The Type of object contained in the source IQueryable{T} which should be projected /// to a result Type. /// - public class ProjectionResultSpecifier + public interface IProjectionResultSpecifier { - private readonly IQueryable _sourceQueryable; - - internal ProjectionResultSpecifier(IQueryable sourceQueryable) - { - _sourceQueryable = sourceQueryable; - } - /// /// Project the elements of the source IQueryable{T} to instances of the given /// , using the default mapper. /// /// - /// The target Type to which the elements of the source IQueryable{T} should be projected. + /// The result Type to which the elements of the source IQueryable{T} should be projected. /// /// /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given /// . The projection is not performed until the Queryable is /// enumerated by a call to .ToArray() or similar. /// - public IQueryable To() - where TResultElement : class - { - return ProjectQuery(Mapper.Default); - } + IQueryable To() + where TResultElement : class; /// /// Project the elements of the source IQueryable{T} to instances of the given @@ -46,37 +37,36 @@ public IQueryable To() /// /// A func providing the mapper with which the projection should be performed. /// - /// The target Type to which the elements of the source IQueryable{T} should be projected. + /// The result Type to which the elements of the source IQueryable{T} should be projected. /// /// /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given /// . The projection is not performed until the Queryable is /// enumerated by a call to .ToArray() or similar. /// - public IQueryable To(Func mapperSelector) - { - var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); - - return ProjectQuery(mapper); - } - - //public static IQueryable To( - // Expression>> configuration) - //{ - - //} + IQueryable To(Func mapperSelector) + where TResultElement : class; - private IQueryable ProjectQuery(IMapper mapper) - { - var mapperContext = ((IMapperInternal)mapper).Context; - - var rootMappingData = ObjectMappingDataFactory.ForProjection( - _sourceQueryable, - mapperContext.QueryProjectionMappingContext); - - var queryProjection = rootMappingData.MapStart(); - - return queryProjection; - } + /// + /// Project the elements of the source IQueryable{T} to instances of the given + /// , using the default mapper and the given + /// . + /// + /// + /// The result Type to which the elements of the source IQueryable{T} should be projected. + /// + /// + /// An inline query projection configuration. If non-null, the query projection will be configured + /// by combining this inline with any applicable configuration + /// already set up via the Mapper.WhenMapping API. + /// + /// + /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given + /// . The projection is not performed until the Queryable is + /// enumerated by a call to .ToArray() or similar. + /// + IQueryable To( + Expression>> configuration) + where TResultElement : class; } -} +} \ No newline at end of file diff --git a/AgileMapper/Configuration/Inline/IInlineMapperKey.cs b/AgileMapper/Configuration/Inline/IInlineMapperKey.cs index 49135fcde..46319ecb7 100644 --- a/AgileMapper/Configuration/Inline/IInlineMapperKey.cs +++ b/AgileMapper/Configuration/Inline/IInlineMapperKey.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Configuration.Inline { + using System; using System.Collections.Generic; using System.Linq.Expressions; using Members; @@ -10,6 +11,8 @@ internal interface IInlineMapperKey MappingRuleSet RuleSet { get; } + Type ConfiguratorType { get; } + IList Configurations { get; } MapperContext CreateInlineMapperContext(); diff --git a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs index c2a578a0f..337d17215 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs @@ -5,22 +5,49 @@ using System.Collections.Generic; using System.Linq.Expressions; using Api.Configuration; + using Api.Configuration.Projection; using Caching; internal class InlineMapperContextSet : IEnumerable { private readonly ICache _inlineContextsCache; + private readonly IMappingContext _queryProjectionMappingContext; public InlineMapperContextSet(MapperContext parentMapperContext) { _inlineContextsCache = parentMapperContext.Cache.CreateScoped(); + _queryProjectionMappingContext = parentMapperContext.QueryProjectionMappingContext; } public MapperContext GetContextFor( Expression>>[] configurations, MappingExecutor executor) { - var key = new InlineMapperKey(configurations, executor); + return GetContextFor>( + configurations, + configInfo => new MappingConfigurator(configInfo), + executor); + } + + public MapperContext GetContextFor( + Expression>>[] configurations, + ProjectionExecutor executor) + { + return GetContextFor>( + configurations, + configInfo => new MappingConfigurator(configInfo), + _queryProjectionMappingContext); + } + + private MapperContext GetContextFor( + Expression>[] configurations, + Func configuratorFactory, + IMappingContext mappingContext) + { + var key = new InlineMapperKey( + configurations, + configuratorFactory, + mappingContext); var inlineMapperContext = _inlineContextsCache.GetOrAdd( key, diff --git a/AgileMapper/Configuration/Inline/InlineMapperKey.cs b/AgileMapper/Configuration/Inline/InlineMapperKey.cs index ba15f0b83..c77b9b502 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperKey.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperKey.cs @@ -3,26 +3,30 @@ namespace AgileObjects.AgileMapper.Configuration.Inline using System; using System.Collections.Generic; using System.Linq.Expressions; - using Api.Configuration; using Extensions.Internal; using Members; - internal class InlineMapperKey : IInlineMapperKey + internal class InlineMapperKey : IInlineMapperKey { - private readonly Expression>>[] _configurations; - private readonly MappingExecutor _executor; + private readonly Expression>[] _configurations; + private readonly Func _configuratorFactory; + private readonly IMappingContext _mappingContext; public InlineMapperKey( - Expression>>[] configurations, - MappingExecutor executor) + Expression>[] configurations, + Func configuratorFactory, + IMappingContext mappingContext) { _configurations = configurations; - _executor = executor; + _configuratorFactory = configuratorFactory; + _mappingContext = mappingContext; } public MappingTypes MappingTypes => MappingTypes.Fixed; - public MappingRuleSet RuleSet => _executor.RuleSet; + public MappingRuleSet RuleSet => _mappingContext.RuleSet; + + public Type ConfiguratorType => typeof(TConfigurator); // ReSharper disable once CoVariantArrayConversion public IList Configurations => _configurations; @@ -30,7 +34,10 @@ public InlineMapperKey( public MapperContext CreateInlineMapperContext() { return InlineMappingConfigurator - .ConfigureInlineMapperContext(_configurations, _executor); + .ConfigureInlineMapperContext( + _configurations, + _configuratorFactory, + _mappingContext); } public override bool Equals(object obj) @@ -39,6 +46,7 @@ public override bool Equals(object obj) // ReSharper disable once PossibleNullReferenceException if ((_configurations.Length != otherKey.Configurations.Count) || + (ConfiguratorType != otherKey.ConfiguratorType) || (RuleSet != otherKey.RuleSet) || !MappingTypes.Equals(otherKey.MappingTypes)) { diff --git a/AgileMapper/Configuration/Inline/InlineMappingConfigurator.cs b/AgileMapper/Configuration/Inline/InlineMappingConfigurator.cs index 103a2f6d4..7da8de485 100644 --- a/AgileMapper/Configuration/Inline/InlineMappingConfigurator.cs +++ b/AgileMapper/Configuration/Inline/InlineMappingConfigurator.cs @@ -7,24 +7,34 @@ namespace AgileObjects.AgileMapper.Configuration.Inline internal static class InlineMappingConfigurator { - public static MapperContext ConfigureInlineMapperContext( - IEnumerable>>> configurations, + public static MapperContext ConfigureInlineMapperContext( + IEnumerable>> configurations, + Func configuratorFactory, IMappingContext mappingContext) { var inlineMapperContext = mappingContext.MapperContext.Clone(); - return ConfigureMapperContext(configurations, mappingContext.RuleSet, inlineMapperContext); + return ConfigureMapperContext( + configurations, + configuratorFactory, + mappingContext.RuleSet, + inlineMapperContext); } public static MapperContext ConfigureMapperContext( IEnumerable>>> configurations, IMappingContext mappingContext) { - return ConfigureMapperContext(configurations, mappingContext.RuleSet, mappingContext.MapperContext); + return ConfigureMapperContext( + configurations, + configInfo => new MappingConfigurator(configInfo), + mappingContext.RuleSet, + mappingContext.MapperContext); } - private static MapperContext ConfigureMapperContext( - IEnumerable>>> configurations, + private static MapperContext ConfigureMapperContext( + IEnumerable>> configurations, + Func configuratorFactory, MappingRuleSet ruleSet, MapperContext mapperContext) { @@ -33,7 +43,7 @@ private static MapperContext ConfigureMapperContext( .ForSourceType() .ForTargetType(); - var configurator = new MappingConfigurator(configInfo); + var configurator = configuratorFactory.Invoke(configInfo); foreach (var configuration in configurations) { diff --git a/AgileMapper/ProjectionExecutor.cs b/AgileMapper/ProjectionExecutor.cs new file mode 100644 index 000000000..806d65273 --- /dev/null +++ b/AgileMapper/ProjectionExecutor.cs @@ -0,0 +1,67 @@ +namespace AgileObjects.AgileMapper +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using Api; + using Api.Configuration.Projection; + using ObjectPopulation; + using Queryables.Api; + + internal class ProjectionExecutor : IProjectionResultSpecifier + { + private readonly IQueryable _sourceQueryable; + + public ProjectionExecutor(IQueryable sourceQueryable) + { + _sourceQueryable = sourceQueryable; + } + + #region To Overloads + + IQueryable IProjectionResultSpecifier.To() + { + return PerformProjection(Mapper.Default); + } + + IQueryable IProjectionResultSpecifier.To( + Func mapperSelector) + { + var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); + + return PerformProjection(mapper); + } + + IQueryable IProjectionResultSpecifier.To( + Expression>> configuration) + { + return PerformProjection(Mapper.Default, new[] { configuration }); + } + + #endregion + + private IQueryable PerformProjection(IMapper mapper) + => PerformProjection(((IMapperInternal)mapper).Context); + + private IQueryable PerformProjection( + IMapper mapper, + Expression>>[] configurations) + { + var mapperContext = ((IMapperInternal)mapper).Context; + var inlineMapperContext = mapperContext.InlineContexts.GetContextFor(configurations, this); + + return PerformProjection(inlineMapperContext); + } + + private IQueryable PerformProjection(MapperContext mapperContext) + { + var rootMappingData = ObjectMappingDataFactory.ForProjection( + _sourceQueryable, + mapperContext.QueryProjectionMappingContext); + + var queryProjection = rootMappingData.MapStart(); + + return queryProjection; + } + } +} diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index 141f69b9b..c578d7983 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -14,11 +14,11 @@ public static class ProjectionExtensions /// /// The Type of the elements to project to a new result Type. /// The source IQueryable{T} on which to perform the projection. - /// A ProjectionResultSpecifier with which to specify the type of query projection to perform. - public static ProjectionResultSpecifier Project( + /// An IProjectionResultSpecifier with which to specify the type of query projection to perform. + public static IProjectionResultSpecifier Project( this IQueryable sourceQueryable) { - return new ProjectionResultSpecifier(sourceQueryable); + return new ProjectionExecutor(sourceQueryable); } } } From 0d4bb7327dfe7d7378c04817bc7e872f735dd2ef Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 15:41:41 +0000 Subject: [PATCH 096/176] Fixing conversion of GetValueOrDefault() calls for EF5 + EF6 / Start of support for depth-specific recursive member projection --- .../WhenProjectingCircularReferences.cs | 4 +- .../WhenProjectingCircularReferences.cs | 4 +- .../WhenProjectingCircularReferences.cs | 4 +- .../WhenProjectingCircularReferences.cs | 8 ++-- ...OneToManyRecursionProjectionFailureTest.cs | 2 +- .../IOneToManyRecursionProjectorTest.cs | 2 +- .../WhenProjectingCircularReferences.cs | 22 +++++------ .../TestClasses/Person.cs | 2 +- .../WhenProjectingToComplexTypeMembers.cs | 20 ++++++++++ .../Api/Configuration/MappingConfigurator.cs | 4 +- .../IFullProjectionInlineConfigurator.cs | 10 +++++ .../Projection/ProjectionConfigurator.cs | 25 +++++++++++++ .../Inline/InlineMapperContextSet.cs | 2 +- .../Projection/RecursionDepthSettings.cs | 37 +++++++++++++++++++ .../Configuration/UserConfigurationSet.cs | 24 ++++++++++++ .../MappingDataCreationFactory.cs | 2 + .../ObjectPopulation/MappingFactory.cs | 2 +- .../Converters/DefaultExpressionConverter.cs | 8 ++-- ...apToDepthRecursiveMemberMappingStrategy.cs | 30 +++++++++++++++ .../Settings/DefaultQueryProviderSettings.cs | 10 ++++- 20 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs create mode 100644 AgileMapper/Configuration/Projection/RecursionDepthSettings.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs index 42629bf59..166aabd49 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs @@ -14,7 +14,7 @@ public WhenProjectingCircularReferences(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() - => DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); + public void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + => DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs index 5cdff0b45..92983117f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs @@ -14,7 +14,7 @@ public WhenProjectingCircularReferences(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() - => DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); + public void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + => DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs index f1c7372e3..e587fa64f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs @@ -14,7 +14,7 @@ public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs index 64459c9d9..652ff3dff 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs @@ -14,11 +14,11 @@ public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); [Fact] - public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); + public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs index 781d7b0eb..6996646fc 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs @@ -2,6 +2,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion { public interface IOneToManyRecursionProjectionFailureTest { - void ShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth(); + void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs index 4a76ca865..e0c4bbad1 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs @@ -2,6 +2,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion { public interface IOneToManyRecursionProjectorTest { - void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index fb7d42dd8..a296bc73e 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -68,14 +68,14 @@ public void ShouldProjectAOneToOneRelationship() #region Project One-to-Many - protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() - => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); + protected void DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); - protected void DoShouldErrorProjectingAOneToManyRelationshipToFirstRecursionDepth() - => RunTestAndExpectThrow(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); + protected void DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + => RunTestAndExpectThrow(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); - protected void DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() - => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(2, context)); + protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); protected void ProjectAOneToManyRelationshipToRecursionDepth( int depth, @@ -90,7 +90,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( context.Categories.Add(topLevel); - if (depth > 1) + if (depth > 0) { topLevel.SubCategories.First().AddSubCategories( new Category { Name = "Top > One > One" }, @@ -104,7 +104,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( topLevel.SubCategories.Third().AddSubCategories( new Category { Name = "Top > Three > One" }); - if (depth > 2) + if (depth > 1) { topLevel.SubCategories.Second().SubCategories.Second().AddSubCategories( new Category { Name = "Top > Two > Two > One" }, @@ -116,7 +116,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( var topLevelDto = context .Categories - .Project().To() + .Project().To(cfg => cfg.RecurseToDepth(depth)) .OrderBy(c => c.Id) .First(c => c.Name == "Top Level"); @@ -136,7 +136,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( Verify(depth1Dtos.Second(), child2); Verify(depth1Dtos.Third(), child3); - if (!(depth > 1)) + if (!(depth > 0)) { depth1Dtos.First().SubCategories.ShouldBeEmpty(); depth1Dtos.Second().SubCategories.ShouldBeEmpty(); @@ -164,7 +164,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( Verify(depth13Dtos.First(), child3.SubCategories.First()); - if (!(depth > 2)) + if (!(depth > 1)) { depth11Dtos.First().SubCategories.ShouldBeEmpty(); depth11Dtos.Second().SubCategories.ShouldBeEmpty(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Person.cs b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs index baa0a828a..622478fbb 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Person.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs @@ -9,7 +9,7 @@ public class Person public string Name { get; set; } - public int AddressId { get; set; } + public int? AddressId { get; set; } public Address Address { get; set; } } diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 29aec3461..414633fbe 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -40,5 +40,25 @@ public void ShouldProjectToAComplexTypeMember() personDto.AddressLine2.ShouldBe("Test Db Line 2"); }); } + + [Fact] + public void ShouldHandleANullComplexTypeMember() + { + RunTest(context => + { + var person = new Person { Name = "No Address!" }; + + context.Persons.Add(person); + context.SaveChanges(); + + var personDto = context.Persons.Project().To().First(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("No Address!"); + personDto.AddressId.ShouldBeDefault(); + personDto.AddressLine1.ShouldBeNull(); + personDto.AddressLine2.ShouldBeNull(); + }); + } } } diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index a093207cd..65eb7ea12 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -8,13 +8,11 @@ using Dynamics; using Extensions.Internal; using Members; - using Projection; using Validation; internal class MappingConfigurator : IFullMappingInlineConfigurator, - IConditionalRootMappingConfigurator, - IFullProjectionInlineConfigurator + IConditionalRootMappingConfigurator { public MappingConfigurator(MappingConfigInfo configInfo) { diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index 4ffe43fc7..c5b32ef71 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -7,5 +7,15 @@ /// The result element Type to which the configuration should apply. public interface IFullProjectionInlineConfigurator { + /// + /// Project recursive relationships to the specified ; default is 1. + /// For example, when projecting a Category entity which has a SubCategories property of Type + /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories + /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the + /// sub-categories of the sub-categories of the top-level Category selected, etc. A recursion depth of + /// zero will only populate the first level of sub-categories. + /// + /// The depth to which to populate projected recursive relationships. + IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); } } diff --git a/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs new file mode 100644 index 000000000..3b58aa55c --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs @@ -0,0 +1,25 @@ +using AgileObjects.AgileMapper.Configuration; + +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + using AgileMapper.Configuration.Projection; + + internal class ProjectionConfigurator + : IFullProjectionInlineConfigurator + { + private readonly MappingConfigInfo _configInfo; + + public ProjectionConfigurator(MappingConfigInfo configInfo) + { + _configInfo = configInfo; + } + + public IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth) + { + var depthSettings = new RecursionDepthSettings(_configInfo, recursionDepth); + + _configInfo.MapperContext.UserConfigurations.Add(depthSettings); + return this; + } + } +} diff --git a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs index 337d17215..e7a5a3dcb 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs @@ -35,7 +35,7 @@ public MapperContext GetContextFor( { return GetContextFor>( configurations, - configInfo => new MappingConfigurator(configInfo), + configInfo => new ProjectionConfigurator(configInfo), _queryProjectionMappingContext); } diff --git a/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs b/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs new file mode 100644 index 000000000..b81ce6f0d --- /dev/null +++ b/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs @@ -0,0 +1,37 @@ +namespace AgileObjects.AgileMapper.Configuration.Projection +{ + using Members; + + internal class RecursionDepthSettings : UserConfiguredItemBase + { + private readonly int _recursionDepth; + + public RecursionDepthSettings(MappingConfigInfo configInfo, int recursionDepth) + : base(configInfo) + { + _recursionDepth = recursionDepth; + } + + public bool IsWithinDepth(IBasicMapperData mapperData) + { + if (_recursionDepth == 0) + { + return false; + } + + var recursionDepth = -1; + + while (mapperData != null) + { + if (mapperData.TargetMember.IsRecursion) + { + ++recursionDepth; + } + + mapperData = mapperData.Parent; + } + + return recursionDepth <= _recursionDepth; + } + } +} diff --git a/AgileMapper/Configuration/UserConfigurationSet.cs b/AgileMapper/Configuration/UserConfigurationSet.cs index a6ed1561a..5f6d277ca 100644 --- a/AgileMapper/Configuration/UserConfigurationSet.cs +++ b/AgileMapper/Configuration/UserConfigurationSet.cs @@ -9,6 +9,7 @@ using Extensions.Internal; using Members; using ObjectPopulation; + using Projection; internal class UserConfigurationSet { @@ -26,6 +27,7 @@ internal class UserConfigurationSet private List _creationCallbackFactories; private List _exceptionCallbackFactories; private DerivedTypePairSet _derivedTypes; + private List _recursionDepthSettings; public UserConfigurationSet(MapperContext mapperContext) { @@ -226,6 +228,28 @@ public Expression GetExceptionCallbackOrNull(IBasicMapperData mapperData) public DerivedTypePairSet DerivedTypes => _derivedTypes ?? (_derivedTypes = new DerivedTypePairSet()); + #region RecursionDepthSettings + + private List RecursionDepthSettings + => _recursionDepthSettings ?? (_recursionDepthSettings = new List()); + + public void Add(RecursionDepthSettings settings) + { + RecursionDepthSettings.Add(settings); + } + + public bool ShortCircuitRecursion(IBasicMapperData mapperData) + { + if (_recursionDepthSettings == null) + { + return true; + } + + return RecursionDepthSettings.FindMatch(mapperData)?.IsWithinDepth(mapperData) != true; + } + + #endregion + #region Validation internal void ThrowIfMemberIsUnmappable(ConfiguredIgnoredMember ignoredMember) diff --git a/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs b/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs index 9ff279caa..28bd05396 100644 --- a/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingDataCreationFactory.cs @@ -7,6 +7,7 @@ internal static class MappingDataCreationFactory { + [DebuggerStepThrough] public static Expression ForDerivedType(ObjectMapperData childMapperData) { UseAsConversion(childMapperData, out var asConversion); @@ -14,6 +15,7 @@ public static Expression ForDerivedType(ObjectMapperData childMapperData) return asConversion; } + [DebuggerStepThrough] private static bool UseAsConversion(ObjectMapperData childMapperData, out Expression conversion) { if (childMapperData.Context.IsStandalone) diff --git a/AgileMapper/ObjectPopulation/MappingFactory.cs b/AgileMapper/ObjectPopulation/MappingFactory.cs index f8f38a414..d86979534 100644 --- a/AgileMapper/ObjectPopulation/MappingFactory.cs +++ b/AgileMapper/ObjectPopulation/MappingFactory.cs @@ -172,7 +172,7 @@ public static Expression GetInlineMappingBlock( createMappingDataCall); } - public static Expression GetDirectAccessMapping( + private static Expression GetDirectAccessMapping( Expression mapping, ObjectMapperData mapperData, MappingValues mappingValues, diff --git a/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs b/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs index c85e5eb3d..05c546256 100644 --- a/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs @@ -2,11 +2,15 @@ { using System; using System.Linq.Expressions; + using System.Reflection; using Extensions.Internal; using NetStandardPolyfills; internal static class DefaultExpressionConverter { + private static readonly MethodInfo _getDefaultValueMethod = typeof(DefaultExpressionConverter) + .GetNonPublicStaticMethod("GetDefaultValue"); + public static Expression Convert(Expression defaultExpression) => Convert((DefaultExpression)defaultExpression); @@ -23,9 +27,7 @@ private static object GetDefaultValueFor(Type type) var getDefaultValueCaller = GlobalContext.Instance.Cache.GetOrAdd(type, t => { var getDefaultValueCall = Expression - .Call(typeof(DefaultExpressionConverter) - .GetNonPublicStaticMethod("GetDefaultValue") - .MakeGenericMethod(t)) + .Call(_getDefaultValueMethod.MakeGenericMethod(t)) .GetConversionToObject(); var getDefaultValueLambda = Expression.Lambda>(getDefaultValueCall); diff --git a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs index 26fac7f75..2bd8a2692 100644 --- a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs +++ b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables.Recursion { using System.Linq.Expressions; + using Extensions.Internal; using ObjectPopulation; using ObjectPopulation.Recursion; @@ -11,6 +12,35 @@ public Expression GetMapRecursionCallFor( Expression sourceValue, int dataSourceIndex, ObjectMapperData declaredTypeMapperData) + { + if (ShortCircuitRecursion(childMappingData)) + { + return GetRecursionShortCircuit(childMappingData); + } + + var mappingValues = new MappingValues( + sourceValue, + childMappingData.MapperData.TargetMember.Type.ToDefaultExpression(), + declaredTypeMapperData.EnumerableIndex); + + var inlineMappingBlock = MappingFactory.GetInlineMappingBlock( + childMappingData, + mappingValues, + MappingDataCreationFactory.ForChild(mappingValues, 0, childMappingData.MapperData)); + + return inlineMappingBlock; + } + + private static bool ShortCircuitRecursion(IObjectMappingData childMappingData) + { + return childMappingData + .MappingContext + .MapperContext + .UserConfigurations + .ShortCircuitRecursion(childMappingData.MapperData); + } + + private static Expression GetRecursionShortCircuit(IObjectMappingData childMappingData) { if (childMappingData.MapperData.TargetMember.IsComplex) { diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index fdf84c52d..0f13ad777 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -39,7 +39,15 @@ public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); public Expression ConvertGetValueOrDefaultCall(MethodCallExpression call) - => Expression.Convert(call.Object, call.Type); + { + var valueIsNotNull = call.Object.GetIsNotDefaultComparison(); + + // ReSharper disable once AssignNullToNotNullAttribute + var castValue = Expression.Convert(call.Object, call.Type); + var fallbackValue = DefaultExpressionConverter.Convert(call.Type.ToDefaultExpression()); + + return Expression.Condition(valueIsNotNull, castValue, fallbackValue); + } public Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) { From c9f80b9d21abce3cb512b5608182d889007d0adf Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 18:31:22 +0000 Subject: [PATCH 097/176] Updating project-to-complex-type-member ORM tests to actually project to a complex type member / Adding in-query null checks to nested complex type members --- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../TestClasses/PersonDto.cs | 6 +-- .../TestClasses/PersonViewModel.cs | 13 +++++++ .../WhenProjectingToComplexTypeMembers.cs | 11 +++--- .../WhenViewingMappingPlans.cs | 4 +- .../Configuration/MappingConfigInfo.cs | 4 +- AgileMapper/DataSources/DataSourceBase.cs | 38 ++++++++++--------- AgileMapper/MappingRuleSetCollection.cs | 7 ++-- AgileMapper/MappingRuleSetSettings.cs | 5 ++- AgileMapper/Members/ExpressionInfoFinder.cs | 25 ++++++------ .../Members/MemberMapperDataExtensions.cs | 18 ++++----- 11 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 2d1ca2bf7..64661d9bf 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -107,6 +107,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs index 0dbe02fe5..6a573624c 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PersonDto.cs @@ -6,10 +6,6 @@ public class PersonDto public string Name { get; set; } - public int AddressId { get; set; } - - public string AddressLine1 { get; set; } - - public string AddressLine2 { get; set; } + public AddressDto Address { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs new file mode 100644 index 000000000..eba387399 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class PersonViewModel + { + public int Id { get; set; } + + public string Name { get; set; } + + public string AddressLine1 { get; set; } + + public string AddressLine2 { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 414633fbe..d1ed66ed5 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -35,9 +35,10 @@ public void ShouldProjectToAComplexTypeMember() personDto.Id.ShouldBe(person.PersonId); personDto.Name.ShouldBe("Test Db"); - personDto.AddressId.ShouldBe(person.Address.AddressId); - personDto.AddressLine1.ShouldBe("Test Db Line 1"); - personDto.AddressLine2.ShouldBe("Test Db Line 2"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Id.ShouldBe(person.Address.AddressId); + personDto.Address.Line1.ShouldBe("Test Db Line 1"); + personDto.Address.Line2.ShouldBe("Test Db Line 2"); }); } @@ -55,9 +56,7 @@ public void ShouldHandleANullComplexTypeMember() personDto.Id.ShouldBe(person.PersonId); personDto.Name.ShouldBe("No Address!"); - personDto.AddressId.ShouldBeDefault(); - personDto.AddressLine1.ShouldBeNull(); - personDto.AddressLine2.ShouldBeNull(); + personDto.Address.ShouldBeNull(); }); } } diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index 2792388b1..4bbc5ba63 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -49,13 +49,13 @@ public void ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() { mapper.GetPlanForProjecting(Context.Products).To(); mapper.GetPlanForProjecting(Context.StringItems).To(); - mapper.GetPlanForProjecting(Context.Persons).To(); + mapper.GetPlanForProjecting(Context.Persons).To(); var allPlans = mapper.GetPlansInCache(); allPlans.ShouldContain("IQueryable -> IQueryable"); allPlans.ShouldContain("IQueryable -> IQueryable"); - allPlans.ShouldContain("IQueryable -> IQueryable"); + allPlans.ShouldContain("IQueryable -> IQueryable"); }); } } diff --git a/AgileMapper/Configuration/MappingConfigInfo.cs b/AgileMapper/Configuration/MappingConfigInfo.cs index 7f31b8448..c449750f5 100644 --- a/AgileMapper/Configuration/MappingConfigInfo.cs +++ b/AgileMapper/Configuration/MappingConfigInfo.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq.Expressions; - using Extensions.Internal; using Members; using ObjectPopulation; using ReadableExpressions; @@ -161,8 +160,7 @@ public Expression GetConditionOrNull( var conditionNestedAccessesChecks = mapperData .GetExpressionInfoFor(condition, targetCanBeNull) - .NestedAccesses - .GetIsNotDefaultComparisonsOrNull(); + .NestedAccessChecks; if (conditionNestedAccessesChecks != null) { diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index 64a96308f..f9e11e9b4 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -30,9 +30,13 @@ protected DataSourceBase( Expression condition = null) { SourceMember = sourceMember; - Condition = condition; Variables = variables; Value = value; + + if (value.NodeType != ExpressionType.Default) + { + Condition = condition; + } } protected DataSourceBase( @@ -45,10 +49,10 @@ protected DataSourceBase( ProcessMemberAccesses( mapperData, ref value, - out var nestedAccesses, + out var nestedAccessChecks, out var variables); - Condition = GetCondition(nestedAccesses, mapperData); + Condition = GetCondition(nestedAccessChecks, mapperData); Variables = variables; Value = value; } @@ -58,11 +62,11 @@ protected DataSourceBase( private static void ProcessMemberAccesses( IMemberMapperData mapperData, ref Expression value, - out IList nestedAccesses, + out Expression nestedAccessChecks, out IList variables) { var valueInfo = mapperData.GetExpressionInfoFor(value, targetCanBeNull: false); - nestedAccesses = valueInfo.NestedAccesses; + nestedAccessChecks = valueInfo.NestedAccessChecks; if (valueInfo.MultiInvocations.None()) { @@ -91,18 +95,16 @@ private static void ProcessMemberAccesses( value = Expression.Block(valueExpressions); } - private Expression GetCondition(IList nestedAccesses, IMemberMapperData mapperData) + private Expression GetCondition(Expression nestedAccessChecks, IMemberMapperData mapperData) { - if (nestedAccesses.None()) + if (nestedAccessChecks == null) { return null; } - var notNullChecks = nestedAccesses.GetIsNotDefaultComparisonsOrNull(); - - if (!IsOptionalEntityMemberId(mapperData)) + if (IsNotOptionalEntityMemberId(mapperData)) { - return notNullChecks; + return nestedAccessChecks; } var sourceMemberValue = SourceMember.GetQualifiedAccess(mapperData); @@ -110,7 +112,7 @@ private Expression GetCondition(IList nestedAccesses, IMemberMapperD if (!sourceValueType.IsNumeric()) { - return notNullChecks; + return nestedAccessChecks; } if (sourceMemberValue.Type.IsNullableType()) @@ -121,16 +123,16 @@ private Expression GetCondition(IList nestedAccesses, IMemberMapperD var zero = 0.ToConstantExpression(sourceValueType); var sourceValueNonZero = Expression.NotEqual(sourceMemberValue, zero); - return Expression.AndAlso(notNullChecks, sourceValueNonZero); + return Expression.AndAlso(nestedAccessChecks, sourceValueNonZero); } - private static bool IsOptionalEntityMemberId(IMemberMapperData mapperData) + private static bool IsNotOptionalEntityMemberId(IMemberMapperData mapperData) { var targetMember = mapperData.TargetMember; if (!targetMember.Type.IsNullableType()) { - return false; + return true; } var targetMemberNameSuffix = default(string); @@ -151,12 +153,12 @@ private static bool IsOptionalEntityMemberId(IMemberMapperData mapperData) break; default: - return false; + return true; } if (!mapperData.TargetTypeIsEntity()) { - return false; + return true; } var entityMemberNameLength = targetMember.Name.Length - targetMemberNameSuffix.Length; @@ -168,7 +170,7 @@ private static bool IsOptionalEntityMemberId(IMemberMapperData mapperData) .GetTargetMembers(mapperData.TargetType) .FirstOrDefault(m => m.Name == entityMemberName); - return mapperData.IsEntity(entityMember?.Type); + return !mapperData.IsEntity(entityMember?.Type, out var _); } #endregion diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index f098b426d..dcf01f3c0 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -18,7 +18,7 @@ internal class MappingRuleSetCollection { SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true, + GuardMemberAccesses = value => true, AllowObjectTracking = true }, new CopySourceEnumerablePopulationStrategy(), @@ -33,7 +33,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true, + GuardMemberAccesses = value => true, AllowObjectTracking = true }, new MergeEnumerablePopulationStrategy(), @@ -48,7 +48,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, - GuardMemberAccesses = true, + GuardMemberAccesses = value => true, AllowObjectTracking = true }, OverwriteEnumerablePopulationStrategy.Instance, @@ -62,6 +62,7 @@ internal class MappingRuleSetCollection { UseMemberInitialisation = true, UseSingleRootMappingExpression = true, + GuardMemberAccesses = value => value.Type.IsComplex(), AllowEnumerableAssignment = true }, new ProjectSourceEnumerablePopulationStrategy(), diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index 482177564..670035903 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -1,5 +1,8 @@ namespace AgileObjects.AgileMapper { + using System; + using System.Linq.Expressions; + internal class MappingRuleSetSettings { public bool RootHasPopulatedTarget { get; set; } @@ -12,7 +15,7 @@ internal class MappingRuleSetSettings public bool UseTryCatch { get; set; } - public bool GuardMemberAccesses { get; set; } + public Func GuardMemberAccesses { get; set; } public bool AllowEnumerableAssignment { get; set; } diff --git a/AgileMapper/Members/ExpressionInfoFinder.cs b/AgileMapper/Members/ExpressionInfoFinder.cs index 88092bae7..19bf80eec 100644 --- a/AgileMapper/Members/ExpressionInfoFinder.cs +++ b/AgileMapper/Members/ExpressionInfoFinder.cs @@ -10,6 +10,9 @@ namespace AgileObjects.AgileMapper.Members internal class ExpressionInfoFinder { + public static readonly ExpressionInfo EmptyExpressionInfo = + new ExpressionInfo(null, Enumerable.EmptyArray); + private readonly Expression _mappingDataObject; public ExpressionInfoFinder(Expression mappingDataObject) @@ -28,37 +31,37 @@ public ExpressionInfo FindIn(Expression expression, bool targetCanBeNull) private class ExpressionInfoFinderInstance : ExpressionVisitor { private readonly Expression _mappingDataObject; + private readonly bool _includeTargetNullChecking; private readonly ICollection _stringMemberAccessSubjects; private readonly ICollection _allInvocations; private readonly ICollection _multiInvocations; private readonly ICollection _nullCheckSubjects; private readonly Dictionary _nestedAccessesByPath; - private readonly bool _includeTargetNullChecking; public ExpressionInfoFinderInstance(Expression mappingDataObject, bool targetCanBeNull) { _mappingDataObject = mappingDataObject; + _includeTargetNullChecking = targetCanBeNull; _stringMemberAccessSubjects = new List(); _allInvocations = new List(); _multiInvocations = new List(); _nullCheckSubjects = new List(); _nestedAccessesByPath = new Dictionary(); - _includeTargetNullChecking = targetCanBeNull; } public ExpressionInfo FindIn(Expression expression) { Visit(expression); - var nestedAccesses = _nestedAccessesByPath.None() - ? Enumerable.EmptyArray - : _nestedAccessesByPath.Values.Reverse().ToArray(); + var nestedAccessChecks = _nestedAccessesByPath.Any() + ? _nestedAccessesByPath.Values.Reverse().ToArray().GetIsNotDefaultComparisonsOrNull() + : null; var multiInvocations = _multiInvocations .OrderBy(inv => inv.ToString()) .ToArray(); - return new ExpressionInfo(nestedAccesses, multiInvocations); + return new ExpressionInfo(nestedAccessChecks, multiInvocations); } protected override Expression VisitBinary(BinaryExpression binary) @@ -118,6 +121,7 @@ private bool IsNotRootObject(MemberExpression memberAccess) { if (memberAccess.Member.Name == "Parent") { + // ReSharper disable once PossibleNullReferenceException return !memberAccess.Member.DeclaringType.Name .StartsWith(nameof(IMappingData), StringComparison.Ordinal); } @@ -263,18 +267,15 @@ private bool AddMemberAccess(Expression memberAccess) public class ExpressionInfo { - public static readonly ExpressionInfo Empty = - new ExpressionInfo(Enumerable.EmptyArray, Enumerable.EmptyArray); - public ExpressionInfo( - IList nestedAccesses, + Expression nestedAccessChecks, IList multiInvocations) { - NestedAccesses = nestedAccesses; + NestedAccessChecks = nestedAccessChecks; MultiInvocations = multiInvocations; } - public IList NestedAccesses { get; } + public Expression NestedAccessChecks { get; } public IList MultiInvocations { get; } } diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index ff8c52326..aae0252f6 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -19,16 +19,17 @@ public static bool IsStandalone(this IObjectMappingData mappingData) => mappingData.IsRoot || mappingData.MapperKey.MappingTypes.RuntimeTypesNeeded; public static bool TargetTypeIsEntity(this IMemberMapperData mapperData) - => IsEntity(mapperData, mapperData.TargetType); + => IsEntity(mapperData, mapperData.TargetType, out var _); - public static bool IsEntity(this IMemberMapperData mapperData, Type type) + public static bool IsEntity(this IMemberMapperData mapperData, Type type, out Member idMember) { if (type == null) { + idMember = null; return false; } - var idMember = mapperData + idMember = mapperData .MapperContext .Naming .GetIdentifierOrNull(TypeKey.ForTypeId(type)); @@ -122,14 +123,9 @@ public static ExpressionInfoFinder.ExpressionInfo GetExpressionInfoFor( Expression value, bool targetCanBeNull) { - if (!mapperData.RuleSet.Settings.GuardMemberAccesses) - { - return ExpressionInfoFinder.ExpressionInfo.Empty; - } - - return mapperData.ExpressionInfoFinder.FindIn( - value, - targetCanBeNull); + return mapperData.RuleSet.Settings.GuardMemberAccesses(value) + ? mapperData.ExpressionInfoFinder.FindIn(value, targetCanBeNull) + : ExpressionInfoFinder.EmptyExpressionInfo; } public static bool SourceMemberIsStringKeyedDictionary( From a191447a7974f121f7607422a0b694a2dfbd4d53 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 3 Feb 2018 19:12:58 +0000 Subject: [PATCH 098/176] Conditionally populating complex type members in EF Core based on sibling member id value, re: bug comparing navigation properties to null --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 2 +- .../WhenProjectingCircularReferences.cs | 4 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- .../WhenProjectingCircularReferences.cs | 4 +- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 2 +- .../WhenProjectingCircularReferences.cs | 4 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- .../WhenProjectingCircularReferences.cs | 4 +- .../ComplexTypeToNullComparisonConverter.cs | 93 +++++++++++++++++++ .../QueryProjectionExpressionFactory.cs | 5 +- .../Queryables/QueryProjectionModifier.cs | 21 ++++- .../Settings/DefaultQueryProviderSettings.cs | 2 + .../Settings/EfCore2QueryProviderSettings.cs | 6 ++ .../Settings/IQueryProviderSettings.cs | 2 + 14 files changed, 133 insertions(+), 20 deletions(-) rename AgileMapper.UnitTests.Orms.Ef5/{ => Recursion}/WhenProjectingCircularReferences.cs (85%) rename AgileMapper.UnitTests.Orms.Ef6/{ => Recursion}/WhenProjectingCircularReferences.cs (85%) rename AgileMapper.UnitTests.Orms.EfCore1/{ => Recursion}/WhenProjectingCircularReferences.cs (84%) rename AgileMapper.UnitTests.Orms.EfCore2/{ => Recursion}/WhenProjectingCircularReferences.cs (87%) create mode 100644 AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 79705cc2d..b8ef15959 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -95,7 +95,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs similarity index 85% rename from AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs rename to AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs index 166aabd49..7a3805599 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Recursion { using Infrastructure; - using Recursion; + using Orms.Recursion; using Xunit; public class WhenProjectingCircularReferences : diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index c4295c4fb..5b219372a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -98,7 +98,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs similarity index 85% rename from AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs rename to AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs index 92983117f..e51d487c1 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Recursion { using Infrastructure; - using Recursion; + using Orms.Recursion; using Xunit; public class WhenProjectingCircularReferences : diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 48134382c..938af71c7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -216,7 +216,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs similarity index 84% rename from AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs rename to AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs index e587fa64f..fe1fe9170 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Recursion { using Infrastructure; - using Recursion; + using Orms.Recursion; using Xunit; public class WhenProjectingCircularReferences : diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index c7995e40d..f7fc2c68f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -153,7 +153,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs similarity index 87% rename from AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs rename to AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs index 652ff3dff..3e63ad7bb 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Recursion { using Infrastructure; - using Recursion; + using Orms.Recursion; using Xunit; public class WhenProjectingCircularReferences : diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs new file mode 100644 index 000000000..b924f8de9 --- /dev/null +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -0,0 +1,93 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using Extensions.Internal; + using Members; + using ReadableExpressions.Extensions; + using Settings; + + internal static class ComplexTypeToNullComparisonConverter + { + public static bool TryConvert( + BinaryExpression comparison, + IQueryProviderSettings settings, + IMemberMapperData mapperData, + out Expression converted) + { + if (settings.SupportsComplexTypeToNullComparisons || !comparison.Right.Type.IsComplex()) + { + converted = null; + return false; + } + + converted = Convert(comparison, mapperData); + return true; + } + + private static Expression Convert(BinaryExpression comparison, IMemberMapperData mapperData) + { + if (!mapperData.IsEntity(comparison.Left.Type, out var idMember)) + { + return true.ToConstantExpression(); + } + + var entityMemberAccess = comparison.Left; + var entityAccess = entityMemberAccess.GetParentOrNull(); + var entityMemberIdMember = GetEntityMemberIdMemberOrNull(entityAccess, entityMemberAccess, idMember); + + if (entityMemberIdMember == null) + { + return true.ToConstantExpression(); + } + + var entityMemberIdMemberAccess = entityMemberIdMember.GetAccess(entityAccess); + + return entityMemberIdMemberAccess.GetIsNotDefaultComparison(); + } + + private static Member GetEntityMemberIdMemberOrNull( + Expression entityAccess, + Expression entityMemberAccess, + Member entityIdMember) + { + var sourceMembers = GlobalContext + .Instance + .MemberCache + .GetSourceMembers(entityAccess.Type) + .Where(m => m.IsSimple) + .ToArray(); + + var entityMemberName = entityMemberAccess.GetMemberName(); + + if (TryGetEntityMemberIdMember(entityMemberName + entityIdMember.Name, sourceMembers, out var member)) + { + return member; + } + + if (!entityIdMember.Name.EqualsIgnoreCase("Id") && + TryGetEntityMemberIdMember(entityMemberName + "Id", sourceMembers, out member)) + { + return member; + } + + if (!entityIdMember.Name.EqualsIgnoreCase("Identifer") && + TryGetEntityMemberIdMember(entityMemberName + "Identifer", sourceMembers, out member)) + { + return member; + } + + return null; + } + + private static bool TryGetEntityMemberIdMember( + string idMemberName, + IEnumerable sourceMembers, + out Member member) + { + member = sourceMembers.FirstOrDefault(m => m.Name.EqualsIgnoreCase(idMemberName)); + return member != null; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs index 8231394a7..46e58e261 100644 --- a/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs +++ b/AgileMapper/Queryables/QueryProjectionExpressionFactory.cs @@ -4,7 +4,6 @@ using System.Linq.Expressions; using Extensions.Internal; using ObjectPopulation; - using Settings; internal class QueryProjectionExpressionFactory : MappingExpressionFactoryBase { @@ -30,9 +29,7 @@ protected override IEnumerable GetObjectPopulation(IObjectMappingDat mapperData.TargetMember.ElementType.ToDefaultExpression(), mappingData)); - var providerSettings = mappingData.GetQueryProviderSettings(); - - queryProjection = QueryProjectionModifier.Modify(queryProjection, providerSettings); + queryProjection = QueryProjectionModifier.Modify(queryProjection, mappingData); yield return queryProjection; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 7c08962bf..3978af874 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -3,20 +3,23 @@ using System.Linq.Expressions; using Converters; using Extensions.Internal; + using ObjectPopulation; using Settings; internal class QueryProjectionModifier : ExpressionVisitor { + private readonly ObjectMapperData _mapperData; private readonly IQueryProviderSettings _settings; - private QueryProjectionModifier(IQueryProviderSettings settings) + private QueryProjectionModifier(IObjectMappingData mappingData) { - _settings = settings; + _mapperData = mappingData.MapperData; + _settings = mappingData.GetQueryProviderSettings(); } - public static Expression Modify(Expression queryProjection, IQueryProviderSettings settings) + public static Expression Modify(Expression queryProjection, IObjectMappingData mappingData) { - return new QueryProjectionModifier(settings).Modify(queryProjection); + return new QueryProjectionModifier(mappingData).Modify(queryProjection); } private Expression Modify(Expression queryProjection) @@ -64,6 +67,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return base.VisitMethodCall(methodCall); } + protected override Expression VisitBinary(BinaryExpression binary) + { + if (ComplexTypeToNullComparisonConverter.TryConvert(binary, _settings, _mapperData, out var converted)) + { + return converted; + } + + return base.VisitBinary(binary); + } + protected override Expression VisitDefault(DefaultExpression defaultExpression) => DefaultExpressionConverter.Convert(defaultExpression); } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 0f13ad777..d135af1f0 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -35,6 +35,8 @@ public DefaultQueryProviderSettings() public virtual bool SupportsEmptyEnumerableCreation => true; + public virtual bool SupportsComplexTypeToNullComparisons => true; + public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); diff --git a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs index 0fad1bc02..23cdf2e7f 100644 --- a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs @@ -3,5 +3,11 @@ internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsStringEqualsIgnoreCase => true; + + // EF Core translates navigation property-to-null comparisons to compare + // on the navigation property id, and then falls over rewriting the + // comparison binary by trying to compare the complex type to its id value + // This is due to be fixed in 2.1. + public override bool SupportsComplexTypeToNullComparisons => false; } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 494f6c16b..44a7ec4d7 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -20,6 +20,8 @@ internal interface IQueryProviderSettings bool SupportsEmptyEnumerableCreation { get; } + bool SupportsComplexTypeToNullComparisons { get; } + Expression ConvertToStringCall(MethodCallExpression call); Expression ConvertGetValueOrDefaultCall(MethodCallExpression call); From b6b55014b7fe8d79a4b8d6910dff1261669d8b10 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 10:47:30 +0000 Subject: [PATCH 099/176] Fixing complex type null-checking in ORM tests --- .../WhenProjectingToComplexTypeMembers.cs | 2 + .../WhenProjectingToComplexTypeMembers.cs | 15 ++++- AgileMapper/DataSources/DataSourceBase.cs | 6 +- AgileMapper/DataSources/DataSourceFinder.cs | 3 +- AgileMapper/Members/ExpressionInfoFinder.cs | 5 ++ .../ComplexTypeConditionalConverter.cs | 62 +++++++++++++++++++ .../ComplexTypeToNullComparisonConverter.cs | 2 +- .../Converters/GetValueOrDefaultConverter.cs | 8 ++- ...er.cs => NullConstantExpressionFactory.cs} | 11 ++-- .../Queryables/QueryProjectionModifier.cs | 50 ++++++++------- .../Settings/DefaultQueryProviderSettings.cs | 17 +---- .../Settings/Ef5QueryProviderSettings.cs | 2 +- .../Settings/Ef6QueryProviderSettings.cs | 2 - .../Settings/EfCore2QueryProviderSettings.cs | 2 +- .../Settings/IQueryProviderSettings.cs | 6 +- .../QueryProviderSettingsExtensions.cs | 2 +- 16 files changed, 135 insertions(+), 60 deletions(-) create mode 100644 AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs rename AgileMapper/Queryables/Converters/{DefaultExpressionConverter.cs => NullConstantExpressionFactory.cs} (73%) diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs index b9975e884..1a1ab4408 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToComplexTypeMembers.cs @@ -9,5 +9,7 @@ public WhenProjectingToComplexTypeMembers(InMemoryEf5TestContext context) : base(context) { } + + public override bool QueryProviderNonEntityNullConstants => false; } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index d1ed66ed5..ed225c511 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -56,8 +56,21 @@ public void ShouldHandleANullComplexTypeMember() personDto.Id.ShouldBe(person.PersonId); personDto.Name.ShouldBe("No Address!"); - personDto.Address.ShouldBeNull(); + + if (QueryProviderNonEntityNullConstants) + { + personDto.Address.ShouldBeNull(); + return; + } + + personDto.Address.ShouldNotBeNull(); + personDto.Address.Id.ShouldBeDefault(); + personDto.Address.Line1.ShouldBeNull(); + personDto.Address.Line2.ShouldBeNull(); + personDto.Address.Postcode.ShouldBeNull(); }); } + + public virtual bool QueryProviderNonEntityNullConstants => true; } } diff --git a/AgileMapper/DataSources/DataSourceBase.cs b/AgileMapper/DataSources/DataSourceBase.cs index f9e11e9b4..050e4c77b 100644 --- a/AgileMapper/DataSources/DataSourceBase.cs +++ b/AgileMapper/DataSources/DataSourceBase.cs @@ -32,11 +32,7 @@ protected DataSourceBase( SourceMember = sourceMember; Variables = variables; Value = value; - - if (value.NodeType != ExpressionType.Default) - { - Condition = condition; - } + Condition = condition; } protected DataSourceBase( diff --git a/AgileMapper/DataSources/DataSourceFinder.cs b/AgileMapper/DataSources/DataSourceFinder.cs index 6a4583540..570e4af27 100644 --- a/AgileMapper/DataSources/DataSourceFinder.cs +++ b/AgileMapper/DataSources/DataSourceFinder.cs @@ -143,7 +143,8 @@ private static IEnumerable GetSourceMemberDataSources( yield break; } - if (matchingSourceMemberDataSource.IsConditional) + if (matchingSourceMemberDataSource.IsConditional && + (matchingSourceMemberDataSource.IsValid || configuredDataSources.Any())) { yield return GetFallbackDataSourceFor(mappingData); } diff --git a/AgileMapper/Members/ExpressionInfoFinder.cs b/AgileMapper/Members/ExpressionInfoFinder.cs index 19bf80eec..5f30707e1 100644 --- a/AgileMapper/Members/ExpressionInfoFinder.cs +++ b/AgileMapper/Members/ExpressionInfoFinder.cs @@ -151,6 +151,11 @@ private static bool IsNullableHasValueAccess(MemberExpression memberAccess) (memberAccess.Expression.Type.IsNullableType()); } + protected override MemberBinding VisitMemberBinding(MemberBinding binding) + { + return base.VisitMemberBinding(binding); + } + protected override Expression VisitMethodCall(MethodCallExpression methodCall) { if ((methodCall.Object != _mappingDataObject) && diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs new file mode 100644 index 000000000..ac70f274d --- /dev/null +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -0,0 +1,62 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System.Linq.Expressions; + using Extensions.Internal; + using ObjectPopulation; + using ReadableExpressions.Extensions; + using Settings; + + internal static class ComplexTypeConditionalConverter + { + public static bool TryConvert( + ConditionalExpression conditional, + IQueryProviderSettings settings, + ObjectMapperData mapperData, + out Expression converted) + { + if (settings.SupportsNonEntityNullConstants || !conditional.Type.IsComplex()) + { + converted = null; + return false; + } + + converted = new NonNullableMemberBinder(conditional).GuardMemberAccesses(); + return true; + } + + public class NonNullableMemberBinder : ExpressionVisitor + { + private readonly ConditionalExpression _conditional; + + public NonNullableMemberBinder(ConditionalExpression conditional) + { + _conditional = conditional; + } + + public Expression GuardMemberAccesses() + => VisitAndConvert(_conditional.IfTrue, nameof(GuardMemberAccesses)); + + protected override MemberBinding VisitMemberBinding(MemberBinding binding) + { + if (binding.BindingType != MemberBindingType.Assignment) + { + return base.VisitMemberBinding(binding); + } + + var memberBinding = (MemberAssignment)binding; + + if (memberBinding.Expression.Type.CanBeNull()) + { + return base.VisitMemberBinding(binding); + } + + var bindingValueOrNull = Expression.Condition( + _conditional.Test, + memberBinding.Expression, + NullConstantExpressionFactory.CreateFor(memberBinding.Expression)); + + return memberBinding.Update(bindingValueOrNull); + } + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs index b924f8de9..4506928fb 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -16,7 +16,7 @@ public static bool TryConvert( IMemberMapperData mapperData, out Expression converted) { - if (settings.SupportsComplexTypeToNullComparisons || !comparison.Right.Type.IsComplex()) + if (settings.SupportsComplexTypeToNullComparison || !comparison.Right.Type.IsComplex()) { converted = null; return false; diff --git a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index 968fc2b0f..a34c81247 100644 --- a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -18,12 +18,18 @@ public static bool TryConvert( return false; } - converted = settings.ConvertGetValueOrDefaultCall(methodCall); + // ReSharper disable once AssignNullToNotNullAttribute + converted = Expression.Condition( + methodCall.Object.GetIsNotDefaultComparison(), + Expression.Convert(methodCall.Object, methodCall.Type), + NullConstantExpressionFactory.CreateFor(methodCall.Type)); + return true; } private static bool IsNotGetValueOrDefaultCall(MethodCallExpression methodCall) { + // ReSharper disable once PossibleNullReferenceException return methodCall.Arguments.Any() || methodCall.Method.IsStatic || !methodCall.Object.Type.IsNullableType() || diff --git a/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs b/AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs similarity index 73% rename from AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs rename to AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs index 05c546256..0fcae7577 100644 --- a/AgileMapper/Queryables/Converters/DefaultExpressionConverter.cs +++ b/AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs @@ -6,16 +6,15 @@ using Extensions.Internal; using NetStandardPolyfills; - internal static class DefaultExpressionConverter + internal static class NullConstantExpressionFactory { - private static readonly MethodInfo _getDefaultValueMethod = typeof(DefaultExpressionConverter) + private static readonly MethodInfo _getDefaultValueMethod = typeof(NullConstantExpressionFactory) .GetNonPublicStaticMethod("GetDefaultValue"); - public static Expression Convert(Expression defaultExpression) - => Convert((DefaultExpression)defaultExpression); + public static Expression CreateFor(Expression expression) => CreateFor(expression.Type); - public static Expression Convert(DefaultExpression defaultExpression) - => GetDefaultValueFor(defaultExpression.Type).ToConstantExpression(defaultExpression.Type); + public static Expression CreateFor(Type type) + => GetDefaultValueFor(type).ToConstantExpression(type); private static object GetDefaultValueFor(Type type) { diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 3978af874..90fb041ea 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -18,35 +18,54 @@ private QueryProjectionModifier(IObjectMappingData mappingData) } public static Expression Modify(Expression queryProjection, IObjectMappingData mappingData) - { - return new QueryProjectionModifier(mappingData).Modify(queryProjection); - } + => new QueryProjectionModifier(mappingData).Modify(queryProjection); private Expression Modify(Expression queryProjection) + => VisitAndConvert(queryProjection, "Modify"); + + protected override Expression VisitBinary(BinaryExpression binary) { - return VisitAndConvert(queryProjection, "Modify"); + if (ComplexTypeToNullComparisonConverter.TryConvert(binary, _settings, _mapperData, out var converted)) + { + return converted; + } + + return base.VisitBinary(binary); } - protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) + protected override Expression VisitConditional(ConditionalExpression conditional) { - if (TryParseAssignmentConverter.TryConvert(assignment, _settings, out var converted)) + if (ComplexTypeConditionalConverter.TryConvert(conditional, _settings, _mapperData, out var converted)) { - return converted; + return Modify(converted); } - return base.VisitMemberAssignment(assignment); + return base.VisitConditional(conditional); } protected override Expression VisitConstant(ConstantExpression constant) { if (constant.Value is LambdaExpression lambda) { - return VisitAndConvert(lambda, "ModifyLambda").ToConstantExpression(constant.Type); + return Modify(lambda).ToConstantExpression(constant.Type); } return base.VisitConstant(constant); } + protected override Expression VisitDefault(DefaultExpression defaultExpression) + => NullConstantExpressionFactory.CreateFor(defaultExpression); + + protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) + { + if (TryParseAssignmentConverter.TryConvert(assignment, _settings, out var converted)) + { + return converted; + } + + return base.VisitMemberAssignment(assignment); + } + protected override Expression VisitMethodCall(MethodCallExpression methodCall) { if (ToStringConverter.TryConvert(methodCall, _settings, out var converted)) @@ -66,18 +85,5 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) return base.VisitMethodCall(methodCall); } - - protected override Expression VisitBinary(BinaryExpression binary) - { - if (ComplexTypeToNullComparisonConverter.TryConvert(binary, _settings, _mapperData, out var converted)) - { - return converted; - } - - return base.VisitBinary(binary); - } - - protected override Expression VisitDefault(DefaultExpression defaultExpression) - => DefaultExpressionConverter.Convert(defaultExpression); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index d135af1f0..9222a5cc7 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -33,24 +33,13 @@ public DefaultQueryProviderSettings() public virtual bool SupportsGetValueOrDefault => true; - public virtual bool SupportsEmptyEnumerableCreation => true; + public virtual bool SupportsComplexTypeToNullComparison => true; - public virtual bool SupportsComplexTypeToNullComparisons => true; + public virtual bool SupportsNonEntityNullConstants => true; public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); - public Expression ConvertGetValueOrDefaultCall(MethodCallExpression call) - { - var valueIsNotNull = call.Object.GetIsNotDefaultComparison(); - - // ReSharper disable once AssignNullToNotNullAttribute - var castValue = Expression.Convert(call.Object, call.Type); - var fallbackValue = DefaultExpressionConverter.Convert(call.Type.ToDefaultExpression()); - - return Expression.Condition(valueIsNotNull, castValue, fallbackValue); - } - public Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue) { if (call.Method.DeclaringType == typeof(Guid)) @@ -95,7 +84,7 @@ private static Expression GetConvertStringToGuid(MethodCallExpression guidTryPar if (fallbackValue.NodeType == ExpressionType.Default) { - fallbackValue = DefaultExpressionConverter.Convert(fallbackValue); + fallbackValue = NullConstantExpressionFactory.CreateFor(fallbackValue); } var nullString = default(string).ToConstantExpression(); diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 221fab86f..da645d7a9 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -13,7 +13,7 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsGetValueOrDefault => false; - public override bool SupportsEmptyEnumerableCreation => false; + public override bool SupportsNonEntityNullConstants => false; #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 6c37e5f8b..ff962c24c 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -9,8 +9,6 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsGetValueOrDefault => false; - public override bool SupportsEmptyEnumerableCreation => false; - #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); diff --git a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs index 23cdf2e7f..9b28885d5 100644 --- a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs @@ -8,6 +8,6 @@ internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings // on the navigation property id, and then falls over rewriting the // comparison binary by trying to compare the complex type to its id value // This is due to be fixed in 2.1. - public override bool SupportsComplexTypeToNullComparisons => false; + public override bool SupportsComplexTypeToNullComparison => false; } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 44a7ec4d7..7a2d4dc92 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -18,14 +18,12 @@ internal interface IQueryProviderSettings bool SupportsGetValueOrDefault { get; } - bool SupportsEmptyEnumerableCreation { get; } + bool SupportsComplexTypeToNullComparison { get; } - bool SupportsComplexTypeToNullComparisons { get; } + bool SupportsNonEntityNullConstants { get; } Expression ConvertToStringCall(MethodCallExpression call); - Expression ConvertGetValueOrDefaultCall(MethodCallExpression call); - Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); Expression ConvertEmptyArrayCreation(NewArrayExpression newEmptyArray); diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index dd73396ba..2f07c5741 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -68,7 +68,7 @@ public static Expression GetCreateDateTimeFromStringOrNull( if (fallbackValue.NodeType == ExpressionType.Default) { - fallbackValue = DefaultExpressionConverter.Convert(fallbackValue); + fallbackValue = NullConstantExpressionFactory.CreateFor(fallbackValue); } var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); From b6386a4d0895cafef8d440b6bf8581813124135d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 11:27:27 +0000 Subject: [PATCH 100/176] Ensuring nested enumerables are materialised where possible / Optimising ExpressionExtensions initialisation --- .../Internal/ExpressionExtensions.cs | 34 +++++++++----- .../Enumerables/EnumerableTypeHelper.cs | 2 +- .../ComplexTypeConditionalConverter.cs | 1 + .../NestedProjectionAssignmentConverter.cs | 47 +++++++++++++++++++ .../Queryables/QueryProjectionModifier.cs | 5 ++ .../Settings/DefaultQueryProviderSettings.cs | 2 + .../Settings/Ef5QueryProviderSettings.cs | 2 + .../Settings/Ef6QueryProviderSettings.cs | 2 + .../Settings/IQueryProviderSettings.cs | 2 + 9 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index a079bd98b..57fd3e03b 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -12,20 +12,25 @@ internal static partial class ExpressionExtensions { - private static readonly MethodInfo _listToArrayMethod = typeof(EnumerableExtensions) - .GetPublicStaticMethods("ToArray").First(); + private static readonly MethodInfo _listToArrayMethod; + private static readonly MethodInfo _collectionToArrayMethod; + private static readonly MethodInfo _linqToArrayMethod; + private static readonly MethodInfo _linqToListMethod; + private static readonly MethodInfo _stringEqualsMethod; - private static readonly MethodInfo _collectionToArrayMethod = typeof(EnumerableExtensions) - .GetPublicStaticMethods("ToArray").ElementAt(1); - - private static readonly MethodInfo _linqToArrayMethod = typeof(Enumerable) - .GetPublicStaticMethod("ToArray"); + static ExpressionExtensions() + { + var toArrayExtensionMethods = typeof(EnumerableExtensions).GetPublicStaticMethods("ToArray").ToArray(); + _listToArrayMethod = toArrayExtensionMethods.First(); + _collectionToArrayMethod = toArrayExtensionMethods.ElementAt(1); - public static readonly MethodInfo LinqToListMethod = typeof(Enumerable) - .GetPublicStaticMethod("ToList"); + var linqEnumerableMethods = typeof(Enumerable).GetPublicStaticMethods().ToArray(); + _linqToArrayMethod = linqEnumerableMethods.First(m => m.Name == "ToArray"); + _linqToListMethod = linqEnumerableMethods.First(m => m.Name == "ToList"); - private static readonly MethodInfo _stringEqualsMethod = typeof(string) - .GetPublicStaticMethod("Equals", parameterCount: 3); + _stringEqualsMethod = typeof(string) + .GetPublicStaticMethod("Equals", parameterCount: 3); + } [DebuggerStepThrough] public static BinaryExpression AssignTo(this Expression subject, Expression value) @@ -186,6 +191,9 @@ public static Expression GetConversionTo(this Expression expression, Type target return Expression.Convert(expression, targetType); } + public static Expression WithToArrayLinqCall(this Expression enumerable, Type elementType) + => GetToEnumerableCall(enumerable, _linqToArrayMethod, elementType); + public static Expression WithToArrayCall(this Expression enumerable, Type elementType) { var conversionMethod = GetToArrayConversionMethod(enumerable, elementType); @@ -271,8 +279,8 @@ public static Expression WithToCollectionCall(this Expression enumerable, Type e return GetCollectionCreation(typeHelper, toArrayCall); } - public static Expression WithToListCall(this Expression enumerable, Type elementType) - => GetToEnumerableCall(enumerable, LinqToListMethod, elementType); + public static Expression WithToListLinqCall(this Expression enumerable, Type elementType) + => GetToEnumerableCall(enumerable, _linqToListMethod, elementType); private static Expression GetToEnumerableCall(Expression enumerable, MethodInfo method, Type elementType) { diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs index 5caba4554..cab520877 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerableTypeHelper.cs @@ -131,7 +131,7 @@ public Expression GetEnumerableConversion(Expression instance, bool allowEnumera return instance.WithToCollectionCall(ElementType); } - return instance.WithToListCall(ElementType); + return instance.WithToListLinqCall(ElementType); } private static bool ValueIsNotEnumerableInterface(Expression instance) diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs index ac70f274d..360258914 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { + using System; using System.Linq.Expressions; using Extensions.Internal; using ObjectPopulation; diff --git a/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs b/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs new file mode 100644 index 000000000..bb70fe14c --- /dev/null +++ b/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs @@ -0,0 +1,47 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System; + using System.Linq.Expressions; + using Extensions.Internal; + using Settings; + + internal static class NestedProjectionAssignmentConverter + { + public static bool TryConvert( + MemberAssignment assignment, + IQueryProviderSettings settings, + Func modifyCallback, + out MemberAssignment converted) + { + if (settings.SupportsEnumerableMaterialisation && + (assignment.Expression.NodeType == ExpressionType.Call) && + assignment.Expression.Type.IsEnumerable()) + { + converted = ConvertToMaterialisation(assignment, modifyCallback); + return true; + } + + converted = null; + return false; + } + + private static MemberAssignment ConvertToMaterialisation( + MemberAssignment assignment, + Func modifyCallback) + { + var materialisedNestedProjection = GetMaterialisedNestedProjection(assignment.Expression); + var modifiedProjection = modifyCallback.Invoke(materialisedNestedProjection); + + return assignment.Update(modifiedProjection); + } + + private static Expression GetMaterialisedNestedProjection(Expression nestedProjection) + { + var elementType = nestedProjection.Type.GetEnumerableElementType(); + + return nestedProjection.Type.IsArray + ? nestedProjection.WithToArrayLinqCall(elementType) + : nestedProjection.WithToListLinqCall(elementType); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 90fb041ea..415cab036 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -63,6 +63,11 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig return converted; } + if (NestedProjectionAssignmentConverter.TryConvert(assignment, _settings, Modify, out converted)) + { + return converted; + } + return base.VisitMemberAssignment(assignment); } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 9222a5cc7..061c13c05 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -37,6 +37,8 @@ public DefaultQueryProviderSettings() public virtual bool SupportsNonEntityNullConstants => true; + public virtual bool SupportsEnumerableMaterialisation => true; + public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index da645d7a9..6c1a0cb34 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -15,6 +15,8 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsNonEntityNullConstants => false; + public override bool SupportsEnumerableMaterialisation => false; + #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.EntityFunctions"); diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index ff962c24c..d860af797 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -9,6 +9,8 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { public override bool SupportsGetValueOrDefault => false; + public override bool SupportsEnumerableMaterialisation => false; + #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 7a2d4dc92..94022e38a 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -22,6 +22,8 @@ internal interface IQueryProviderSettings bool SupportsNonEntityNullConstants { get; } + bool SupportsEnumerableMaterialisation { get; } + Expression ConvertToStringCall(MethodCallExpression call); Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); From 8c3e547dc35172f821cc07f2a955c0975b7ab4e1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 15:08:14 +0000 Subject: [PATCH 101/176] Tidying --- .../SourceElementsDictionaryAdapter.cs | 5 +- .../EnumerablePopulationBuilder.cs | 66 +++++++++++-------- .../ComplexTypeConditionalConverter.cs | 10 +-- .../ComplexTypeToNullComparisonConverter.cs | 23 ++++--- ... DefaultValueConstantExpressionFactory.cs} | 4 +- .../Converters/GetValueOrDefaultConverter.cs | 7 +- .../NestedProjectionAssignmentConverter.cs | 13 ++-- .../StringEqualsIgnoreCaseConverter.cs | 5 +- .../Converters/ToStringConverter.cs | 7 +- .../Converters/TryParseAssignmentConverter.cs | 5 +- .../Queryables/IQueryProjectionModifier.cs | 15 +++++ .../Queryables/QueryProjectionModifier.cs | 32 ++++----- .../Settings/DefaultQueryProviderSettings.cs | 2 +- .../QueryProviderSettingsExtensions.cs | 2 +- 14 files changed, 107 insertions(+), 89 deletions(-) rename AgileMapper/Queryables/Converters/{NullConstantExpressionFactory.cs => DefaultValueConstantExpressionFactory.cs} (92%) create mode 100644 AgileMapper/Queryables/IQueryProjectionModifier.cs diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceElementsDictionaryAdapter.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceElementsDictionaryAdapter.cs index efa2b5702..1257e1a17 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceElementsDictionaryAdapter.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/SourceElementsDictionaryAdapter.cs @@ -64,9 +64,8 @@ public override Expression GetSourceValues() var filteredEntries = Expression.Call(linqWhereMethod, dictionaryAccess, keyMatchesLambda); - var linqSelectMethod = typeof(Enumerable) - .GetPublicStaticMethods("Select") - .First(m => m.GetParameters()[1].ParameterType.GetGenericTypeArguments().Length == 2) + var linqSelectMethod = EnumerablePopulationBuilder + .EnumerableSelectWithoutIndexMethod .MakeGenericMethod(kvpType, SourceMember.ValueType); var kvpValueLambda = Expression.Lambda( diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index c6c4a0432..b74e9ade5 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -14,31 +14,11 @@ internal class EnumerablePopulationBuilder { #region Untyped MethodInfos - private static readonly MethodInfo _enumerableSelectWithoutIndexMethod = typeof(Enumerable) - .GetPublicStaticMethods("Select") - .First(m => - (m.GetParameters().Length == 2) && - (m.GetParameters()[1].ParameterType.GetGenericTypeArguments().Length == 2)); - - private static readonly MethodInfo _enumerableSelectWithIndexMethod = typeof(Enumerable) - .GetPublicStaticMethods("Select") - .First(m => - (m.GetParameters().Length == 2) && - (m.GetParameters()[1].ParameterType.GetGenericTypeArguments().Length == 3)); - - private static readonly MethodInfo _queryableSelectMethod = typeof(Queryable) - .GetPublicStaticMethods("Select") - .First(m => - (m.GetParameters().Length == 2) && - (m.GetParameters()[1].ParameterType.GetGenericTypeArguments()[0].GetGenericTypeArguments().Length == 2)); - - private static readonly MethodInfo _forEachMethod = typeof(EnumerableExtensions) - .GetPublicStaticMethods("ForEach") - .First(); - - private static readonly MethodInfo _forEachTupleMethod = typeof(EnumerableExtensions) - .GetPublicStaticMethods("ForEach") - .Last(); + public static readonly MethodInfo EnumerableSelectWithoutIndexMethod; + private static readonly MethodInfo _enumerableSelectWithIndexMethod; + private static readonly MethodInfo _queryableSelectMethod; + private static readonly MethodInfo _forEachMethod; + private static readonly MethodInfo _forEachTupleMethod; #endregion @@ -65,6 +45,40 @@ public EnumerablePopulationBuilder(ObjectMapperData mapperData) _populationExpressions = new List(); } + static EnumerablePopulationBuilder() + { + var linqSelectMethods = typeof(Enumerable) + .GetPublicStaticMethods("Select") + .Select(m => new + { + Method = m, + Parameters = m.GetParameters() + }) + .Where(m => m.Parameters.Length == 2) + .Select(m => new + { + m.Method, + ProjectionLambdaParameterCount = m.Parameters[1].ParameterType.GetGenericTypeArguments().Length + }) + .ToArray(); + + EnumerableSelectWithoutIndexMethod = linqSelectMethods + .First(m => m.ProjectionLambdaParameterCount == 2).Method; + + _enumerableSelectWithIndexMethod = linqSelectMethods + .First(m => m.ProjectionLambdaParameterCount == 3).Method; + + _queryableSelectMethod = typeof(Queryable) + .GetPublicStaticMethods("Select") + .First(m => + (m.GetParameters().Length == 2) && + (m.GetParameters()[1].ParameterType.GetGenericTypeArguments()[0].GetGenericTypeArguments().Length == 2)); + + var forEachMethods = typeof(EnumerableExtensions).GetPublicStaticMethods("ForEach").ToArray(); + _forEachMethod = forEachMethods.First(); + _forEachTupleMethod = forEachMethods.Last(); + } + #region Operator public static implicit operator BlockExpression(EnumerablePopulationBuilder builder) @@ -575,7 +589,7 @@ private Expression CreateSourceItemsProjection( ? _queryableSelectMethod : counterRequired ? _enumerableSelectWithIndexMethod - : _enumerableSelectWithoutIndexMethod; + : EnumerableSelectWithoutIndexMethod; ParameterExpression[] projectionFuncParameters; Type[] funcTypes; diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs index 360258914..68b287207 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -1,21 +1,17 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { - using System; using System.Linq.Expressions; using Extensions.Internal; - using ObjectPopulation; using ReadableExpressions.Extensions; - using Settings; internal static class ComplexTypeConditionalConverter { public static bool TryConvert( ConditionalExpression conditional, - IQueryProviderSettings settings, - ObjectMapperData mapperData, + IQueryProjectionModifier modifier, out Expression converted) { - if (settings.SupportsNonEntityNullConstants || !conditional.Type.IsComplex()) + if (modifier.Settings.SupportsNonEntityNullConstants || !conditional.Type.IsComplex()) { converted = null; return false; @@ -54,7 +50,7 @@ protected override MemberBinding VisitMemberBinding(MemberBinding binding) var bindingValueOrNull = Expression.Condition( _conditional.Test, memberBinding.Expression, - NullConstantExpressionFactory.CreateFor(memberBinding.Expression)); + DefaultValueConstantExpressionFactory.CreateFor(memberBinding.Expression)); return memberBinding.Update(bindingValueOrNull); } diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs index 4506928fb..9329b5c0e 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -6,56 +6,55 @@ using Extensions.Internal; using Members; using ReadableExpressions.Extensions; - using Settings; internal static class ComplexTypeToNullComparisonConverter { public static bool TryConvert( BinaryExpression comparison, - IQueryProviderSettings settings, - IMemberMapperData mapperData, + IQueryProjectionModifier context, out Expression converted) { - if (settings.SupportsComplexTypeToNullComparison || !comparison.Right.Type.IsComplex()) + if (context.Settings.SupportsComplexTypeToNullComparison || + !comparison.Right.Type.IsComplex()) { converted = null; return false; } - converted = Convert(comparison, mapperData); + converted = Convert(comparison, context); return true; } - private static Expression Convert(BinaryExpression comparison, IMemberMapperData mapperData) + private static Expression Convert(BinaryExpression comparison, IQueryProjectionModifier context) { - if (!mapperData.IsEntity(comparison.Left.Type, out var idMember)) + if (!context.MapperData.IsEntity(comparison.Left.Type, out var idMember)) { return true.ToConstantExpression(); } var entityMemberAccess = comparison.Left; - var entityAccess = entityMemberAccess.GetParentOrNull(); - var entityMemberIdMember = GetEntityMemberIdMemberOrNull(entityAccess, entityMemberAccess, idMember); + var entityParentAccess = entityMemberAccess.GetParentOrNull(); + var entityMemberIdMember = GetEntityMemberIdMemberOrNull(entityParentAccess, entityMemberAccess, idMember); if (entityMemberIdMember == null) { return true.ToConstantExpression(); } - var entityMemberIdMemberAccess = entityMemberIdMember.GetAccess(entityAccess); + var entityMemberIdMemberAccess = entityMemberIdMember.GetAccess(entityParentAccess); return entityMemberIdMemberAccess.GetIsNotDefaultComparison(); } private static Member GetEntityMemberIdMemberOrNull( - Expression entityAccess, + Expression entityParentAccess, Expression entityMemberAccess, Member entityIdMember) { var sourceMembers = GlobalContext .Instance .MemberCache - .GetSourceMembers(entityAccess.Type) + .GetSourceMembers(entityParentAccess.Type) .Where(m => m.IsSimple) .ToArray(); diff --git a/AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs b/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs similarity index 92% rename from AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs rename to AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs index 0fcae7577..fc187577b 100644 --- a/AgileMapper/Queryables/Converters/NullConstantExpressionFactory.cs +++ b/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs @@ -6,9 +6,9 @@ using Extensions.Internal; using NetStandardPolyfills; - internal static class NullConstantExpressionFactory + internal static class DefaultValueConstantExpressionFactory { - private static readonly MethodInfo _getDefaultValueMethod = typeof(NullConstantExpressionFactory) + private static readonly MethodInfo _getDefaultValueMethod = typeof(DefaultValueConstantExpressionFactory) .GetNonPublicStaticMethod("GetDefaultValue"); public static Expression CreateFor(Expression expression) => CreateFor(expression.Type); diff --git a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index a34c81247..ad39e590a 100644 --- a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -3,16 +3,15 @@ using System.Linq.Expressions; using Extensions.Internal; using ReadableExpressions.Extensions; - using Settings; internal static class GetValueOrDefaultConverter { public static bool TryConvert( MethodCallExpression methodCall, - IQueryProviderSettings settings, + IQueryProjectionModifier context, out Expression converted) { - if (settings.SupportsGetValueOrDefault || IsNotGetValueOrDefaultCall(methodCall)) + if (context.Settings.SupportsGetValueOrDefault || IsNotGetValueOrDefaultCall(methodCall)) { converted = null; return false; @@ -22,7 +21,7 @@ public static bool TryConvert( converted = Expression.Condition( methodCall.Object.GetIsNotDefaultComparison(), Expression.Convert(methodCall.Object, methodCall.Type), - NullConstantExpressionFactory.CreateFor(methodCall.Type)); + DefaultValueConstantExpressionFactory.CreateFor(methodCall.Type)); return true; } diff --git a/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs b/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs index bb70fe14c..a8322c4d2 100644 --- a/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs +++ b/AgileMapper/Queryables/Converters/NestedProjectionAssignmentConverter.cs @@ -1,23 +1,20 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { - using System; using System.Linq.Expressions; using Extensions.Internal; - using Settings; internal static class NestedProjectionAssignmentConverter { public static bool TryConvert( MemberAssignment assignment, - IQueryProviderSettings settings, - Func modifyCallback, + IQueryProjectionModifier modifier, out MemberAssignment converted) { - if (settings.SupportsEnumerableMaterialisation && + if (modifier.Settings.SupportsEnumerableMaterialisation && (assignment.Expression.NodeType == ExpressionType.Call) && assignment.Expression.Type.IsEnumerable()) { - converted = ConvertToMaterialisation(assignment, modifyCallback); + converted = ConvertToMaterialisation(assignment, modifier); return true; } @@ -27,10 +24,10 @@ public static bool TryConvert( private static MemberAssignment ConvertToMaterialisation( MemberAssignment assignment, - Func modifyCallback) + IQueryProjectionModifier context) { var materialisedNestedProjection = GetMaterialisedNestedProjection(assignment.Expression); - var modifiedProjection = modifyCallback.Invoke(materialisedNestedProjection); + var modifiedProjection = context.Modify(materialisedNestedProjection); return assignment.Update(modifiedProjection); } diff --git a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs index 91f517f20..00541a562 100644 --- a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs @@ -2,16 +2,15 @@ { using System.Linq.Expressions; using NetStandardPolyfills; - using Settings; internal static class StringEqualsIgnoreCaseConverter { public static bool TryConvert( MethodCallExpression methodCall, - IQueryProviderSettings settings, + IQueryProjectionModifier context, out Expression converted) { - if (settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) + if (context.Settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) { converted = null; return false; diff --git a/AgileMapper/Queryables/Converters/ToStringConverter.cs b/AgileMapper/Queryables/Converters/ToStringConverter.cs index d36db05de..94d4711ea 100644 --- a/AgileMapper/Queryables/Converters/ToStringConverter.cs +++ b/AgileMapper/Queryables/Converters/ToStringConverter.cs @@ -2,22 +2,21 @@ { using System.Linq.Expressions; using Extensions.Internal; - using Settings; internal static class ToStringConverter { public static bool TryConvert( MethodCallExpression methodCall, - IQueryProviderSettings settings, + IQueryProjectionModifier context, out Expression converted) { - if (settings.SupportsToString || IsNotToStringCall(methodCall)) + if (context.Settings.SupportsToString || IsNotToStringCall(methodCall)) { converted = null; return false; } - converted = settings.ConvertToStringCall(methodCall); + converted = context.Settings.ConvertToStringCall(methodCall); return true; } diff --git a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs index f31b229b6..dfc604265 100644 --- a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs @@ -2,13 +2,12 @@ { using System.Linq.Expressions; using Extensions.Internal; - using Settings; internal static class TryParseAssignmentConverter { public static bool TryConvert( MemberAssignment assignment, - IQueryProviderSettings settings, + IQueryProjectionModifier modifier, out MemberAssignment converted) { if (assignment.Expression.NodeType != ExpressionType.Block) @@ -42,7 +41,7 @@ public static bool TryConvert( return false; } - var convertedValue = settings.ConvertTryParseCall(methodCall, tryParseOrDefault.IfFalse); + var convertedValue = modifier.Settings.ConvertTryParseCall(methodCall, tryParseOrDefault.IfFalse); converted = assignment.Update(convertedValue); return true; diff --git a/AgileMapper/Queryables/IQueryProjectionModifier.cs b/AgileMapper/Queryables/IQueryProjectionModifier.cs new file mode 100644 index 000000000..6db19e168 --- /dev/null +++ b/AgileMapper/Queryables/IQueryProjectionModifier.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper.Queryables +{ + using System.Linq.Expressions; + using Members; + using Settings; + + internal interface IQueryProjectionModifier + { + IQueryProviderSettings Settings { get; } + + IMemberMapperData MapperData { get; } + + Expression Modify(Expression queryProjection); + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 415cab036..3272e9a24 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -3,29 +3,31 @@ using System.Linq.Expressions; using Converters; using Extensions.Internal; + using Members; using ObjectPopulation; using Settings; - internal class QueryProjectionModifier : ExpressionVisitor + internal class QueryProjectionModifier : ExpressionVisitor, IQueryProjectionModifier { - private readonly ObjectMapperData _mapperData; - private readonly IQueryProviderSettings _settings; - private QueryProjectionModifier(IObjectMappingData mappingData) { - _mapperData = mappingData.MapperData; - _settings = mappingData.GetQueryProviderSettings(); + MapperData = mappingData.MapperData; + Settings = mappingData.GetQueryProviderSettings(); } + public IQueryProviderSettings Settings { get; } + + public IMemberMapperData MapperData { get; } + public static Expression Modify(Expression queryProjection, IObjectMappingData mappingData) => new QueryProjectionModifier(mappingData).Modify(queryProjection); - private Expression Modify(Expression queryProjection) + public Expression Modify(Expression queryProjection) => VisitAndConvert(queryProjection, "Modify"); protected override Expression VisitBinary(BinaryExpression binary) { - if (ComplexTypeToNullComparisonConverter.TryConvert(binary, _settings, _mapperData, out var converted)) + if (ComplexTypeToNullComparisonConverter.TryConvert(binary, this, out var converted)) { return converted; } @@ -35,7 +37,7 @@ protected override Expression VisitBinary(BinaryExpression binary) protected override Expression VisitConditional(ConditionalExpression conditional) { - if (ComplexTypeConditionalConverter.TryConvert(conditional, _settings, _mapperData, out var converted)) + if (ComplexTypeConditionalConverter.TryConvert(conditional, this, out var converted)) { return Modify(converted); } @@ -54,16 +56,16 @@ protected override Expression VisitConstant(ConstantExpression constant) } protected override Expression VisitDefault(DefaultExpression defaultExpression) - => NullConstantExpressionFactory.CreateFor(defaultExpression); + => DefaultValueConstantExpressionFactory.CreateFor(defaultExpression); protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { - if (TryParseAssignmentConverter.TryConvert(assignment, _settings, out var converted)) + if (TryParseAssignmentConverter.TryConvert(assignment, this, out var converted)) { return converted; } - if (NestedProjectionAssignmentConverter.TryConvert(assignment, _settings, Modify, out converted)) + if (NestedProjectionAssignmentConverter.TryConvert(assignment, this, out converted)) { return converted; } @@ -73,17 +75,17 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig protected override Expression VisitMethodCall(MethodCallExpression methodCall) { - if (ToStringConverter.TryConvert(methodCall, _settings, out var converted)) + if (ToStringConverter.TryConvert(methodCall, this, out var converted)) { return converted; } - if (GetValueOrDefaultConverter.TryConvert(methodCall, _settings, out converted)) + if (GetValueOrDefaultConverter.TryConvert(methodCall, this, out converted)) { return converted; } - if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, _settings, out converted)) + if (StringEqualsIgnoreCaseConverter.TryConvert(methodCall, this, out converted)) { return converted; } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 061c13c05..e71edccf3 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -86,7 +86,7 @@ private static Expression GetConvertStringToGuid(MethodCallExpression guidTryPar if (fallbackValue.NodeType == ExpressionType.Default) { - fallbackValue = NullConstantExpressionFactory.CreateFor(fallbackValue); + fallbackValue = DefaultValueConstantExpressionFactory.CreateFor(fallbackValue); } var nullString = default(string).ToConstantExpression(); diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 2f07c5741..b25fdbb02 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -68,7 +68,7 @@ public static Expression GetCreateDateTimeFromStringOrNull( if (fallbackValue.NodeType == ExpressionType.Default) { - fallbackValue = NullConstantExpressionFactory.CreateFor(fallbackValue); + fallbackValue = DefaultValueConstantExpressionFactory.CreateFor(fallbackValue); } var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); From 4d9f57b0ac629a7865217aad5732398f5478417b Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 15:18:33 +0000 Subject: [PATCH 102/176] EF Core 2 test coverage for projection to 2nd-recursion-depth / EF Core 1 test coverage for projection to 1st and 2nd recursion depth --- .../WhenProjectingCircularReferences.cs | 8 ++++++ .../WhenProjectingCircularReferences.cs | 4 +++ .../WhenProjectingToEnumerableMembers.cs | 9 ++----- .../WhenProjectingCircularReferences.cs | 26 ++++++++++++++----- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs index fe1fe9170..ca7772d6a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs @@ -16,5 +16,13 @@ public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) [Fact] public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + + [Fact] + public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + + [Fact] + public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs index 3e63ad7bb..d9fe0270b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs @@ -20,5 +20,9 @@ public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() [Fact] public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + + [Fact] + public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index fb1e92db4..12b38ef4d 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -99,13 +99,8 @@ protected void RunShouldProjectToAComplexTypeEnumerableMember() protected void ProjectToComplexTypeEnumerableMember(TOrmContext context) { - var item1 = new OrderItem - { - }; - - var item2 = new OrderItem - { - }; + var item1 = new OrderItem(); + var item2 = new OrderItem(); var order = new Order { diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index a296bc73e..6ae7bf81b 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -77,6 +77,9 @@ protected void DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDe protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); + protected void DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(2, context)); + protected void ProjectAOneToManyRelationshipToRecursionDepth( int depth, TOrmContext context) @@ -164,17 +167,26 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( Verify(depth13Dtos.First(), child3.SubCategories.First()); + depth11Dtos.First().SubCategories.ShouldBeEmpty(); + depth11Dtos.Second().SubCategories.ShouldBeEmpty(); + + depth12Dtos.First().SubCategories.ShouldBeEmpty(); + depth12Dtos.Third().SubCategories.ShouldBeEmpty(); + + depth13Dtos.First().SubCategories.ShouldBeEmpty(); + if (!(depth > 1)) { - depth11Dtos.First().SubCategories.ShouldBeEmpty(); - depth11Dtos.Second().SubCategories.ShouldBeEmpty(); - - depth12Dtos.First().SubCategories.ShouldBeEmpty(); depth12Dtos.Second().SubCategories.ShouldBeEmpty(); - depth12Dtos.Third().SubCategories.ShouldBeEmpty(); - - depth13Dtos.First().SubCategories.ShouldBeEmpty(); + return; } + + var depth122Dtos = GetOrderedSubCategories(depth12Dtos.Second()); + + depth122Dtos.Length.ShouldBe(2); + + Verify(depth122Dtos.First(), child2.SubCategories.Second().SubCategories.First()); + Verify(depth122Dtos.Second(), child2.SubCategories.Second().SubCategories.Second()); } private static CategoryDto[] GetOrderedSubCategories(CategoryDto parentDto) From 5f6da650a1bdaf6892647afca5926ecf94c5990e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 16:40:34 +0000 Subject: [PATCH 103/176] Organising enumerable member projection tests --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 2 +- .../{ => Enumerables}/WhenProjectingToEnumerableMembers.cs | 4 ++-- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- .../{ => Enumerables}/WhenProjectingToEnumerableMembers.cs | 4 ++-- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 2 +- .../{ => Enumerables}/WhenProjectingToEnumerableMembers.cs | 4 ++-- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- .../{ => Enumerables}/WhenProjectingToEnumerableMembers.cs | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) rename AgileMapper.UnitTests.Orms.Ef5/{ => Enumerables}/WhenProjectingToEnumerableMembers.cs (87%) rename AgileMapper.UnitTests.Orms.Ef6/{ => Enumerables}/WhenProjectingToEnumerableMembers.cs (87%) rename AgileMapper.UnitTests.Orms.EfCore1/{ => Enumerables}/WhenProjectingToEnumerableMembers.cs (87%) rename AgileMapper.UnitTests.Orms.EfCore2/{ => Enumerables}/WhenProjectingToEnumerableMembers.cs (87%) diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index b8ef15959..16304ce8f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -98,7 +98,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs similarity index 87% rename from AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs rename to AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs index e8e41367b..92c066ae5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Enumerables { - using Enumerables; using Infrastructure; + using Orms.Enumerables; using Xunit; public class WhenProjectingToEnumerableMembers : diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 5b219372a..98449650a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -101,7 +101,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs similarity index 87% rename from AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs rename to AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs index f1fd8e05a..19ff3d2ee 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Enumerables { - using Enumerables; using Infrastructure; + using Orms.Enumerables; using Xunit; public class WhenProjectingToEnumerableMembers : diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 938af71c7..d79d74031 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -219,7 +219,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs similarity index 87% rename from AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs rename to AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs index 30c11bac6..a0cbf4134 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Enumerables { - using Enumerables; using Infrastructure; + using Orms.Enumerables; using Xunit; public class WhenProjectingToEnumerableMembers : diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index f7fc2c68f..7ed9b25d3 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -154,7 +154,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs similarity index 87% rename from AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs rename to AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs index 7f30d5c0f..e9f79e9e5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,7 +1,7 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Enumerables { - using Enumerables; using Infrastructure; + using Orms.Enumerables; using Xunit; public class WhenProjectingToEnumerableMembers : From 4801598e756e8256765b53c91bf6b73c1d07ef81 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 17:00:18 +0000 Subject: [PATCH 104/176] Test coverage for entity flattening projection --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenProjectingToFlatTypes.cs | 12 ++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenProjectingToFlatTypes.cs | 12 ++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenProjectingToFlatTypes.cs | 12 ++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenProjectingToFlatTypes.cs | 12 ++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + .../TestClasses/PersonViewModel.cs | 8 +++ .../WhenProjectingFlatTypes.cs | 6 +- .../WhenProjectingToFlatTypes.cs | 62 +++++++++++++++++++ 12 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs create mode 100644 AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 16304ce8f..7fbdc76c9 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -99,6 +99,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs new file mode 100644 index 000000000..3358a6c4c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + + public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes + { + public WhenProjectingToFlatTypes(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 98449650a..110bac6bd 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -102,6 +102,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs new file mode 100644 index 000000000..bf0a20123 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + + public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes + { + public WhenProjectingToFlatTypes(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index d79d74031..7ead4a13f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -220,6 +220,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs new file mode 100644 index 000000000..9961a87a2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + + public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes + { + public WhenProjectingToFlatTypes(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 7ed9b25d3..373b83242 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -155,6 +155,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs new file mode 100644 index 000000000..e32bcc557 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + + public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes + { + public WhenProjectingToFlatTypes(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 64661d9bf..1a7567ddb 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -135,6 +135,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs index eba387399..80c756144 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs @@ -6,8 +6,16 @@ public class PersonViewModel public string Name { get; set; } + public int? AddressId { get; set; } + public string AddressLine1 { get; set; } public string AddressLine2 { get; set; } + + public string AddressPostcode + { + get; + set; + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 49ba548b3..942ec3444 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -14,7 +14,7 @@ protected WhenProjectingFlatTypes(ITestContext context) } [Fact] - public void ShouldProjectAFlatTypeToAnArray() + public void ShouldProjectAFlatTypeToAFlatTypeArray() { RunTest(context => { @@ -34,10 +34,10 @@ public void ShouldProjectAFlatTypeToAnArray() productDtos.Length.ShouldBe(2); productDtos[0].ProductId.ShouldBe(product1.ProductId); - productDtos[0].Name.ShouldBe(product1.Name); + productDtos[0].Name.ShouldBe("Product One"); productDtos[1].ProductId.ShouldBe(product2.ProductId); - productDtos[1].Name.ShouldBe(product2.Name); + productDtos[1].Name.ShouldBe("Product Two"); }); } diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs new file mode 100644 index 000000000..3c92181b1 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs @@ -0,0 +1,62 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Linq; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenProjectingToFlatTypes : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenProjectingToFlatTypes(ITestContext context) + : base(context) + { + } + + [Fact] + public void ShouldProjectAComplexTypeMemberToAFlatTypeList() + { + RunTest(context => + { + var person1 = new Person + { + Name = "Person One", + Address = new Address + { + Line1 = "Person One Address Line 1", + Line2 = "Person One Address Line 2", + Postcode = "Person One Address Postcode" + } + }; + + var person2 = new Person { Name = "Person Two" }; + + context.Persons.Add(person1); + context.Persons.Add(person2); + context.SaveChanges(); + + var personViewModels = context + .Persons + .Project().To() + .OrderBy(pvm => pvm.Id) + .ToList(); + + personViewModels.Count.ShouldBe(2); + + personViewModels[0].Id.ShouldBe(person1.PersonId); + personViewModels[0].Name.ShouldBe("Person One"); + personViewModels[0].AddressId.ShouldBe(person1.Address.AddressId); + personViewModels[0].AddressLine1.ShouldBe("Person One Address Line 1"); + personViewModels[0].AddressLine2.ShouldBe("Person One Address Line 2"); + personViewModels[0].AddressPostcode.ShouldBe("Person One Address Postcode"); + + personViewModels[1].Id.ShouldBe(person2.PersonId); + personViewModels[1].Name.ShouldBe("Person Two"); + personViewModels[1].AddressId.ShouldBeNull(); + personViewModels[1].AddressLine1.ShouldBeNull(); + personViewModels[1].AddressLine2.ShouldBeNull(); + personViewModels[1].AddressPostcode.ShouldBeNull(); + }); + } + } +} From 37849da9f654f71363b7a253519d4b0c4aacfe2f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 17:24:35 +0000 Subject: [PATCH 105/176] Updating ORM tests to async, switching to async operations where available --- .../WhenProjectingToEnumerableMembers.cs | 5 +- .../Infrastructure/Ef5TestDbContext.cs | 8 ++- .../WhenProjectingToEnumerableMembers.cs | 5 +- .../Infrastructure/Ef6TestDbContext.cs | 3 +- .../WhenProjectingToEnumerableMembers.cs | 5 +- .../Infrastructure/EfCore1TestDbContext.cs | 3 +- .../WhenProjectingToEnumerableMembers.cs | 5 +- .../Infrastructure/EfCore2TestDbContext.cs | 3 +- .../WhenCreatingProjections.cs | 17 +++--- .../ICollectionMemberProjectionFailureTest.cs | 4 +- .../ICollectionMemberProjectorTest.cs | 4 +- .../IEnumerableMemberProjectorTest.cs | 4 +- .../WhenProjectingToEnumerableMembers.cs | 15 ++--- .../Infrastructure/ITestDbContext.cs | 3 +- .../Infrastructure/OrmTestClassBase.cs | 17 +++--- .../WhenProjectingCircularReferences.cs | 23 ++++---- .../WhenConvertingToBools.cs | 55 ++++++++++--------- .../WhenConvertingToDateTimes.cs | 25 +++++---- .../WhenConvertingToDoubles.cs | 37 +++++++------ .../WhenConvertingToGuids.cs | 17 +++--- .../WhenConvertingToInts.cs | 49 +++++++++-------- .../WhenConvertingToStrings.cs | 13 +++-- .../WhenProjectingFlatTypes.cs | 13 +++-- .../WhenProjectingToComplexTypeMembers.cs | 13 +++-- .../WhenProjectingToFlatTypes.cs | 7 ++- .../WhenViewingMappingPlans.cs | 13 +++-- AgileMapper.UnitTests/Should.cs | 31 ++++++++++- 27 files changed, 231 insertions(+), 166 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs index 92c066ae5..c664b19ba 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Enumerables { + using System.Threading.Tasks; using Infrastructure; using Orms.Enumerables; using Xunit; @@ -15,11 +16,11 @@ public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingToAComplexTypeCollectionMember() + public Task ShouldErrorProjectingToAComplexTypeCollectionMember() => RunShouldErrorProjectingToAComplexTypeCollectionMember(); [Fact] - public void ShouldProjectToAComplexTypeEnumerableMember() + public Task ShouldProjectToAComplexTypeEnumerableMember() => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 1f1414243..01b92c421 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -2,6 +2,7 @@ { using System.Data.Common; using System.Data.Entity; + using System.Threading.Tasks; using Effort; using Orms.Infrastructure; using TestClasses; @@ -106,7 +107,12 @@ IDbSetWrapper ITestDbContext.LongItems IDbSetWrapper ITestDbContext.StringItems => new Ef5DbSetWrapper(StringItems); - void ITestDbContext.SaveChanges() => SaveChanges(); + Task ITestDbContext.SaveChanges() + { + SaveChanges(); + + return Task.CompletedTask; + } #endregion } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs index 19ff3d2ee..befda8339 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Enumerables { + using System.Threading.Tasks; using Infrastructure; using Orms.Enumerables; using Xunit; @@ -15,11 +16,11 @@ public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) } [Fact] - public void ShouldProjectToAComplexTypeCollectionMember() + public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); [Fact] - public void ShouldProjectToAComplexTypeEnumerableMember() + public Task ShouldProjectToAComplexTypeEnumerableMember() => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index e089036f0..01ea2707c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -2,6 +2,7 @@ { using System.Data.Common; using System.Data.Entity; + using System.Threading.Tasks; using Effort; using Orms.Infrastructure; using TestClasses; @@ -106,7 +107,7 @@ IDbSetWrapper ITestDbContext.LongItems IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(StringItems); - void ITestDbContext.SaveChanges() => SaveChanges(); + Task ITestDbContext.SaveChanges() => SaveChangesAsync(); #endregion } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs index a0cbf4134..55dd40601 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Enumerables { + using System.Threading.Tasks; using Infrastructure; using Orms.Enumerables; using Xunit; @@ -15,11 +16,11 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectToAComplexTypeCollectionMember() + public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); [Fact] - public void ShouldProjectToAComplexTypeEnumerableMember() + public Task ShouldProjectToAComplexTypeEnumerableMember() => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 3d252d58f..477dcbdec 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Infrastructure { + using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; using TestClasses; @@ -110,7 +111,7 @@ IDbSetWrapper ITestDbContext.LongItems IDbSetWrapper ITestDbContext.StringItems => new EfCore1DbSetWrapper(StringItems); - void ITestDbContext.SaveChanges() => SaveChanges(); + Task ITestDbContext.SaveChanges() => SaveChangesAsync(); #endregion } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs index e9f79e9e5..242c7847b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Enumerables { + using System.Threading.Tasks; using Infrastructure; using Orms.Enumerables; using Xunit; @@ -15,11 +16,11 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectToAComplexTypeCollectionMember() + public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); [Fact] - public void ShouldProjectToAComplexTypeEnumerableMember() + public Task ShouldProjectToAComplexTypeEnumerableMember() => RunShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index a4c0573c2..e872a7adf 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Infrastructure { + using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Debug; @@ -118,7 +119,7 @@ IDbSetWrapper ITestDbContext.LongItems IDbSetWrapper ITestDbContext.StringItems => new EfCore2DbSetWrapper(StringItems); - void ITestDbContext.SaveChanges() => SaveChanges(); + Task ITestDbContext.SaveChanges() => SaveChangesAsync(); #endregion } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index 4cfa43e92..20f2b4b25 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -1,7 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { - using System.Linq; + using System.Threading.Tasks; using Infrastructure; + using Microsoft.EntityFrameworkCore; using MoreTestClasses; using Orms.Infrastructure; using TestClasses; @@ -15,16 +16,16 @@ public WhenCreatingProjections(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldReuseACachedProjectionMapper() + public Task ShouldReuseACachedProjectionMapper() { - RunTest(mapper => + return RunTest(async mapper => { using (var context1 = new EfCore2TestDbContext()) { - var stringDtos = context1 + var stringDtos = await context1 .StringItems .Project().To(c => c.Using(mapper)) - .ToArray(); + .ToListAsync(); stringDtos.ShouldBeEmpty(); } @@ -32,12 +33,12 @@ public void ShouldReuseACachedProjectionMapper() using (var context2 = new EfCore2TestDbContext()) { context2.StringItems.Add(new PublicString { Id = 1, Value = "New!" }); - context2.SaveChanges(); + await context2.SaveChangesAsync(); - var moreStringDtos = context2 + var moreStringDtos = await context2 .StringItems .Project().To(c => c.Using(mapper)) - .ToArray(); + .ToArrayAsync(); moreStringDtos.ShouldHaveSingleItem(); } diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs index 42be30cef..0e6e97778 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables { + using System.Threading.Tasks; + public interface ICollectionMemberProjectionFailureTest { - void ShouldErrorProjectingToAComplexTypeCollectionMember(); + Task ShouldErrorProjectingToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs index 2759aa939..a69c1e068 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables { + using System.Threading.Tasks; + public interface ICollectionMemberProjectorTest { - void ShouldProjectToAComplexTypeCollectionMember(); + Task ShouldProjectToAComplexTypeCollectionMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs index 964463f17..86bc7318a 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables { + using System.Threading.Tasks; + public interface IEnumerableMemberProjectorTest { - void ShouldProjectToAComplexTypeEnumerableMember(); + Task ShouldProjectToAComplexTypeEnumerableMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index 12b38ef4d..f18b87fdd 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -16,13 +17,13 @@ protected WhenProjectingToEnumerableMembers(ITestContext context) #region Project -> Collection - protected void RunShouldProjectToAComplexTypeCollectionMember() + protected Task RunShouldProjectToAComplexTypeCollectionMember() => RunTest(ProjectToComplexTypeCollectionMember); - protected void RunShouldErrorProjectingToAComplexTypeCollectionMember() + protected Task RunShouldErrorProjectingToAComplexTypeCollectionMember() => RunTestAndExpectThrow(ProjectToComplexTypeCollectionMember); - protected void ProjectToComplexTypeCollectionMember(TOrmContext context) + protected async Task ProjectToComplexTypeCollectionMember(TOrmContext context) { var rotaEntry1 = new RotaEntry { @@ -62,7 +63,7 @@ protected void ProjectToComplexTypeCollectionMember(TOrmContext context) }; context.Rotas.Add(rota); - context.SaveChanges(); + await context.SaveChanges(); var rotaDto = context.Rotas.Where(r => r.Id == 1).Project().To().First(); @@ -94,10 +95,10 @@ protected void ProjectToComplexTypeCollectionMember(TOrmContext context) #region Project -> Enumerable - protected void RunShouldProjectToAComplexTypeEnumerableMember() + protected Task RunShouldProjectToAComplexTypeEnumerableMember() => RunTest(ProjectToComplexTypeEnumerableMember); - protected void ProjectToComplexTypeEnumerableMember(TOrmContext context) + protected async Task ProjectToComplexTypeEnumerableMember(TOrmContext context) { var item1 = new OrderItem(); var item2 = new OrderItem(); @@ -109,7 +110,7 @@ protected void ProjectToComplexTypeEnumerableMember(TOrmContext context) }; context.Orders.Add(order); - context.SaveChanges(); + await context.SaveChanges(); var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 6496e9fac..d5f6d12f5 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; + using System.Threading.Tasks; using TestClasses; public interface ITestDbContext : IDisposable @@ -35,6 +36,6 @@ public interface ITestDbContext : IDisposable IDbSetWrapper StringItems { get; } - void SaveChanges(); + Task SaveChanges(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 7343a1c95..4c2452942 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; + using System.Threading.Tasks; using Xunit; [Collection(TestConstants.OrmCollectionName)] @@ -14,20 +15,20 @@ protected OrmTestClassBase(ITestContext context) protected TOrmContext Context { get; } - protected Exception RunTestAndExpectThrow(Action testAction) - => RunTestAndExpectThrow(testAction); + protected Task RunTestAndExpectThrow(Func test) + => RunTestAndExpectThrow(test); - protected TException RunTestAndExpectThrow(Action testAction) + protected Task RunTestAndExpectThrow(Func test) where TException : Exception { - return Should.Throw(() => RunTest(testAction)); + return Should.ThrowAsync(() => RunTest(test)); } - protected void RunTest(Action testAction) + protected async Task RunTest(Func test) { try { - testAction.Invoke(Context); + await test.Invoke(Context); } finally { @@ -35,13 +36,13 @@ protected void RunTest(Action testAction) } } - protected void RunTest(Action testAction) + protected async Task RunTest(Func test) { try { using (var mapper = Mapper.CreateNew()) { - testAction.Invoke(mapper); + await test.Invoke(mapper); } } finally diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index 6ae7bf81b..02556b0d3 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -2,6 +2,7 @@ { using System; using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -15,9 +16,9 @@ protected WhenProjectingCircularReferences(ITestContext context) } [Fact] - public void ShouldProjectAOneToOneRelationship() + public Task ShouldProjectAOneToOneRelationship() { - RunTest(context => + return RunTest(async context => { var company = new Company { @@ -26,7 +27,7 @@ public void ShouldProjectAOneToOneRelationship() }; context.Companies.Add(company); - context.SaveChanges(); + await context.SaveChanges(); var ceo = new Employee { @@ -38,13 +39,13 @@ public void ShouldProjectAOneToOneRelationship() }; context.Employees.Add(ceo); - context.SaveChanges(); + await context.SaveChanges(); company.CeoId = ceo.Id; company.Ceo = ceo; company.HeadOfficeId = company.HeadOffice.AddressId; - context.SaveChanges(); + await context.SaveChanges(); var companyDto = context.Companies.Project().To().ShouldHaveSingleItem(); @@ -68,19 +69,19 @@ public void ShouldProjectAOneToOneRelationship() #region Project One-to-Many - protected void DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + protected Task DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); - protected void DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + protected Task DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() => RunTestAndExpectThrow(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); - protected void DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + protected Task DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(1, context)); - protected void DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + protected Task DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(2, context)); - protected void ProjectAOneToManyRelationshipToRecursionDepth( + protected async Task ProjectAOneToManyRelationshipToRecursionDepth( int depth, TOrmContext context) { @@ -115,7 +116,7 @@ protected void ProjectAOneToManyRelationshipToRecursionDepth( } } - context.SaveChanges(); + await context.SaveChanges(); var topLevelDto = context .Categories diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index 10e139510..405ca1f44 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,12 +15,12 @@ protected WhenConvertingToBools(ITestContext context) } [Fact] - public void ShouldProjectAnIntOneToTrue() + public Task ShouldProjectAnIntOneToTrue() { - RunTest(context => + return RunTest(async context => { context.IntItems.Add(new PublicInt { Value = 1 }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.IntItems.Project().To().First(); @@ -28,12 +29,12 @@ public void ShouldProjectAnIntOneToTrue() } [Fact] - public void ShouldProjectAnIntZeroToFalse() + public Task ShouldProjectAnIntZeroToFalse() { - RunTest(context => + return RunTest(async context => { context.IntItems.Add(new PublicInt { Value = 0 }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.IntItems.Project().To().First(); @@ -42,12 +43,12 @@ public void ShouldProjectAnIntZeroToFalse() } [Fact] - public void ShouldProjectAStringTrueToTrue() + public Task ShouldProjectAStringTrueToTrue() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "true" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -56,12 +57,12 @@ public void ShouldProjectAStringTrueToTrue() } [Fact] - public void ShouldProjectAStringTrueToTrueIgnoringCase() + public Task ShouldProjectAStringTrueToTrueIgnoringCase() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "tRuE" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -70,12 +71,12 @@ public void ShouldProjectAStringTrueToTrueIgnoringCase() } [Fact] - public void ShouldProjectAStringOneToTrue() + public Task ShouldProjectAStringOneToTrue() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "1" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -84,12 +85,12 @@ public void ShouldProjectAStringOneToTrue() } [Fact] - public void ShouldProjectAStringFalseToFalse() + public Task ShouldProjectAStringFalseToFalse() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "false" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -98,12 +99,12 @@ public void ShouldProjectAStringFalseToFalse() } [Fact] - public void ShouldProjectAStringZeroToFalse() + public Task ShouldProjectAStringZeroToFalse() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "0" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -112,12 +113,12 @@ public void ShouldProjectAStringZeroToFalse() } [Fact] - public void ShouldProjectAStringNonBooleanValueToFalse() + public Task ShouldProjectAStringNonBooleanValueToFalse() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = "uokyujhygt" }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -126,12 +127,12 @@ public void ShouldProjectAStringNonBooleanValueToFalse() } [Fact] - public void ShouldProjectAStringNullToFalse() + public Task ShouldProjectAStringNullToFalse() { - RunTest(context => + return RunTest(async context => { context.StringItems.Add(new PublicString { Value = null }); - context.SaveChanges(); + await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index c8ad53387..01ee79d16 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -2,6 +2,7 @@ { using System; using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -15,18 +16,18 @@ protected WhenConvertingToDateTimes(ITestContext context) #region Parseable String -> DateTime - protected void RunShouldProjectAParseableStringToADateTime() + protected Task RunShouldProjectAParseableStringToADateTime() => RunTest(ProjectParseableStringToADateTime); - protected void RunShouldErrorProjectingAParseableStringToADateTime() + protected Task RunShouldErrorProjectingAParseableStringToADateTime() => RunTestAndExpectThrow(ProjectParseableStringToADateTime); - private static void ProjectParseableStringToADateTime(TOrmContext context) + private static async Task ProjectParseableStringToADateTime(TOrmContext context) { var now = DateTime.Now; context.StringItems.Add(new PublicString { Value = now.ToString("s") }); - context.SaveChanges(); + await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); @@ -37,16 +38,16 @@ private static void ProjectParseableStringToADateTime(TOrmContext context) #region Null String -> DateTime - protected void RunShouldProjectANullStringToADateTime() + protected Task RunShouldProjectANullStringToADateTime() => RunTest(ProjectANullStringToADateTime); - protected void RunShouldErrorProjectingANullStringToADateTime() + protected Task RunShouldErrorProjectingANullStringToADateTime() => RunTestAndExpectThrow(ProjectANullStringToADateTime); - private static void ProjectANullStringToADateTime(TOrmContext context) + private static async Task ProjectANullStringToADateTime(TOrmContext context) { context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); + await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); @@ -57,16 +58,16 @@ private static void ProjectANullStringToADateTime(TOrmContext context) #region Unparseable String -> DateTime - protected void RunShouldProjectAnUnparseableStringToADateTime() + protected Task RunShouldProjectAnUnparseableStringToADateTime() => RunTest(ProjectAnUnparseableStringToADateTime); - protected void RunShouldErrorProjectingAnUnparseableStringToADateTime() + protected Task RunShouldErrorProjectingAnUnparseableStringToADateTime() => RunTestAndExpectThrow(ProjectAnUnparseableStringToADateTime); - private static void ProjectAnUnparseableStringToADateTime(TOrmContext context) + private static async Task ProjectAnUnparseableStringToADateTime(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); - context.SaveChanges(); + await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs index 5f8654182..7baeb99ba 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,12 +15,12 @@ protected WhenConvertingToDoubles(ITestContext context) } [Fact] - public void ShouldProjectAShortToADouble() + public Task ShouldProjectAShortToADouble() { - RunTest(context => + return RunTest(async context => { context.ShortItems.Add(new PublicShort { Value = 123 }); - context.SaveChanges(); + await context.SaveChanges(); var doubleItem = context.ShortItems.Project().To().First(); @@ -28,12 +29,12 @@ public void ShouldProjectAShortToADouble() } [Fact] - public void ShouldProjectALongToADouble() + public Task ShouldProjectALongToADouble() { - RunTest(context => + return RunTest(async context => { context.LongItems.Add(new PublicLong { Value = 12345L }); - context.SaveChanges(); + await context.SaveChanges(); var doubleItem = context.LongItems.Project().To().First(); @@ -43,16 +44,16 @@ public void ShouldProjectALongToADouble() #region Parseable String -> Double - protected void RunShouldProjectAParseableStringToADouble() + protected Task RunShouldProjectAParseableStringToADouble() => RunTest(ProjectParseableStringToDouble); - protected void RunShouldErrorProjectingAParseableStringToADouble() + protected Task RunShouldErrorProjectingAParseableStringToADouble() => RunTestAndExpectThrow(ProjectParseableStringToDouble); - private static void ProjectParseableStringToDouble(TOrmContext context) + private static async Task ProjectParseableStringToDouble(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "738.01" }); - context.SaveChanges(); + await context.SaveChanges(); var doubleItem = context.StringItems.Project().To().First(); @@ -63,16 +64,16 @@ private static void ProjectParseableStringToDouble(TOrmContext context) #region Null String -> Double - protected void RunShouldProjectANullStringToADouble() + protected Task RunShouldProjectANullStringToADouble() => RunTest(ProjectNullStringToDouble); - protected void RunShouldErrorProjectingANullStringToADouble() + protected Task RunShouldErrorProjectingANullStringToADouble() => RunTestAndExpectThrow(ProjectNullStringToDouble); - private static void ProjectNullStringToDouble(TOrmContext context) + private static async Task ProjectNullStringToDouble(TOrmContext context) { context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); + await context.SaveChanges(); var doubleItem = context.StringItems.Project().To().First(); @@ -83,16 +84,16 @@ private static void ProjectNullStringToDouble(TOrmContext context) #region Unparseable String -> Double - protected void RunShouldProjectAnUnparseableStringToADouble() + protected Task RunShouldProjectAnUnparseableStringToADouble() => RunTest(ProjectUnparseableStringToDouble); - protected void RunShouldErrorProjectingAnUnparseableStringToADouble() + protected Task RunShouldErrorProjectingAnUnparseableStringToADouble() => RunTestAndExpectThrow(ProjectUnparseableStringToDouble); - private static void ProjectUnparseableStringToDouble(TOrmContext context) + private static async Task ProjectUnparseableStringToDouble(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "poioiujygy" }); - context.SaveChanges(); + await context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index dbe258630..c40d3ceb5 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -2,6 +2,7 @@ { using System; using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -15,18 +16,18 @@ protected WhenConvertingToGuids(ITestContext context) #region Parseable String -> Guid - protected void RunShouldProjectAParseableStringToAGuid() + protected Task RunShouldProjectAParseableStringToAGuid() => RunTest(ProjectAParseableStringToAGuid); - protected void RunShouldErrorProjectingAParseableStringToAGuid() + protected Task RunShouldErrorProjectingAParseableStringToAGuid() => RunTestAndExpectThrow(ProjectAParseableStringToAGuid); - private static void ProjectAParseableStringToAGuid(TOrmContext context) + private static async Task ProjectAParseableStringToAGuid(TOrmContext context) { var guid = Guid.NewGuid(); context.StringItems.Add(new PublicString { Value = guid.ToString() }); - context.SaveChanges(); + await context.SaveChanges(); var guidItem = context.StringItems.Project().To().First(); @@ -37,16 +38,16 @@ private static void ProjectAParseableStringToAGuid(TOrmContext context) #region Null String -> Guid - protected void RunShouldProjectANullStringToAGuid() + protected Task RunShouldProjectANullStringToAGuid() => RunTest(ProjectANullStringToAGuid); - protected void RunShouldErrorProjectingANullStringToAGuid() + protected Task RunShouldErrorProjectingANullStringToAGuid() => RunTestAndExpectThrow(ProjectANullStringToAGuid); - private static void ProjectANullStringToAGuid(TOrmContext context) + private static async Task ProjectANullStringToAGuid(TOrmContext context) { context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); + await context.SaveChanges(); var guidItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 60faa7e36..dae6ef46b 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,12 +15,12 @@ protected WhenConvertingToInts(ITestContext context) } [Fact] - public void ShouldProjectAShortToAnInt() + public Task ShouldProjectAShortToAnInt() { - RunTest(context => + return RunTest(async context => { context.ShortItems.Add(new PublicShort { Value = 123 }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.ShortItems.Project().To().First(); @@ -28,12 +29,12 @@ public void ShouldProjectAShortToAnInt() } [Fact] - public void ShouldProjectAnInRangeLongToAnInt() + public Task ShouldProjectAnInRangeLongToAnInt() { - RunTest(context => + return RunTest(async context => { context.LongItems.Add(new PublicLong { Value = 12345L }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -42,12 +43,12 @@ public void ShouldProjectAnInRangeLongToAnInt() } [Fact] - public void ShouldProjectATooBigLongToAnInt() + public Task ShouldProjectATooBigLongToAnInt() { - RunTest(context => + return RunTest(async context => { context.LongItems.Add(new PublicLong { Value = long.MaxValue }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -56,12 +57,12 @@ public void ShouldProjectATooBigLongToAnInt() } [Fact] - public void ShouldProjectATooSmallLongToAnInt() + public Task ShouldProjectATooSmallLongToAnInt() { - RunTest(context => + return RunTest(async context => { context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -71,16 +72,16 @@ public void ShouldProjectATooSmallLongToAnInt() #region Parseable String -> Int - protected void RunShouldProjectAParseableStringToAnInt() + protected Task RunShouldProjectAParseableStringToAnInt() => RunTest(ProjectParseableStringToInt); - protected void RunShouldErrorProjectingAParseableStringToAnInt() + protected Task RunShouldErrorProjectingAParseableStringToAnInt() => RunTestAndExpectThrow(ProjectParseableStringToInt); - private static void ProjectParseableStringToInt(TOrmContext context) + private static async Task ProjectParseableStringToInt(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "738" }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.StringItems.Project().To().First(); @@ -91,16 +92,16 @@ private static void ProjectParseableStringToInt(TOrmContext context) #region Null String -> Int - protected void RunShouldProjectANullStringToAnInt() + protected Task RunShouldProjectANullStringToAnInt() => RunTest(ProjectNullStringToInt); - protected void RunShouldErrorProjectingANullStringToAnInt() + protected Task RunShouldErrorProjectingANullStringToAnInt() => RunTestAndExpectThrow(ProjectNullStringToInt); - private static void ProjectNullStringToInt(TOrmContext context) + private static async Task ProjectNullStringToInt(TOrmContext context) { context.StringItems.Add(new PublicString { Value = default(string) }); - context.SaveChanges(); + await context.SaveChanges(); var intItem = context.StringItems.Project().To().First(); @@ -111,16 +112,16 @@ private static void ProjectNullStringToInt(TOrmContext context) #region Unparseable String -> Int - protected void RunShouldProjectAnUnparseableStringToAnInt() + protected Task RunShouldProjectAnUnparseableStringToAnInt() => RunTest(ProjectUnparseableStringToInt); - protected void RunShouldErrorProjectingAnUnparseableStringToAnInt() + protected Task RunShouldErrorProjectingAnUnparseableStringToAnInt() => RunTestAndExpectThrow(ProjectUnparseableStringToInt); - private static void ProjectUnparseableStringToInt(TOrmContext context) + private static async Task ProjectUnparseableStringToInt(TOrmContext context) { context.StringItems.Add(new PublicString { Value = "hsejk" }); - context.SaveChanges(); + await context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index 50e70b5ee..edf0f1a08 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,12 +15,12 @@ protected WhenConvertingToStrings(ITestContext context) } [Fact] - public void ShouldProjectAnIntToAString() + public Task ShouldProjectAnIntToAString() { - RunTest(context => + return RunTest(async context => { context.IntItems.Add(new PublicInt { Value = 763483 }); - context.SaveChanges(); + await context.SaveChanges(); var stringItem = context.IntItems.Project().To().First(); @@ -28,12 +29,12 @@ public void ShouldProjectAnIntToAString() } [Fact] - public void ShouldProjectABoolToAString() + public Task ShouldProjectABoolToAString() { - RunTest(context => + return RunTest(async context => { context.BoolItems.Add(new PublicBool { Value = true }); - context.SaveChanges(); + await context.SaveChanges(); var stringItem = context.BoolItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 942ec3444..dce9a0d6b 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,16 +15,16 @@ protected WhenProjectingFlatTypes(ITestContext context) } [Fact] - public void ShouldProjectAFlatTypeToAFlatTypeArray() + public Task ShouldProjectAFlatTypeToAFlatTypeArray() { - RunTest(context => + return RunTest(async context => { var product1 = new Product { Name = "Product One" }; var product2 = new Product { Name = "Product Two" }; context.Products.Add(product1); context.Products.Add(product2); - context.SaveChanges(); + await context.SaveChanges(); var productDtos = context .Products @@ -42,14 +43,14 @@ public void ShouldProjectAFlatTypeToAFlatTypeArray() } [Fact] - public void ShouldProjectAFlatTypeToANonMatchingTypeList() + public Task ShouldProjectAFlatTypeToANonMatchingTypeList() { - RunTest(context => + return RunTest(async context => { var product = new Product { Name = "Uno" }; context.Products.Add(product); - context.SaveChanges(); + await context.SaveChanges(); var productDtos = context.Products.Project().To().ToList(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index ed225c511..12242e673 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,9 +15,9 @@ protected WhenProjectingToComplexTypeMembers(ITestContext context) } [Fact] - public void ShouldProjectToAComplexTypeMember() + public Task ShouldProjectToAComplexTypeMember() { - RunTest(context => + return RunTest(async context => { var person = new Person { @@ -29,7 +30,7 @@ public void ShouldProjectToAComplexTypeMember() }; context.Persons.Add(person); - context.SaveChanges(); + await context.SaveChanges(); var personDto = context.Persons.Project().To().First(); @@ -43,14 +44,14 @@ public void ShouldProjectToAComplexTypeMember() } [Fact] - public void ShouldHandleANullComplexTypeMember() + public Task ShouldHandleANullComplexTypeMember() { - RunTest(context => + return RunTest(async context => { var person = new Person { Name = "No Address!" }; context.Persons.Add(person); - context.SaveChanges(); + await context.SaveChanges(); var personDto = context.Persons.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs index 3c92181b1..dc7e2b0f3 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using TestClasses; using Xunit; @@ -14,9 +15,9 @@ protected WhenProjectingToFlatTypes(ITestContext context) } [Fact] - public void ShouldProjectAComplexTypeMemberToAFlatTypeList() + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() { - RunTest(context => + return RunTest(async context => { var person1 = new Person { @@ -33,7 +34,7 @@ public void ShouldProjectAComplexTypeMemberToAFlatTypeList() context.Persons.Add(person1); context.Persons.Add(person2); - context.SaveChanges(); + await context.SaveChanges(); var personViewModels = context .Persons diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs index 4bbc5ba63..2739e5937 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms { using System.Linq; + using System.Threading.Tasks; using Infrastructure; using MoreTestClasses; using ObjectPopulation; @@ -16,9 +17,9 @@ protected WhenViewingMappingPlans(ITestContext context) } [Fact] - public void ShouldCreateAQueryProjectionPlanForASpecificQueryProvider() + public Task ShouldCreateAQueryProjectionPlanForASpecificQueryProvider() { - RunTest(mapper => + return RunTest(mapper => { string plan = mapper .GetPlanForProjecting(Context.Products) @@ -39,13 +40,15 @@ public void ShouldCreateAQueryProjectionPlanForASpecificQueryProvider() var usedMapper = (IObjectMapper)mapper.RootMapperCountShouldBeOne(); usedMapper.ShouldBe(cachedMapper); + + return Task.CompletedTask; }); } [Fact] - public void ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() + public Task ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() { - RunTest(mapper => + return RunTest(mapper => { mapper.GetPlanForProjecting(Context.Products).To(); mapper.GetPlanForProjecting(Context.StringItems).To(); @@ -56,6 +59,8 @@ public void ShouldReturnCachedQueryProjectionPlansInAllCachedPlans() allPlans.ShouldContain("IQueryable -> IQueryable"); allPlans.ShouldContain("IQueryable -> IQueryable"); allPlans.ShouldContain("IQueryable -> IQueryable"); + + return Task.CompletedTask; }); } } diff --git a/AgileMapper.UnitTests/Should.cs b/AgileMapper.UnitTests/Should.cs index bf0123696..18c866966 100644 --- a/AgileMapper.UnitTests/Should.cs +++ b/AgileMapper.UnitTests/Should.cs @@ -1,15 +1,16 @@ namespace AgileObjects.AgileMapper.UnitTests { using System; + using System.Threading.Tasks; public static class Should { - public static TException Throw(Action testAction) + public static TException Throw(Action test) where TException : Exception { return Throw(() => { - testAction.Invoke(); + test.Invoke(); return new object(); }); @@ -30,6 +31,32 @@ public static TException Throw(Func testFunc) throw new Exception("Expected exception of type " + typeof(TException).Name); } + public static Task ThrowAsync(Func test) + where TException : Exception + { + return ThrowAsync(async () => + { + await test.Invoke(); + + return new object(); + }); + } + + public static async Task ThrowAsync(Func> test) + where TException : Exception + { + try + { + await test.Invoke(); + } + catch (TException ex) + { + return ex; + } + + throw new Exception("Expected exception of type " + typeof(TException).Name); + } + public static void NotThrow(Action testAction) => NotThrow(testAction); public static void NotThrow(Action testAction) From f2c03522b40945475daa43a4ad692b775564a5a6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 18:07:18 +0000 Subject: [PATCH 106/176] Test coverage for default recursive projection behaviour / Completing ORM tests' async conversion --- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../Recursion/WhenProjectingCircularReferences.cs | 3 ++- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../WhenConvertingToDoubles.cs | 7 ++++--- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 5 +++-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 7 ++++--- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../Recursion/WhenProjectingCircularReferences.cs | 3 ++- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../WhenConvertingToDoubles.cs | 7 ++++--- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 5 +++-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 7 ++++--- .../Recursion/WhenProjectingCircularReferences.cs | 11 ++++++++--- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../WhenConvertingToDoubles.cs | 7 ++++--- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 5 +++-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 7 ++++--- .../Recursion/WhenProjectingCircularReferences.cs | 11 ++++++++--- .../WhenConvertingToDateTimes.cs | 7 ++++--- .../WhenConvertingToDoubles.cs | 7 ++++--- .../SimpleTypeConversion/WhenConvertingToGuids.cs | 5 +++-- .../SimpleTypeConversion/WhenConvertingToInts.cs | 7 ++++--- .../WhenCreatingProjections.cs | 2 ++ .../IOneToManyRecursionProjectionFailureTest.cs | 4 +++- .../Recursion/IOneToManyRecursionProjectorTest.cs | 10 +++++++++- .../Recursion/WhenProjectingCircularReferences.cs | 15 +++++++++++---- .../IStringConversionFailureTest.cs | 6 ++++-- .../IStringConversionValidationFailureTest.cs | 4 +++- .../IStringConversionValidatorTest.cs | 4 +++- .../SimpleTypeConversion/IStringConverterTest.cs | 6 ++++-- .../IFullProjectionInlineConfigurator.cs | 6 +++--- .../Projection/RecursionDepthSettings.cs | 6 +++--- AgileMapper/Configuration/UserConfigurationSet.cs | 2 +- 33 files changed, 134 insertions(+), 77 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index b37a0971c..c486e243f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; @@ -16,15 +17,15 @@ public WhenConvertingToDateTimes(LocalDbTestContext conte } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldProjectAnUnparseableString() + public Task ShouldProjectAnUnparseableString() => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs index 7a3805599..08108db1d 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Recursion/WhenProjectingCircularReferences.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Recursion { + using System.Threading.Tasks; using Infrastructure; using Orms.Recursion; using Xunit; @@ -14,7 +15,7 @@ public WhenProjectingCircularReferences(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + public Task ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() => DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 17af52b67..042e9b456 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADateTime(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs index 4a93b639f..c7981d37a 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -16,15 +17,15 @@ public WhenConvertingToDoubles(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADouble(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADouble(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADouble(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs index c25cb94ee..315bfc799 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAGuid(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs index c3d61163a..1e0464066 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -16,15 +17,15 @@ public WhenConvertingToInts(InMemoryEf5TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAnInt(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs index c15556969..628cebe64 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; @@ -16,15 +17,15 @@ public WhenConvertingToDateTimes(LocalDbTestContext conte } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldProjectAnUnparseableString() + public Task ShouldProjectAnUnparseableString() => RunShouldProjectAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs index e51d487c1..fdabe83b9 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Recursion/WhenProjectingCircularReferences.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Recursion { + using System.Threading.Tasks; using Infrastructure; using Orms.Recursion; using Xunit; @@ -14,7 +15,7 @@ public WhenProjectingCircularReferences(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() + public Task ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() => DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 9582c1e88..ba97aad5e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADateTime(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs index 43a2fa2fd..901189f0e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -16,15 +17,15 @@ public WhenConvertingToDoubles(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToADouble(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToADouble(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADouble(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs index f56022435..43070ee28 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAGuid(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs index 8be83e7a2..2985146fa 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToInts(InMemoryEf6TestContext context) } [Fact] - public void ShouldErrorProjectingAParseableString() + public Task ShouldErrorProjectingAParseableString() => RunShouldErrorProjectingAParseableStringToAnInt(); [Fact] - public void ShouldErrorProjectingANullString() + public Task ShouldErrorProjectingANullString() => RunShouldErrorProjectingANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs index ca7772d6a..0eaebc7ea 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Recursion { + using System.Threading.Tasks; using Infrastructure; using Orms.Recursion; using Xunit; @@ -14,15 +15,19 @@ public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); [Fact] - public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); [Fact] - public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); + + [Fact] + public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 29dab15f1..af642eb6b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs index f067e152a..3233f892b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDoubles(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADouble(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADouble(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADouble(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs index 4ff46222d..af8ba9704 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAGuid(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs index bea0b1fc5..0b6553e3c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToInts(InMemoryEfCore1TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAnInt(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs index d9fe0270b..f6695dc02 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Recursion { + using System.Threading.Tasks; using Infrastructure; using Orms.Recursion; using Xunit; @@ -14,15 +15,19 @@ public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); [Fact] - public void ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() => DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); [Fact] - public void ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() + public Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); + + [Fact] + public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs index dcbd6aa79..a4ed23a25 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDateTimes(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADateTime(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADateTime(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADateTime(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs index 1fd67c41d..0a8b0a831 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToDoubles(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToADouble(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToADouble(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToADouble(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs index 7997c385a..9c8f4aeab 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -14,11 +15,11 @@ public WhenConvertingToGuids(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAGuid(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToAGuid(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index a2bf251ac..15113e1ef 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; using Xunit; @@ -15,15 +16,15 @@ public WhenConvertingToInts(InMemoryEfCore2TestContext context) } [Fact] - public void ShouldProjectAParseableString() + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAnInt(); [Fact] - public void ShouldProjectANullString() + public Task ShouldProjectANullString() => RunShouldProjectANullStringToAnInt(); [Fact] - public void ShouldErrorProjectingAnUnparseableString() + public Task ShouldErrorProjectingAnUnparseableString() => RunShouldErrorProjectingAnUnparseableStringToAnInt(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index 20f2b4b25..fe7eb9dda 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -30,6 +30,8 @@ public Task ShouldReuseACachedProjectionMapper() stringDtos.ShouldBeEmpty(); } + mapper.RootMapperCountShouldBeOne(); + using (var context2 = new EfCore2TestDbContext()) { context2.StringItems.Add(new PublicString { Id = 1, Value = "New!" }); diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs index 6996646fc..95a152e9c 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectionFailureTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion { + using System.Threading.Tasks; + public interface IOneToManyRecursionProjectionFailureTest { - void ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); + Task ShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs index e0c4bbad1..2295d68c0 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs @@ -1,7 +1,15 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion { + using System.Threading.Tasks; + public interface IOneToManyRecursionProjectorTest { - void ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + + Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); + + Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); + + Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index 02556b0d3..bd9d2741f 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -81,8 +81,11 @@ protected Task DoShouldProjectAOneToManyRelationshipToFirstRecursionDepth() protected Task DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(2, context)); + protected Task DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() + => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(null, context)); + protected async Task ProjectAOneToManyRelationshipToRecursionDepth( - int depth, + int? depth, TOrmContext context) { var topLevel = new Category { Name = "Top Level" }; @@ -118,9 +121,13 @@ protected async Task ProjectAOneToManyRelationshipToRecursionDepth( await context.SaveChanges(); - var topLevelDto = context - .Categories - .Project().To(cfg => cfg.RecurseToDepth(depth)) + var query = depth.HasValue + ? context.Categories + .Project().To(cfg => cfg.RecurseToDepth(depth.Value)) + : context.Categories + .Project().To(); + + var topLevelDto = query .OrderBy(c => c.Id) .First(c => c.Name == "Top Level"); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs index 32bfefa37..4eaa046a4 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionFailureTest.cs @@ -1,9 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { + using System.Threading.Tasks; + public interface IStringConversionFailureTest { - void ShouldErrorProjectingAParseableString(); + Task ShouldErrorProjectingAParseableString(); - void ShouldErrorProjectingANullString(); + Task ShouldErrorProjectingANullString(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs index 58ac79148..8eea0c7cb 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidationFailureTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { + using System.Threading.Tasks; + public interface IStringConversionValidationFailureTest { - void ShouldErrorProjectingAnUnparseableString(); + Task ShouldErrorProjectingAnUnparseableString(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs index 10bcb3347..26a9c9ad5 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConversionValidatorTest.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { + using System.Threading.Tasks; + public interface IStringConversionValidatorTest { - void ShouldProjectAnUnparseableString(); + Task ShouldProjectAnUnparseableString(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs index d9a259a14..e820de867 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/IStringConverterTest.cs @@ -1,9 +1,11 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { + using System.Threading.Tasks; + public interface IStringConverterTest { - void ShouldProjectAParseableString(); + Task ShouldProjectAParseableString(); - void ShouldProjectANullString(); + Task ShouldProjectANullString(); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index c5b32ef71..5b84b2931 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -8,12 +8,12 @@ public interface IFullProjectionInlineConfigurator { /// - /// Project recursive relationships to the specified ; default is 1. + /// Project recursive relationships to the specified . /// For example, when projecting a Category entity which has a SubCategories property of Type /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the - /// sub-categories of the sub-categories of the top-level Category selected, etc. A recursion depth of - /// zero will only populate the first level of sub-categories. + /// sub-categories of the sub-categories of the top-level Category selected, etc. The default is zero, + /// which only populates the first level of sub-categories. /// /// The depth to which to populate projected recursive relationships. IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); diff --git a/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs b/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs index b81ce6f0d..1317d67ac 100644 --- a/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs +++ b/AgileMapper/Configuration/Projection/RecursionDepthSettings.cs @@ -12,11 +12,11 @@ public RecursionDepthSettings(MappingConfigInfo configInfo, int recursionDepth) _recursionDepth = recursionDepth; } - public bool IsWithinDepth(IBasicMapperData mapperData) + public bool IsBeyondDepth(IBasicMapperData mapperData) { if (_recursionDepth == 0) { - return false; + return true; } var recursionDepth = -1; @@ -31,7 +31,7 @@ public bool IsWithinDepth(IBasicMapperData mapperData) mapperData = mapperData.Parent; } - return recursionDepth <= _recursionDepth; + return recursionDepth > _recursionDepth; } } } diff --git a/AgileMapper/Configuration/UserConfigurationSet.cs b/AgileMapper/Configuration/UserConfigurationSet.cs index 5f6d277ca..3840789be 100644 --- a/AgileMapper/Configuration/UserConfigurationSet.cs +++ b/AgileMapper/Configuration/UserConfigurationSet.cs @@ -245,7 +245,7 @@ public bool ShortCircuitRecursion(IBasicMapperData mapperData) return true; } - return RecursionDepthSettings.FindMatch(mapperData)?.IsWithinDepth(mapperData) != true; + return RecursionDepthSettings.FindMatch(mapperData)?.IsBeyondDepth(mapperData) != false; } #endregion From 850a5bcc474a827be400604ecae380941d7a0858 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 4 Feb 2018 18:09:33 +0000 Subject: [PATCH 107/176] Removing explicit zeroeth-depth ORM recursion tests --- .../Recursion/WhenProjectingCircularReferences.cs | 8 ++------ .../Recursion/WhenProjectingCircularReferences.cs | 8 ++------ .../Recursion/IOneToManyRecursionProjectorTest.cs | 4 +--- .../Recursion/WhenProjectingCircularReferences.cs | 3 --- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs index 0eaebc7ea..74c9ab960 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Recursion/WhenProjectingCircularReferences.cs @@ -15,8 +15,8 @@ public WhenProjectingCircularReferences(InMemoryEfCore1TestContext context) } [Fact] - public Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); [Fact] public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() @@ -25,9 +25,5 @@ public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() [Fact] public Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); - - [Fact] - public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs index f6695dc02..1770d8411 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Recursion/WhenProjectingCircularReferences.cs @@ -15,8 +15,8 @@ public WhenProjectingCircularReferences(InMemoryEfCore2TestContext context) } [Fact] - public Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() + => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); [Fact] public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() @@ -25,9 +25,5 @@ public Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth() [Fact] public Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth() => DoShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); - - [Fact] - public Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth() - => DoShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs index 2295d68c0..0cfdea28c 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/IOneToManyRecursionProjectorTest.cs @@ -4,12 +4,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Recursion public interface IOneToManyRecursionProjectorTest { - Task ShouldProjectAOneToManyRelationshipToZeroethRecursionDepth(); + Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); Task ShouldProjectAOneToManyRelationshipToFirstRecursionDepth(); Task ShouldProjectAOneToManyRelationshipToSecondRecursionDepth(); - - Task ShouldProjectAOneToManyRelationshipToDefaultRecursionDepth(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index bd9d2741f..b1e700b71 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -69,9 +69,6 @@ public Task ShouldProjectAOneToOneRelationship() #region Project One-to-Many - protected Task DoShouldProjectAOneToManyRelationshipToZeroethRecursionDepth() - => RunTest(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); - protected Task DoShouldErrorProjectingAOneToManyRelationshipToZeroethRecursionDepth() => RunTestAndExpectThrow(context => ProjectAOneToManyRelationshipToRecursionDepth(0, context)); From e7a78113d2311a387eb7baf6221deb322a306f76 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 08:38:08 +0000 Subject: [PATCH 108/176] Start of ORM configuration tests --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringDataSources.cs | 19 ++++++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + .../WhenConfiguringDataSources.cs | 43 +++++++++++++++++++ .../Api/Configuration/MappingConfigurator.cs | 15 +++++++ .../Projection/IFullProjectionConfigurator.cs | 33 ++++++++++++++ .../IFullProjectionInlineConfigurator.cs | 15 ++----- .../Projection/ProjectionConfigurator.cs | 25 ----------- .../Api/Configuration/TargetSpecifier.cs | 31 ++++++++----- .../Inline/InlineMapperContextSet.cs | 10 ++++- 10 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs delete mode 100644 AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 373b83242..1d7956842 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -139,6 +139,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..2602d1cb2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDataSources : WhenConfiguringDataSources + { + public WhenConfiguringDataSources(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstant() + => DoShouldApplyAConfiguredConstant(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 1a7567ddb..5d4f7c9ac 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -139,6 +139,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..61c1e0e75 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + + public abstract class WhenConfiguringDataSources : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringDataSources(ITestContext context) + : base(context) + { + } + + protected Task DoShouldApplyAConfiguredConstant() + { + return RunTest(async context => + { + var product = new Product { Name = "P1" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("PRODUCT") + .To(dto => dto.Name); + + var productDto = context + .Products + .Project().To(_ => _.Using(mapper)) + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("PRODUCT"); + } + }); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 65eb7ea12..01ce540be 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -4,14 +4,17 @@ using System.Linq.Expressions; using System.Reflection; using AgileMapper.Configuration; + using AgileMapper.Configuration.Projection; using Dictionaries; using Dynamics; using Extensions.Internal; using Members; + using Projection; using Validation; internal class MappingConfigurator : IFullMappingInlineConfigurator, + IFullProjectionInlineConfigurator, IConditionalRootMappingConfigurator { public MappingConfigurator(MappingConfigInfo configInfo) @@ -77,6 +80,18 @@ public IFullMappingInlineConfigurator UseNamePatterns(params s #endregion + #region IFullProjectionInlineConfigurator Members + + public IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth) + { + var depthSettings = new RecursionDepthSettings(ConfigInfo, recursionDepth); + + ConfigInfo.MapperContext.UserConfigurations.Add(depthSettings); + return this; + } + + #endregion + #region If Overloads public IConditionalRootMappingConfigurator If( diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs new file mode 100644 index 000000000..fda1558e8 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs @@ -0,0 +1,33 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for configuring query projections from and to given source and result element Types. + /// + /// The source element Type to which the configuration should apply. + /// The result element Type to which the configuration should apply. + public interface IFullProjectionConfigurator + { + /// + /// Project recursive relationships to the specified . + /// For example, when projecting a Category entity which has a SubCategories property of Type + /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories + /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the + /// sub-categories of the sub-categories of the top-level Category selected, etc. The default is zero, + /// which only populates the first level of sub-categories. + /// + /// The depth to which to populate projected recursive relationships. + IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); + + /// + /// Configure a constant value for a particular target member when projecting from and to the source and + /// result types being configured. + /// + /// The type of the custom constant value being configured. + /// The constant value to map to the configured result member. + /// + /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom + /// constant value should be applied. + /// + CustomDataSourceTargetMemberSpecifier Map(TSourceValue value); + } +} diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index 5b84b2931..eb10baa4a 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -1,21 +1,12 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Projection { /// - /// Provides options for configuring query projections from and to given source and target element Types, inline. + /// Provides options for configuring query projections from and to given source and result element Types, inline. /// /// The source element Type to which the configuration should apply. /// The result element Type to which the configuration should apply. - public interface IFullProjectionInlineConfigurator + public interface IFullProjectionInlineConfigurator : + IFullProjectionConfigurator { - /// - /// Project recursive relationships to the specified . - /// For example, when projecting a Category entity which has a SubCategories property of Type - /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories - /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the - /// sub-categories of the sub-categories of the top-level Category selected, etc. The default is zero, - /// which only populates the first level of sub-categories. - /// - /// The depth to which to populate projected recursive relationships. - IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); } } diff --git a/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs deleted file mode 100644 index 3b58aa55c..000000000 --- a/AgileMapper/Api/Configuration/Projection/ProjectionConfigurator.cs +++ /dev/null @@ -1,25 +0,0 @@ -using AgileObjects.AgileMapper.Configuration; - -namespace AgileObjects.AgileMapper.Api.Configuration.Projection -{ - using AgileMapper.Configuration.Projection; - - internal class ProjectionConfigurator - : IFullProjectionInlineConfigurator - { - private readonly MappingConfigInfo _configInfo; - - public ProjectionConfigurator(MappingConfigInfo configInfo) - { - _configInfo = configInfo; - } - - public IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth) - { - var depthSettings = new RecursionDepthSettings(_configInfo, recursionDepth); - - _configInfo.MapperContext.UserConfigurations.Add(depthSettings); - return this; - } - } -} diff --git a/AgileMapper/Api/Configuration/TargetSpecifier.cs b/AgileMapper/Api/Configuration/TargetSpecifier.cs index 6ed561cd3..34e1cb232 100644 --- a/AgileMapper/Api/Configuration/TargetSpecifier.cs +++ b/AgileMapper/Api/Configuration/TargetSpecifier.cs @@ -1,10 +1,9 @@ namespace AgileObjects.AgileMapper.Api.Configuration { - using System.Collections.Generic; using AgileMapper.Configuration; - using AgileMapper.Configuration.Dictionaries; using Dictionaries; using Dynamics; + using Projection; /// /// Provides options for specifying the target type and mapping rule set to which the configuration should @@ -22,7 +21,8 @@ internal TargetSpecifier(MappingConfigInfo configInfo) /// /// Configure how this mapper performs mappings from the source type being configured in all MappingRuleSets - /// (create new, overwrite, etc), to the target type specified by the type argument. + /// (create new, overwrite, etc), to the target type specified by the given + /// argument. /// /// The target type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. @@ -30,17 +30,18 @@ public IFullMappingConfigurator To() => new MappingConfigurator(_configInfo.ForAllRuleSets()); /// - /// Configure how this mapper performs mappings from the source type being configured to the target - /// type specified by the type argument when mapping to new objects. + /// Configure how this mapper performs mappings from the source type being configured to the result + /// type specified by the given argument when mapping to new objects. /// - /// The target type to which the configuration will apply. + /// The result type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. - public IFullMappingConfigurator ToANew() - => UsingRuleSet(Constants.CreateNew); + public IFullMappingConfigurator ToANew() + => UsingRuleSet(Constants.CreateNew); /// /// Configure how this mapper performs mappings from the source type being configured to the target - /// type specified by the type argument when performing OnTo (merge) mappings. + /// type specified by the given argument when performing OnTo (merge) + /// mappings. /// /// The target type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. @@ -49,13 +50,23 @@ public IFullMappingConfigurator OnTo() /// /// Configure how this mapper performs mappings from the source type being configured to the target - /// type specified by the type argument when performing Over (overwrite) mappings. + /// type specified by the given argument when performing Over (overwrite) + /// mappings. /// /// The target type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. public IFullMappingConfigurator Over() => UsingRuleSet(Constants.Overwrite); + /// + /// Configure how this mapper performs query projections from the source type being configured to the + /// result type specified by the given argument. + /// + /// The result type to which the configuration will apply. + /// An IFullProjectionConfigurator with which to complete the configuration. + public IFullProjectionConfigurator ProjectedTo() + => UsingRuleSet(Constants.Project); + private MappingConfigurator UsingRuleSet(string name) => new MappingConfigurator(_configInfo.ForRuleSet(name)); diff --git a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs index e7a5a3dcb..060cc666a 100644 --- a/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs +++ b/AgileMapper/Configuration/Inline/InlineMapperContextSet.cs @@ -25,7 +25,7 @@ public MapperContext GetContextFor( { return GetContextFor>( configurations, - configInfo => new MappingConfigurator(configInfo), + CreateMappingConfigurator, executor); } @@ -35,10 +35,16 @@ public MapperContext GetContextFor( { return GetContextFor>( configurations, - configInfo => new ProjectionConfigurator(configInfo), + CreateMappingConfigurator, _queryProjectionMappingContext); } + private static MappingConfigurator CreateMappingConfigurator( + MappingConfigInfo configInfo) + { + return new MappingConfigurator(configInfo); + } + private MapperContext GetContextFor( Expression>[] configurations, Func configuratorFactory, From a3b2956d9e918ef628b3b934cccb4582b9bfc735 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 08:51:56 +0000 Subject: [PATCH 109/176] Moving mapper specification from To() to Project() --- .../WhenCreatingProjections.cs | 4 +-- .../WhenConfiguringDataSources.cs | 2 +- AgileMapper/Api/IProjectionResultSpecifier.cs | 18 ------------- AgileMapper/ProjectionExecutor.cs | 26 +++++-------------- AgileMapper/ProjectionExtensions.cs | 26 ++++++++++++++++--- 5 files changed, 32 insertions(+), 44 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index fe7eb9dda..a713e4bc9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -24,7 +24,7 @@ public Task ShouldReuseACachedProjectionMapper() { var stringDtos = await context1 .StringItems - .Project().To(c => c.Using(mapper)) + .Project(_ => _.Using(mapper)).To() .ToListAsync(); stringDtos.ShouldBeEmpty(); @@ -39,7 +39,7 @@ public Task ShouldReuseACachedProjectionMapper() var moreStringDtos = await context2 .StringItems - .Project().To(c => c.Using(mapper)) + .Project(_ => _.Using(mapper)).To() .ToArrayAsync(); moreStringDtos.ShouldHaveSingleItem(); diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 61c1e0e75..b5f7e6496 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -31,7 +31,7 @@ protected Task DoShouldApplyAConfiguredConstant() var productDto = context .Products - .Project().To(_ => _.Using(mapper)) + .Project(_ => _.Using(mapper)).To() .ShouldHaveSingleItem(); productDto.ProductId.ShouldBe(product.ProductId); diff --git a/AgileMapper/Api/IProjectionResultSpecifier.cs b/AgileMapper/Api/IProjectionResultSpecifier.cs index eb12f5270..c9a705251 100644 --- a/AgileMapper/Api/IProjectionResultSpecifier.cs +++ b/AgileMapper/Api/IProjectionResultSpecifier.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Linq.Expressions; using Configuration.Projection; - using Queryables.Api; /// /// Provides options for specifying the query projection result Type. @@ -30,23 +29,6 @@ public interface IProjectionResultSpecifier IQueryable To() where TResultElement : class; - /// - /// Project the elements of the source IQueryable{T} to instances of the given - /// , using the mapper specified by the given - /// . - /// - /// A func providing the mapper with which the projection should be performed. - /// - /// The result Type to which the elements of the source IQueryable{T} should be projected. - /// - /// - /// An IQueryable{TResultElement} of the source IQueryable{T} projected to instances of the given - /// . The projection is not performed until the Queryable is - /// enumerated by a call to .ToArray() or similar. - /// - IQueryable To(Func mapperSelector) - where TResultElement : class; - /// /// Project the elements of the source IQueryable{T} to instances of the given /// , using the default mapper and the given diff --git a/AgileMapper/ProjectionExecutor.cs b/AgileMapper/ProjectionExecutor.cs index 806d65273..c3cb7026c 100644 --- a/AgileMapper/ProjectionExecutor.cs +++ b/AgileMapper/ProjectionExecutor.cs @@ -6,49 +6,35 @@ using Api; using Api.Configuration.Projection; using ObjectPopulation; - using Queryables.Api; internal class ProjectionExecutor : IProjectionResultSpecifier { + private readonly MapperContext _mapperContext; private readonly IQueryable _sourceQueryable; - public ProjectionExecutor(IQueryable sourceQueryable) + public ProjectionExecutor(MapperContext mapperContext, IQueryable sourceQueryable) { + _mapperContext = mapperContext; _sourceQueryable = sourceQueryable; } #region To Overloads IQueryable IProjectionResultSpecifier.To() - { - return PerformProjection(Mapper.Default); - } - - IQueryable IProjectionResultSpecifier.To( - Func mapperSelector) - { - var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); - - return PerformProjection(mapper); - } + => PerformProjection(_mapperContext); IQueryable IProjectionResultSpecifier.To( Expression>> configuration) { - return PerformProjection(Mapper.Default, new[] { configuration }); + return PerformProjection(new[] { configuration }); } #endregion - private IQueryable PerformProjection(IMapper mapper) - => PerformProjection(((IMapperInternal)mapper).Context); - private IQueryable PerformProjection( - IMapper mapper, Expression>>[] configurations) { - var mapperContext = ((IMapperInternal)mapper).Context; - var inlineMapperContext = mapperContext.InlineContexts.GetContextFor(configurations, this); + var inlineMapperContext = _mapperContext.InlineContexts.GetContextFor(configurations, this); return PerformProjection(inlineMapperContext); } diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index c578d7983..a07462d08 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper { + using System; using System.Linq; using Api; + using Queryables.Api; /// /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. @@ -10,15 +12,33 @@ public static class ProjectionExtensions { /// /// Project the elements of the given to instances of a specified - /// result Type. The projection operation is performed entirely on the data source. + /// result Type, using a mapper provided by a given , or the default + /// mapper if none is supplied. The projection operation is performed entirely on the data source. /// /// The Type of the elements to project to a new result Type. /// The source IQueryable{T} on which to perform the projection. + /// + /// A func providing the mapper with which the projection should be performed. If not supplied, the default + /// mapper will be used. + /// /// An IProjectionResultSpecifier with which to specify the type of query projection to perform. public static IProjectionResultSpecifier Project( - this IQueryable sourceQueryable) + this IQueryable sourceQueryable, + Func mapperSelector = null) { - return new ProjectionExecutor(sourceQueryable); + MapperContext mapperContext; + + if (mapperSelector != null) + { + var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); + mapperContext = ((IMapperInternal)mapper).Context; + } + else + { + mapperContext = Mapper.Default.Context; + } + + return new ProjectionExecutor(mapperContext, sourceQueryable); } } } From 65d9d598cec5b7bd17dfff6ab6b35c5880716dca Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 08:54:13 +0000 Subject: [PATCH 110/176] Constant value configuration tests for EFCore1, EF5 and EF6 --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringDataSources.cs | 19 +++++++++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringDataSources.cs | 19 +++++++++++++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringDataSources.cs | 19 +++++++++++++++++++ 6 files changed, 60 insertions(+) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 7fbdc76c9..4e3cabffe 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -85,6 +85,7 @@ VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..3cf335cd2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDataSources : WhenConfiguringDataSources + { + public WhenConfiguringDataSources(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstant() + => DoShouldApplyAConfiguredConstant(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 110bac6bd..673546a16 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,6 +87,7 @@ VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..36df89667 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDataSources : WhenConfiguringDataSources + { + public WhenConfiguringDataSources(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstant() + => DoShouldApplyAConfiguredConstant(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 7ead4a13f..dda2ea585 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -205,6 +205,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..dc291ed58 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDataSources : WhenConfiguringDataSources + { + public WhenConfiguringDataSources(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstant() + => DoShouldApplyAConfiguredConstant(); + } +} \ No newline at end of file From 3e2618bef453e376d1612682efc4be53ec5a9d25 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 10:52:55 +0000 Subject: [PATCH 111/176] Support for conditional configured data sources in query projection --- .../WhenConfiguringDataSources.cs | 4 + .../WhenConfiguringDataSources.cs | 37 ++++++++ .../Api/Configuration/MappingConfigurator.cs | 12 ++- .../IConditionalRootProjectionConfigurator.cs | 13 +++ .../Projection/IFullProjectionConfigurator.cs | 21 +++-- .../Projection/IRootProjectionConfigurator.cs | 22 +++++ .../Configuration/ConfiguredLambdaInfo.cs | 27 +++++- .../Configuration/ParametersSwapper.cs | 91 +++++++++++-------- 8 files changed, 171 insertions(+), 56 deletions(-) create mode 100644 AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index 2602d1cb2..ee476cef0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -15,5 +15,9 @@ public WhenConfiguringDataSources(InMemoryEfCore2TestContext context) [Fact] public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredConstant() + => DoShouldConditionallyApplyAConfiguredConstant(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index b5f7e6496..f2477cef0 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration { + using System.Linq; using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -39,5 +40,41 @@ protected Task DoShouldApplyAConfiguredConstant() } }); } + + protected Task DoShouldConditionallyApplyAConfiguredConstant() + { + return RunTest(async context => + { + var product1 = new Product { Name = "P1" }; + var product2 = new Product { Name = "P2" }; + + context.Products.Add(product1); + context.Products.Add(product2); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Name == "P2") + .Map("PRODUCT!?") + .To(dto => dto.Name); + + var productDtos = context + .Products + .Project(_ => _.Using(mapper)).To() + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P1"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("PRODUCT!?"); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 01ce540be..6cab8e0d7 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -15,7 +15,8 @@ internal class MappingConfigurator : IFullMappingInlineConfigurator, IFullProjectionInlineConfigurator, - IConditionalRootMappingConfigurator + IConditionalRootMappingConfigurator, + IConditionalRootProjectionConfigurator { public MappingConfigurator(MappingConfigInfo configInfo) { @@ -90,13 +91,18 @@ public IFullProjectionInlineConfigurator RecurseToDepth(int re return this; } + public IConditionalRootProjectionConfigurator If(Expression> condition) + => SetCondition(condition); + #endregion #region If Overloads public IConditionalRootMappingConfigurator If( Expression, bool>> condition) - => SetCondition(condition); + { + return SetCondition(condition); + } public IConditionalRootMappingConfigurator If(Expression> condition) => SetCondition(condition); @@ -104,7 +110,7 @@ public IConditionalRootMappingConfigurator If(Expression If(Expression> condition) => SetCondition(condition); - private IConditionalRootMappingConfigurator SetCondition(LambdaExpression conditionLambda) + private MappingConfigurator SetCondition(LambdaExpression conditionLambda) { ConfigInfo.AddConditionOrThrow(conditionLambda); return this; diff --git a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs new file mode 100644 index 000000000..7f869d908 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for configuring a mapping based on the preceding condition. + /// + /// The source type to which the configuration should apply. + /// The result type to which the configuration should apply. + public interface IConditionalRootProjectionConfigurator : + IRootProjectionConfigurator + { + + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs index fda1558e8..d4bee1769 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs @@ -1,11 +1,15 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Projection { + using System; + using System.Linq.Expressions; + /// /// Provides options for configuring query projections from and to given source and result element Types. /// /// The source element Type to which the configuration should apply. /// The result element Type to which the configuration should apply. - public interface IFullProjectionConfigurator + public interface IFullProjectionConfigurator : + IRootProjectionConfigurator { /// /// Project recursive relationships to the specified . @@ -19,15 +23,12 @@ public interface IFullProjectionConfigurator IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); /// - /// Configure a constant value for a particular target member when projecting from and to the source and - /// result types being configured. + /// Configure a condition which must evaluate to true for the configuration to apply. The condition + /// expression is passed the source element being projected. /// - /// The type of the custom constant value being configured. - /// The constant value to map to the configured result member. - /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom - /// constant value should be applied. - /// - CustomDataSourceTargetMemberSpecifier Map(TSourceValue value); + /// The condition to evaluate. + /// An IConditionalRootProjectionConfigurator with which to complete the configuration. + IConditionalRootProjectionConfigurator If( + Expression> condition); } } diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs new file mode 100644 index 000000000..0727cabd8 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -0,0 +1,22 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for configuring projections from and to a given source and result type. + /// + /// The source type to which the configuration should apply. + /// The result type to which the configuration should apply. + public interface IRootProjectionConfigurator + { + /// + /// Configure a constant value for a particular target member when projecting from and to the source and + /// result types being configured. + /// + /// The type of the custom constant value being configured. + /// The constant value to map to the configured result member. + /// + /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom + /// constant value should be applied. + /// + CustomDataSourceTargetMemberSpecifier Map(TSourceValue value); + } +} \ No newline at end of file diff --git a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs index 47b2424d9..56b58b5f5 100644 --- a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs +++ b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs @@ -11,14 +11,17 @@ internal class ConfiguredLambdaInfo { private readonly LambdaExpression _lambda; + private readonly Type[] _contextTypes; private readonly ParametersSwapper _parametersSwapper; private ConfiguredLambdaInfo( LambdaExpression lambda, + Type[] contextTypes, Type returnType, ParametersSwapper parametersSwapper) { _lambda = lambda; + _contextTypes = contextTypes; _parametersSwapper = parametersSwapper; ReturnType = returnType; } @@ -28,10 +31,25 @@ private ConfiguredLambdaInfo( public static ConfiguredLambdaInfo For(LambdaExpression lambda) { var funcArguments = lambda.Parameters.Select(p => p.Type).ToArray(); - var contextTypes = (funcArguments.Length != 1) ? funcArguments : funcArguments[0].GetGenericTypeArguments(); + var contextTypes = GetContextTypes(funcArguments); var parameterSwapper = ParametersSwapper.For(contextTypes, funcArguments); - return new ConfiguredLambdaInfo(lambda, lambda.ReturnType, parameterSwapper); + return new ConfiguredLambdaInfo(lambda, contextTypes, lambda.ReturnType, parameterSwapper); + } + + private static Type[] GetContextTypes(Type[] funcArguments) + { + if (funcArguments.Length != 1) + { + return funcArguments; + } + + if (funcArguments[0].IsGenericType()) + { + return funcArguments[0].GetGenericTypeArguments(); + } + + return new[] { funcArguments[0], typeof(object) }; } public static ConfiguredLambdaInfo ForFunc(TFunc func, params Type[] argumentTypes) @@ -97,6 +115,7 @@ private static ConfiguredLambdaInfo For( return new ConfiguredLambdaInfo( valueFactoryLambda, + contextTypes, returnTypeFactory.Invoke(funcTypes), parameterSwapper); } @@ -129,8 +148,8 @@ public Expression GetBody( QualifiedMember targetMember = null) { return position.IsPriorToObjectCreation(targetMember) - ? _parametersSwapper.Swap(_lambda, mapperData, ParametersSwapper.UseTargetMember) - : _parametersSwapper.Swap(_lambda, mapperData, ParametersSwapper.UseTargetInstance); + ? _parametersSwapper.Swap(_lambda, _contextTypes, mapperData, ParametersSwapper.UseTargetMember) + : _parametersSwapper.Swap(_lambda, _contextTypes, mapperData, ParametersSwapper.UseTargetInstance); } } } \ No newline at end of file diff --git a/AgileMapper/Configuration/ParametersSwapper.cs b/AgileMapper/Configuration/ParametersSwapper.cs index f2104f331..97889cbcd 100644 --- a/AgileMapper/Configuration/ParametersSwapper.cs +++ b/AgileMapper/Configuration/ParametersSwapper.cs @@ -18,6 +18,7 @@ internal class ParametersSwapper { new ParametersSwapper(0, (ct, ft) => true, SwapNothing), new ParametersSwapper(1, IsContext, SwapForContextParameter), + new ParametersSwapper(1, IsSource, SwapForSource), new ParametersSwapper(2, IsSourceAndTarget, SwapForSourceAndTarget), new ParametersSwapper(3, IsSourceTargetAndIndex, SwapForSourceTargetAndIndex), new ParametersSwapper(3, IsSourceTargetAndCreatedObject, SwapForSourceTargetAndCreatedObject), @@ -53,8 +54,11 @@ private static bool Is( return parametersChecker.Invoke(contextTypes, contextTypeArgument.GetGenericTypeArguments()); } + private static bool IsSource(Type[] contextTypes, Type[] funcArguments) + => contextTypes[0].IsAssignableTo(funcArguments[0]); + private static bool IsSourceAndTarget(Type[] contextTypes, Type[] funcArguments) - => contextTypes[0].IsAssignableTo(funcArguments[0]) && contextTypes[1].IsAssignableTo(funcArguments[1]); + => IsSource(contextTypes, funcArguments) && contextTypes[1].IsAssignableTo(funcArguments[1]); private static bool IsSourceTargetAndIndex(Type[] contextTypes, Type[] funcArguments) => IsSourceAndTarget(contextTypes, funcArguments) && IsIndex(funcArguments); @@ -82,7 +86,7 @@ private static Expression SwapForContextParameter(SwapArgs swapArgs) } var contextTypes = contextType.GetGenericTypeArguments(); - var contextInfo = GetAppropriateMappingContext(contextTypes, swapArgs); + var contextInfo = GetAppropriateMappingContext(swapArgs); if (swapArgs.Lambda.Body.NodeType == ExpressionType.Invoke) { @@ -132,6 +136,9 @@ private static Expression GetInvocationContextArgument(MappingContextInfo contex return lambda.ReplaceParameterWith(createObjectCreationContextCall); } + private static Expression SwapForSource(SwapArgs swapArgs) => + ReplaceParameters(swapArgs, c => c.SourceAccess); + private static Expression SwapForSourceAndTarget(SwapArgs swapArgs) => ReplaceParameters(swapArgs, c => c.SourceAccess, c => c.TargetAccess); @@ -148,23 +155,21 @@ private static Expression ReplaceParameters( SwapArgs swapArgs, params Func[] parameterFactories) { - var contextInfo = GetAppropriateMappingContext( - swapArgs.Lambda.Parameters.Select(p => p.Type).ToArray(), - swapArgs); + var contextInfo = GetAppropriateMappingContext(swapArgs); return swapArgs.Lambda.ReplaceParametersWith(parameterFactories.Select(f => f.Invoke(contextInfo)).ToArray()); } - private static MappingContextInfo GetAppropriateMappingContext(Type[] contextTypes, SwapArgs swapArgs) + private static MappingContextInfo GetAppropriateMappingContext(SwapArgs swapArgs) { - if (swapArgs.MapperData.TypesMatch(contextTypes)) + if (swapArgs.ContextTypesMatch()) { - return new MappingContextInfo(swapArgs, contextTypes); + return new MappingContextInfo(swapArgs); } - var dataAccess = swapArgs.MapperData.GetAppropriateMappingContextAccess(contextTypes); + var dataAccess = swapArgs.GetAppropriateMappingContextAccess(); - return new MappingContextInfo(swapArgs, dataAccess, contextTypes); + return new MappingContextInfo(swapArgs, dataAccess); } #endregion @@ -212,21 +217,15 @@ public static Expression UseTargetInstance(IMemberMapperData mapperData, Express } private static bool ConvertTargetType(Type targetType, Expression targetInstanceAccess) - { - if (targetInstanceAccess.Type.IsAssignableTo(targetType)) - { - return targetInstanceAccess.Type.IsValueType(); - } - - return true; - } + => targetInstanceAccess.Type.IsValueType() || !targetInstanceAccess.Type.IsAssignableTo(targetType); public Expression Swap( LambdaExpression lambda, + Type[] contextTypes, IMemberMapperData mapperData, Func targetValueFactory) { - var swapArgs = new SwapArgs(lambda, mapperData, targetValueFactory); + var swapArgs = new SwapArgs(lambda, contextTypes, mapperData, targetValueFactory); return _parametersSwapper.Invoke(swapArgs); } @@ -235,33 +234,29 @@ public Expression Swap( public class MappingContextInfo { - public MappingContextInfo(SwapArgs swapArgs, Type[] contextTypes) - : this(swapArgs, swapArgs.MapperData.MappingDataObject, contextTypes) + private readonly SwapArgs _swapArgs; + + public MappingContextInfo(SwapArgs swapArgs) + : this(swapArgs, swapArgs.MapperData.MappingDataObject) { } - public MappingContextInfo(SwapArgs swapArgs, Expression contextAccess, Type[] contextTypes) + public MappingContextInfo(SwapArgs swapArgs, Expression contextAccess) { - var contextSourceType = contextTypes[0]; - var contextTargetType = contextTypes[1]; - var sourceAccess = swapArgs.MapperData.GetSourceAccess(contextAccess, contextSourceType); - var targetAccess = swapArgs.TargetValueFactory.Invoke(swapArgs.MapperData, contextAccess, contextTargetType); + _swapArgs = swapArgs; - ContextTypes = contextTypes; - CreatedObject = GetCreatedObject(swapArgs, contextTypes); - SourceAccess = GetValueAccess(sourceAccess, contextSourceType); - TargetAccess = GetValueAccess(targetAccess, contextTargetType); - Index = swapArgs.MapperData.EnumerableIndex; - Parent = swapArgs.MapperData.ParentObject; - MappingDataAccess = swapArgs.MapperData.GetTypedContextAccess(contextAccess, contextTypes); + CreatedObject = GetCreatedObject(swapArgs); + SourceAccess = GetValueAccess(swapArgs.GetSourceAccess(contextAccess), ContextTypes[0]); + TargetAccess = GetValueAccess(swapArgs.GetTargetAccess(contextAccess), ContextTypes[1]); + MappingDataAccess = swapArgs.GetTypedContextAccess(contextAccess); } - private static Expression GetCreatedObject(SwapArgs swapArgs, ICollection contextTypes) + private static Expression GetCreatedObject(SwapArgs swapArgs) { - var neededCreatedObjectType = contextTypes.Last(); + var neededCreatedObjectType = swapArgs.ContextTypes.Last(); var createdObject = swapArgs.MapperData.CreatedObject; - if ((contextTypes.Count == 3) && (neededCreatedObjectType == typeof(int?))) + if ((swapArgs.ContextTypes.Length == 3) && (neededCreatedObjectType == typeof(int?))) { return createdObject; } @@ -276,7 +271,7 @@ private static Expression GetValueAccess(Expression valueAccess, Type neededAcce : valueAccess; } - public Type[] ContextTypes { get; } + public Type[] ContextTypes => _swapArgs.ContextTypes; public Expression CreatedObject { get; } @@ -286,28 +281,46 @@ private static Expression GetValueAccess(Expression valueAccess, Type neededAcce public Expression TargetAccess { get; } - public Expression Index { get; } + public Expression Index => _swapArgs.MapperData.EnumerableIndex; - public Expression Parent { get; } + public Expression Parent => _swapArgs.MapperData.ParentObject; } public class SwapArgs { public SwapArgs( LambdaExpression lambda, + Type[] contextTypes, IMemberMapperData mapperData, Func targetValueFactory) { Lambda = lambda; + ContextTypes = contextTypes; MapperData = mapperData; TargetValueFactory = targetValueFactory; } public LambdaExpression Lambda { get; } + public Type[] ContextTypes { get; } + public IMemberMapperData MapperData { get; } public Func TargetValueFactory { get; } + + public bool ContextTypesMatch() => MapperData.TypesMatch(ContextTypes); + + public Expression GetAppropriateMappingContextAccess() + => MapperData.GetAppropriateMappingContextAccess(ContextTypes); + + public Expression GetTypedContextAccess(Expression contextAccess) + => MapperData.GetTypedContextAccess(contextAccess, ContextTypes); + + public Expression GetSourceAccess(Expression contextAccess) + => MapperData.GetSourceAccess(contextAccess, ContextTypes[0]); + + public Expression GetTargetAccess(Expression contextAccess) + => TargetValueFactory.Invoke(MapperData, contextAccess, ContextTypes[1]); } #endregion From e3203b25a3327b56df5f654457cfe771b1a769a6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 11:00:56 +0000 Subject: [PATCH 112/176] Replacing projection mapper specifier with ProjectUsing() method --- .../WhenCreatingProjections.cs | 4 +- .../WhenConfiguringDataSources.cs | 4 +- AgileMapper/ProjectionExtensions.cs | 40 +++++++++---------- .../Api/ProjectionMapperSelector.cs | 21 ---------- 4 files changed, 22 insertions(+), 47 deletions(-) delete mode 100644 AgileMapper/Queryables/Api/ProjectionMapperSelector.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs index a713e4bc9..067d5da28 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenCreatingProjections.cs @@ -24,7 +24,7 @@ public Task ShouldReuseACachedProjectionMapper() { var stringDtos = await context1 .StringItems - .Project(_ => _.Using(mapper)).To() + .ProjectUsing(mapper).To() .ToListAsync(); stringDtos.ShouldBeEmpty(); @@ -39,7 +39,7 @@ public Task ShouldReuseACachedProjectionMapper() var moreStringDtos = await context2 .StringItems - .Project(_ => _.Using(mapper)).To() + .ProjectUsing(mapper).To() .ToArrayAsync(); moreStringDtos.ShouldHaveSingleItem(); diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index f2477cef0..6fb47ee62 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -32,7 +32,7 @@ protected Task DoShouldApplyAConfiguredConstant() var productDto = context .Products - .Project(_ => _.Using(mapper)).To() + .ProjectUsing(mapper).To() .ShouldHaveSingleItem(); productDto.ProductId.ShouldBe(product.ProductId); @@ -63,7 +63,7 @@ protected Task DoShouldConditionallyApplyAConfiguredConstant() var productDtos = context .Products - .Project(_ => _.Using(mapper)).To() + .ProjectUsing(mapper).To() .ToArray(); productDtos.Length.ShouldBe(2); diff --git a/AgileMapper/ProjectionExtensions.cs b/AgileMapper/ProjectionExtensions.cs index a07462d08..4e9352473 100644 --- a/AgileMapper/ProjectionExtensions.cs +++ b/AgileMapper/ProjectionExtensions.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper { - using System; using System.Linq; using Api; - using Queryables.Api; /// /// Provides extension methods to support projecting an IQueryable to an IQueryable of a different type. @@ -12,33 +10,31 @@ public static class ProjectionExtensions { /// /// Project the elements of the given to instances of a specified - /// result Type, using a mapper provided by a given , or the default - /// mapper if none is supplied. The projection operation is performed entirely on the data source. + /// result Type, using the default mapper. The projection operation is performed entirely on the data source. /// /// The Type of the elements to project to a new result Type. /// The source IQueryable{T} on which to perform the projection. - /// - /// A func providing the mapper with which the projection should be performed. If not supplied, the default - /// mapper will be used. - /// /// An IProjectionResultSpecifier with which to specify the type of query projection to perform. public static IProjectionResultSpecifier Project( - this IQueryable sourceQueryable, - Func mapperSelector = null) + this IQueryable sourceQueryable) { - MapperContext mapperContext; - - if (mapperSelector != null) - { - var mapper = mapperSelector.Invoke(ProjectionMapperSelector.Instance); - mapperContext = ((IMapperInternal)mapper).Context; - } - else - { - mapperContext = Mapper.Default.Context; - } + return new ProjectionExecutor(Mapper.Default.Context, sourceQueryable); + } - return new ProjectionExecutor(mapperContext, sourceQueryable); + /// + /// Project the elements of the given to instances of a specified + /// result Type, using the given . The projection operation is performed + /// entirely on the data source. + /// + /// The Type of the elements to project to a new result Type. + /// The source IQueryable{T} on which to perform the projection. + /// The mapper with which the projection should be performed. + /// An IProjectionResultSpecifier with which to specify the type of query projection to perform. + public static IProjectionResultSpecifier ProjectUsing( + this IQueryable sourceQueryable, + IMapper mapper) + { + return new ProjectionExecutor(((IMapperInternal)mapper).Context, sourceQueryable); } } } diff --git a/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs b/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs deleted file mode 100644 index f0e2bdfeb..000000000 --- a/AgileMapper/Queryables/Api/ProjectionMapperSelector.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AgileObjects.AgileMapper.Queryables.Api -{ - /// - /// Provides the option to supply a particular with which to perform a query projection. - /// - public class ProjectionMapperSelector - { - internal static readonly ProjectionMapperSelector Instance = new ProjectionMapperSelector(); - - private ProjectionMapperSelector() - { - } - - /// - /// Use the given in this query projection. - /// - /// The to use in this query projection. - /// The to use in this query projection. - public IMapper Using(IMapper mapper) => mapper; - } -} \ No newline at end of file From 2284e7f101558383615af14b02e31766f41c7878 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 11:34:09 +0000 Subject: [PATCH 113/176] Test coverage for mapping a configured constant to a nested projection result member --- .../WhenConfiguringDataSources.cs | 4 +++ .../WhenConfiguringDataSources.cs | 35 +++++++++++++++++++ .../IQueryProjectionModifier.cs | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) rename AgileMapper/Queryables/{ => Converters}/IQueryProjectionModifier.cs (82%) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index ee476cef0..f4dbad18c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -19,5 +19,9 @@ public Task ShouldApplyAConfiguredConstant() [Fact] public Task ShouldConditionallyApplyAConfiguredConstant() => DoShouldConditionallyApplyAConfiguredConstant(); + + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMember() + => DoShouldApplyAConfiguredConstantToANestedMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 6fb47ee62..5fd916080 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -76,5 +76,40 @@ protected Task DoShouldConditionallyApplyAConfiguredConstant() } }); } + + protected Task DoShouldApplyAConfiguredConstantToANestedMember() + { + return RunTest(async context => + { + var person = new Person + { + Name = "Person 1", + Address = new Address { Line1 = "Line 1", Postcode = "Postcode" } + }; + + context.Persons.Add(person); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("LINE ONE!?") + .To(dto => dto.Address.Line1); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Person 1"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("LINE ONE!?"); + personDto.Address.Postcode.ShouldBe("Postcode"); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/IQueryProjectionModifier.cs b/AgileMapper/Queryables/Converters/IQueryProjectionModifier.cs similarity index 82% rename from AgileMapper/Queryables/IQueryProjectionModifier.cs rename to AgileMapper/Queryables/Converters/IQueryProjectionModifier.cs index 6db19e168..af95a4561 100644 --- a/AgileMapper/Queryables/IQueryProjectionModifier.cs +++ b/AgileMapper/Queryables/Converters/IQueryProjectionModifier.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables +namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; using Members; From c587ed2ac15400be24b6c9d8c78e74494cc5ae15 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 11:41:49 +0000 Subject: [PATCH 114/176] Support for configuring custom source members for query projections --- .../WhenConfiguringDataSources.cs | 6 ++-- .../WhenConfiguringDataSources.cs | 28 +++++++++++++++++++ .../Api/Configuration/MappingConfigurator.cs | 26 +++++++++++------ .../Projection/IRootProjectionConfigurator.cs | 18 +++++++++++- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index f4dbad18c..0874314d0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -13,8 +13,7 @@ public WhenConfiguringDataSources(InMemoryEfCore2TestContext context) } [Fact] - public Task ShouldApplyAConfiguredConstant() - => DoShouldApplyAConfiguredConstant(); + public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); [Fact] public Task ShouldConditionallyApplyAConfiguredConstant() @@ -23,5 +22,8 @@ public Task ShouldConditionallyApplyAConfiguredConstant() [Fact] public Task ShouldApplyAConfiguredConstantToANestedMember() => DoShouldApplyAConfiguredConstantToANestedMember(); + + [Fact] + public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 5fd916080..05da03ae2 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -111,5 +111,33 @@ protected Task DoShouldApplyAConfiguredConstantToANestedMember() } }); } + + protected Task DoShouldApplyAConfiguredMember() + { + return RunTest(async context => + { + var product = new Product { Name = "P1" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.ProductId) + .To(dto => dto.Name); + + var personDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(product.ProductId); + personDto.Name.ShouldBe(product.ProductId); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 6cab8e0d7..3449f2f02 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -216,25 +216,25 @@ public PostEventMappingConfigStartingPoint After public CustomDataSourceTargetMemberSpecifier Map( Expression, TSourceValue>> valueFactoryExpression) { - return new CustomDataSourceTargetMemberSpecifier( - ConfigInfo.ForSourceValueType(), - valueFactoryExpression); + return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); + } + + public CustomDataSourceTargetMemberSpecifier Map( + Expression> valueFactoryExpression) + { + return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } public CustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { - return new CustomDataSourceTargetMemberSpecifier( - ConfigInfo.ForSourceValueType(), - valueFactoryExpression); + return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } public CustomDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { - return new CustomDataSourceTargetMemberSpecifier( - ConfigInfo.ForSourceValueType(), - valueFactoryExpression); + return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } public CustomDataSourceTargetMemberSpecifier MapFunc( @@ -254,6 +254,14 @@ public CustomDataSourceTargetMemberSpecifier Map #region Map Helpers + private CustomDataSourceTargetMemberSpecifier GetValueFactoryTargetMemberSpecifier( + LambdaExpression valueFactoryExpression) + { + return new CustomDataSourceTargetMemberSpecifier( + ConfigInfo.ForSourceValueType(), + valueFactoryExpression); + } + private CustomDataSourceTargetMemberSpecifier GetConstantValueTargetMemberSpecifier( TSourceValue value) { diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index 0727cabd8..dc6ef9e4f 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -1,5 +1,8 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Projection { + using System; + using System.Linq.Expressions; + /// /// Provides options for configuring projections from and to a given source and result type. /// @@ -8,7 +11,20 @@ public interface IRootProjectionConfigurator { /// - /// Configure a constant value for a particular target member when projecting from and to the source and + /// Configure a custom data source for a particular result member when mapping from and to the source and + /// result types being configured. The factory expression is passed the source element being projected. + /// + /// The type of the custom value being configured. + /// The expression to map to the configured result member. + /// + /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom + /// value should be applied. + /// + CustomDataSourceTargetMemberSpecifier Map( + Expression> valueFactoryExpression); + + /// + /// Configure a constant value for a particular result member when projecting from and to the source and /// result types being configured. /// /// The type of the custom constant value being configured. From 47c339c6cef98834f521fe6c1f97309f2a8ee480 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 12:06:28 +0000 Subject: [PATCH 115/176] Support for projection-specific config continuations in projection configuration --- .../WhenConfiguringDataSources.cs | 4 + .../WhenConfiguringDataSources.cs | 33 +++++++++ .../Api/Configuration/CallbackSpecifier.cs | 6 +- .../CustomDataSourceTargetMemberSpecifier.cs | 74 +++++++------------ .../DerivedPairTargetTypeSpecifier.cs | 4 +- .../Api/Configuration/EnumPairSpecifier.cs | 8 +- .../Api/Configuration/ICallbackSpecifier.cs | 12 +-- .../IConditionalRootMappingConfigurator.cs | 8 +- ...mMappingDataSourceTargetMemberSpecifier.cs | 57 ++++++++++++++ .../IMappingConfigContinuation.cs | 22 ++++++ .../IPostInstanceCreationCallbackSpecifier.cs | 20 ++--- .../Configuration/IRootMappingConfigurator.cs | 50 ++++++------- .../InstanceCreationCallbackSpecifier.cs | 8 +- .../MappingConfigContinuation.cs | 33 ++++----- .../Api/Configuration/MappingConfigurator.cs | 47 +++++++----- ...ojectionDataSourceTargetMemberSpecifier.cs | 25 +++++++ .../IProjectionConfigContinuation.cs | 16 ++++ .../Projection/IRootProjectionConfigurator.cs | 4 +- 18 files changed, 284 insertions(+), 147 deletions(-) create mode 100644 AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs create mode 100644 AgileMapper/Api/Configuration/IMappingConfigContinuation.cs create mode 100644 AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index 0874314d0..bea8b46ca 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -25,5 +25,9 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() [Fact] public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyMultipleConfiguredMembers() + => DoShouldApplyMultipleConfiguredMembers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 05da03ae2..688d1286f 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -139,5 +139,38 @@ protected Task DoShouldApplyAConfiguredMember() } }); } + + protected Task DoShouldApplyMultipleConfiguredMembers() + { + return RunTest(async context => + { + var product = new Product { Name = "Product1" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.ProductId) + .To(dto => dto.Name) + .And + .Map(p => p.Name) + .To(p => p.Address.Line1); + + var personDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(product.ProductId); + personDto.Name.ShouldBe(product.ProductId); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Product1"); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/CallbackSpecifier.cs b/AgileMapper/Api/Configuration/CallbackSpecifier.cs index ad2fea6e9..fb5e23326 100644 --- a/AgileMapper/Api/Configuration/CallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/CallbackSpecifier.cs @@ -49,13 +49,13 @@ private CallbackSpecifier SetCondition(LambdaExpression condit return this; } - public MappingConfigContinuation Call(Action> callback) + public IMappingConfigContinuation Call(Action> callback) => CreateCallbackFactory(callback); - public MappingConfigContinuation Call(Action callback) + public IMappingConfigContinuation Call(Action callback) => CreateCallbackFactory(callback); - public MappingConfigContinuation Call(Action callback) + public IMappingConfigContinuation Call(Action callback) => CreateCallbackFactory(callback); private MappingConfigContinuation CreateCallbackFactory(TAction callback) diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index 1017f20b6..807be5e60 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -12,20 +12,19 @@ using Members; using Members.Dictionaries; using NetStandardPolyfills; + using Projection; using ReadableExpressions.Extensions; - /// - /// Provides options for specifying a target member to which a configuration option should apply. - /// - /// The source type to which the configuration should apply. - /// The target type to which the configuration should apply. - public class CustomDataSourceTargetMemberSpecifier + + internal class CustomDataSourceTargetMemberSpecifier : + ICustomMappingDataSourceTargetMemberSpecifier, + ICustomProjectionDataSourceTargetMemberSpecifier { private readonly MappingConfigInfo _configInfo; private readonly LambdaExpression _customValueLambda; private readonly ConfiguredLambdaInfo _customValueLambdaInfo; - internal CustomDataSourceTargetMemberSpecifier( + public CustomDataSourceTargetMemberSpecifier( MappingConfigInfo configInfo, LambdaExpression customValueLambda) : this(configInfo, default(ConfiguredLambdaInfo)) @@ -33,7 +32,7 @@ internal CustomDataSourceTargetMemberSpecifier( _customValueLambda = customValueLambda; } - internal CustomDataSourceTargetMemberSpecifier( + public CustomDataSourceTargetMemberSpecifier( MappingConfigInfo configInfo, ConfiguredLambdaInfo customValueLambda) { @@ -41,29 +40,20 @@ internal CustomDataSourceTargetMemberSpecifier( _customValueLambdaInfo = customValueLambda; } - /// - /// Apply the configuration to the given . - /// - /// The target member's type. - /// The target member to which to apply the configuration. - /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. - /// - public MappingConfigContinuation To( + + public IMappingConfigContinuation To( Expression> targetMember) - => RegisterDataSource(() => CreateFromLambda(targetMember)); - - /// - /// Apply the configuration to the given . - /// - /// The type of the target set method's argument. - /// The target set method to which to apply the configuration. - /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. - /// - public MappingConfigContinuation To( + { + return RegisterDataSource(() => CreateFromLambda(targetMember)); + } + + IProjectionConfigContinuation ICustomProjectionDataSourceTargetMemberSpecifier.To( + Expression> resultMember) + { + return RegisterDataSource(() => CreateFromLambda(resultMember)); + } + + public IMappingConfigContinuation To( Expression>> targetSetMethod) => RegisterDataSource(() => CreateFromLambda(targetSetMethod)); @@ -149,28 +139,14 @@ private ConfiguredLambdaInfo GetValueLambda() return valueLambdaInfo; } - /// - /// Apply the configuration to the constructor parameter with the type specified by the type argument. - /// - /// The target constructor parameter's type. - /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. - /// - public MappingConfigContinuation ToCtor() + public IMappingConfigContinuation ToCtor() => RegisterDataSource(CreateForCtorParam); - /// - /// Apply the configuration to the constructor parameter with the specified . - /// - /// The target constructor parameter's name. - /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. - /// - public MappingConfigContinuation ToCtor(string parameterName) + public IMappingConfigContinuation ToCtor(string parameterName) => RegisterDataSource(() => CreateForCtorParam(parameterName)); + #region Ctor Helpers + private ConfiguredDataSourceFactory CreateForCtorParam() => CreateForCtorParam(GetUniqueConstructorParameterOrThrow()); @@ -249,6 +225,8 @@ private ConfiguredDataSourceFactory CreateForCtorParam(ParameterInfo par return new ConfiguredDataSourceFactory(_configInfo, valueLambda, constructorParameter); } + #endregion + private MappingConfigContinuation RegisterDataSource( Func factoryFactory) { diff --git a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs index 9c67ca4e2..2137912b9 100644 --- a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs +++ b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs @@ -30,10 +30,10 @@ internal DerivedPairTargetTypeSpecifier(MappingConfigInfo configInfo) /// The derived target type to create for the configured derived source type. /// /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - public MappingConfigContinuation To() + public IMappingConfigContinuation To() where TDerivedTarget : TTarget { var derivedTypePair = DerivedTypePair diff --git a/AgileMapper/Api/Configuration/EnumPairSpecifier.cs b/AgileMapper/Api/Configuration/EnumPairSpecifier.cs index 9e8af13bc..e26a0a8f2 100644 --- a/AgileMapper/Api/Configuration/EnumPairSpecifier.cs +++ b/AgileMapper/Api/Configuration/EnumPairSpecifier.cs @@ -65,8 +65,8 @@ private static void ThrowIfEmpty(ICollection firstEnumMembers) /// /// The type of the second enum being paired. /// The second enum member in the pair. - /// A MappingConfigContinuation with which to configure other aspects of mapping. - public MappingConfigContinuation With(TSecondEnum secondEnumMember) + /// An IMappingConfigContinuation with which to configure other aspects of mapping. + public IMappingConfigContinuation With(TSecondEnum secondEnumMember) where TSecondEnum : struct => With(new[] { secondEnumMember }); @@ -76,8 +76,8 @@ public MappingConfigContinuation With(TSecondEnum /// /// The type of the second enum being paired. /// The second set of enum members in the pairs. - /// A MappingConfigContinuation with which to configure other aspects of mapping. - public MappingConfigContinuation With(params TSecondEnum[] secondEnumMembers) + /// An IMappingConfigContinuation with which to configure other aspects of mapping. + public IMappingConfigContinuation With(params TSecondEnum[] secondEnumMembers) where TSecondEnum : struct { ThrowIfNotEnumType(); diff --git a/AgileMapper/Api/Configuration/ICallbackSpecifier.cs b/AgileMapper/Api/Configuration/ICallbackSpecifier.cs index 780525545..e95eb90ca 100644 --- a/AgileMapper/Api/Configuration/ICallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/ICallbackSpecifier.cs @@ -17,10 +17,10 @@ public interface ICallbackSpecifier /// /// The callback to execute. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action> callback); + IMappingConfigContinuation Call(Action> callback); /// /// Specify a callback to be executed. The condition expression is passed the current mapping's source @@ -28,10 +28,10 @@ public interface ICallbackSpecifier /// /// The callback to execute. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action callback); + IMappingConfigContinuation Call(Action callback); /// /// Specify a callback to be executed. The condition expression is passed the current mapping's source @@ -39,9 +39,9 @@ public interface ICallbackSpecifier /// /// The callback to execute. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action callback); + IMappingConfigContinuation Call(Action callback); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs index 05a74b4f4..df4828858 100644 --- a/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs @@ -14,19 +14,19 @@ public interface IConditionalRootMappingConfigurator /// /// The derived target type to create. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation MapTo() + IMappingConfigContinuation MapTo() where TDerivedTarget : TTarget; /// /// Map the target type being configured to null if the preceding condition evaluates to true. /// /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation MapToNull(); + IMappingConfigContinuation MapToNull(); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs new file mode 100644 index 000000000..1dd84ef36 --- /dev/null +++ b/AgileMapper/Api/Configuration/ICustomMappingDataSourceTargetMemberSpecifier.cs @@ -0,0 +1,57 @@ +namespace AgileObjects.AgileMapper.Api.Configuration +{ + using System; + using System.Linq.Expressions; + + /// + /// Provides options for specifying a target member to which a configuration option should apply. + /// + /// The source type to which the configuration should apply. + /// The target type to which the configuration should apply. + public interface ICustomMappingDataSourceTargetMemberSpecifier + { + /// + /// Apply the configuration to the given . + /// + /// The target member's type. + /// The target member to which to apply the configuration. + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source + /// and target type being configured. + /// + IMappingConfigContinuation To( + Expression> targetMember); + + /// + /// Apply the configuration to the given . + /// + /// The type of the target set method's argument. + /// The target set method to which to apply the configuration. + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source + /// and target type being configured. + /// + IMappingConfigContinuation To( + Expression>> targetSetMethod); + + /// + /// Apply the configuration to the constructor parameter with the type specified by the type argument. + /// + /// The target constructor parameter's type. + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source + /// and target type being configured. + /// + IMappingConfigContinuation ToCtor(); + + /// + /// Apply the configuration to the constructor parameter with the specified . + /// + /// The target constructor parameter's name. + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and + /// target type being configured. + /// + IMappingConfigContinuation ToCtor(string parameterName); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IMappingConfigContinuation.cs b/AgileMapper/Api/Configuration/IMappingConfigContinuation.cs new file mode 100644 index 000000000..c436f050d --- /dev/null +++ b/AgileMapper/Api/Configuration/IMappingConfigContinuation.cs @@ -0,0 +1,22 @@ +namespace AgileObjects.AgileMapper.Api.Configuration +{ + /// + /// Enables chaining of configurations for the same source and target type. + /// + /// The source type to which the configuration should apply. + /// The target type to which the configuration should apply. + public interface IMappingConfigContinuation + { + /// + /// Perform another configuration of how this mapper maps to and from the source and target types + /// being configured. This property exists purely to provide a more fluent configuration interface. + /// + IFullMappingConfigurator And { get; } + + /// + /// Perform an alternative configuration of how this mapper maps to and from the source and target types + /// being configured. This property exists purely to provide a more fluent configuration interface. + /// + IFullMappingConfigurator But { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IPostInstanceCreationCallbackSpecifier.cs b/AgileMapper/Api/Configuration/IPostInstanceCreationCallbackSpecifier.cs index 73f796e09..fde81ccd1 100644 --- a/AgileMapper/Api/Configuration/IPostInstanceCreationCallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/IPostInstanceCreationCallbackSpecifier.cs @@ -18,10 +18,10 @@ public interface IPostInstanceCreationCallbackSpecifier /// The callback to call. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call( + IMappingConfigContinuation Call( Action> callback); /// @@ -30,10 +30,10 @@ MappingConfigContinuation Call( /// /// The callback to call. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. - /// - MappingConfigContinuation Call(Action callback); + /// + IMappingConfigContinuation Call(Action callback); /// /// Configure a callback to call in the configured conditions. The callback is passed the current @@ -41,10 +41,10 @@ MappingConfigContinuation Call( /// /// The callback to call. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. - /// - MappingConfigContinuation Call(Action callback); + /// + IMappingConfigContinuation Call(Action callback); /// /// Configure a callback to call in the configured conditions. The callback is passed the current @@ -52,9 +52,9 @@ MappingConfigContinuation Call( /// /// The callback to call. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action callback); + IMappingConfigContinuation Call(Action callback); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index 3aa0fb7b6..fe0c14ed0 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -21,10 +21,10 @@ public interface IRootMappingConfigurator /// The factory expression to use to create instances of the type being configured. /// /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation CreateInstancesUsing( + IMappingConfigContinuation CreateInstancesUsing( Expression, TTarget>> factory); /// @@ -49,10 +49,10 @@ MappingConfigContinuation CreateInstancesUsing( /// The factory function to use to create instances of the type being configured. /// /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class; + IMappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class; /// /// Configure a factory to use to create instances of the type specified by the type argument. @@ -69,10 +69,10 @@ MappingConfigContinuation CreateInstancesUsing( /// /// The target member(s) which should be ignored. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Ignore(params Expression>[] targetMembers); + IMappingConfigContinuation Ignore(params Expression>[] targetMembers); /// /// Ignore all target member(s) of the given Type when mapping @@ -80,10 +80,10 @@ MappingConfigContinuation CreateInstancesUsing( /// /// The Type of target member to ignore. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation IgnoreTargetMembersOfType(); + IMappingConfigContinuation IgnoreTargetMembersOfType(); /// /// Ignore all target member(s) matching the given when mapping @@ -91,10 +91,10 @@ MappingConfigContinuation CreateInstancesUsing( /// /// The matching function with which to select target members to ignore. /// - /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation IgnoreTargetMembersWhere(Expression> memberFilter); + IMappingConfigContinuation IgnoreTargetMembersWhere(Expression> memberFilter); /// /// Configure a custom data source for a particular target member when mapping from and to the source and @@ -104,10 +104,10 @@ MappingConfigContinuation CreateInstancesUsing( /// The type of the custom value being configured. /// The expression to map to the configured target member. /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the target member to which the custom - /// value should be applied. + /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// custom value should be applied. /// - CustomDataSourceTargetMemberSpecifier Map( + ICustomMappingDataSourceTargetMemberSpecifier Map( Expression, TSourceValue>> valueFactoryExpression); /// @@ -118,10 +118,10 @@ CustomDataSourceTargetMemberSpecifier Map( /// The type of the custom value being configured. /// The expression to map to the configured target member. /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the target member to which the custom - /// value should be applied. + /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// custom value should be applied. /// - CustomDataSourceTargetMemberSpecifier Map( + ICustomMappingDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression); /// @@ -132,10 +132,10 @@ CustomDataSourceTargetMemberSpecifier Map( /// The type of the custom value being configured. /// The expression to map to the configured target member. /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the target member to which the custom - /// value should be applied. + /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// custom value should be applied. /// - CustomDataSourceTargetMemberSpecifier Map( + ICustomMappingDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression); /// @@ -144,10 +144,10 @@ CustomDataSourceTargetMemberSpecifier Map( /// The type of value returned by the given Func. /// The Func object to map to the configured target member. /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the target member to which the custom - /// value should be applied. + /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// custom value should be applied. /// - CustomDataSourceTargetMemberSpecifier MapFunc( + ICustomMappingDataSourceTargetMemberSpecifier MapFunc( Func valueFunc); /// @@ -157,9 +157,9 @@ CustomDataSourceTargetMemberSpecifier MapFunc( /// The type of the custom constant value being configured. /// The constant value to map to the configured target member. /// - /// A CustomDataSourceTargetMemberSpecifier with which to specify the target member to which the custom - /// constant value should be applied. + /// An ICustomMappingDataSourceTargetMemberSpecifier with which to specify the target member to which the + /// custom constant value should be applied. /// - CustomDataSourceTargetMemberSpecifier Map(TSourceValue value); + ICustomMappingDataSourceTargetMemberSpecifier Map(TSourceValue value); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs b/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs index a52af95a1..a9b2b6d3f 100644 --- a/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs @@ -81,19 +81,19 @@ void IPreInstanceCreationCallbackSpecifier.Call( Expression> condition) => SetCondition(condition); - MappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( Action> callback) => CreateCallbackFactory(callback); - MappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( Action callback) => CreateCallbackFactory(callback); - MappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( Action callback) => CreateCallbackFactory(callback); - MappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPostInstanceCreationCallbackSpecifier.Call( Action callback) => CreateCallbackFactory(callback); diff --git a/AgileMapper/Api/Configuration/MappingConfigContinuation.cs b/AgileMapper/Api/Configuration/MappingConfigContinuation.cs index 78db49766..651792490 100644 --- a/AgileMapper/Api/Configuration/MappingConfigContinuation.cs +++ b/AgileMapper/Api/Configuration/MappingConfigContinuation.cs @@ -1,33 +1,28 @@ namespace AgileObjects.AgileMapper.Api.Configuration { using AgileMapper.Configuration; + using Projection; - /// - /// Enables chaining of configurations for the same source and target type. - /// - /// The source type to which the configuration should apply. - /// The target type to which the configuration should apply. - public class MappingConfigContinuation + + internal class MappingConfigContinuation : + IMappingConfigContinuation, + IProjectionConfigContinuation { private readonly MappingConfigInfo _configInfo; - internal MappingConfigContinuation(MappingConfigInfo configInfo) + public MappingConfigContinuation(MappingConfigInfo configInfo) { _configInfo = configInfo; - } + } + + public IFullMappingConfigurator And => CreateNewConfigurator(); - /// - /// Perform another configuration of how this mapper maps to and from the source and target types - /// being configured. This property exists purely to provide a more fluent configuration interface. - /// - public IFullMappingConfigurator And - => new MappingConfigurator(_configInfo.Clone()); + IFullProjectionConfigurator IProjectionConfigContinuation.And + => CreateNewConfigurator(); + + public IFullMappingConfigurator But => CreateNewConfigurator(); - /// - /// Perform an alternative configuration of how this mapper maps to and from the source and target types - /// being configured. This property exists purely to provide a more fluent configuration interface. - /// - public IFullMappingConfigurator But + private MappingConfigurator CreateNewConfigurator() => new MappingConfigurator(_configInfo.Clone()); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 3449f2f02..baab26510 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -118,7 +118,7 @@ private MappingConfigurator SetCondition(LambdaExpression cond #endregion - public MappingConfigContinuation CreateInstancesUsing( + public IMappingConfigContinuation CreateInstancesUsing( Expression, TTarget>> factory) { new FactorySpecifier(ConfigInfo).Using(factory); @@ -126,7 +126,7 @@ public MappingConfigContinuation CreateInstancesUsing( return new MappingConfigContinuation(ConfigInfo); } - public MappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class + public IMappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class { new FactorySpecifier(ConfigInfo).Using(factory); @@ -174,12 +174,12 @@ public EnumPairSpecifier PairEnum(TFir #region Ignoring Members - public MappingConfigContinuation IgnoreTargetMembersOfType() + public IMappingConfigContinuation IgnoreTargetMembersOfType() { return IgnoreTargetMembersWhere(member => member.HasType()); } - public MappingConfigContinuation IgnoreTargetMembersWhere( + public IMappingConfigContinuation IgnoreTargetMembersWhere( Expression> memberFilter) { var configuredIgnoredMember = new ConfiguredIgnoredMember(ConfigInfo, memberFilter); @@ -189,7 +189,7 @@ public MappingConfigContinuation IgnoreTargetMembersWhere( return new MappingConfigContinuation(ConfigInfo); } - public MappingConfigContinuation Ignore(params Expression>[] targetMembers) + public IMappingConfigContinuation Ignore(params Expression>[] targetMembers) { foreach (var targetMember in targetMembers) { @@ -213,43 +213,41 @@ public PostEventMappingConfigStartingPoint After #region Map Overloads - public CustomDataSourceTargetMemberSpecifier Map( + public ICustomMappingDataSourceTargetMemberSpecifier Map( Expression, TSourceValue>> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public CustomDataSourceTargetMemberSpecifier Map( + public ICustomProjectionDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public CustomDataSourceTargetMemberSpecifier Map( + public ICustomMappingDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public CustomDataSourceTargetMemberSpecifier Map( + public ICustomMappingDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression) { return GetValueFactoryTargetMemberSpecifier(valueFactoryExpression); } - public CustomDataSourceTargetMemberSpecifier MapFunc( + public ICustomMappingDataSourceTargetMemberSpecifier MapFunc( Func valueFunc) => GetConstantValueTargetMemberSpecifier(valueFunc); - public CustomDataSourceTargetMemberSpecifier Map(TSourceValue value) - { - var valueLambdaInfo = ConfiguredLambdaInfo.ForFunc(value, typeof(TSource), typeof(TTarget)); + public ICustomMappingDataSourceTargetMemberSpecifier Map(TSourceValue value) + => GetConstantValueTargetMemberSpecifier(value); - return (valueLambdaInfo != null) - ? new CustomDataSourceTargetMemberSpecifier( - ConfigInfo.ForSourceValueType(valueLambdaInfo.ReturnType), - valueLambdaInfo) - : GetConstantValueTargetMemberSpecifier(value); + ICustomProjectionDataSourceTargetMemberSpecifier IRootProjectionConfigurator.Map( + TSourceValue value) + { + return GetConstantValueTargetMemberSpecifier(value); } #region Map Helpers @@ -265,6 +263,15 @@ private CustomDataSourceTargetMemberSpecifier GetValueFactoryT private CustomDataSourceTargetMemberSpecifier GetConstantValueTargetMemberSpecifier( TSourceValue value) { + var valueLambdaInfo = ConfiguredLambdaInfo.ForFunc(value, typeof(TSource), typeof(TTarget)); + + if (valueLambdaInfo != null) + { + return new CustomDataSourceTargetMemberSpecifier( + ConfigInfo.ForSourceValueType(valueLambdaInfo.ReturnType), + valueLambdaInfo); + } + var valueConstant = value.ToConstantExpression(); var valueLambda = Expression.Lambda>(valueConstant); @@ -275,7 +282,7 @@ private CustomDataSourceTargetMemberSpecifier GetConstantValue #endregion - public MappingConfigContinuation MapTo() + public IMappingConfigContinuation MapTo() where TDerivedTarget : TTarget { var derivedTypePair = new DerivedPairTargetTypeSpecifier(ConfigInfo); @@ -283,7 +290,7 @@ public MappingConfigContinuation MapTo() return derivedTypePair.To(); } - public MappingConfigContinuation MapToNull() + public IMappingConfigContinuation MapToNull() { var condition = new MapToNullCondition(ConfigInfo); diff --git a/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs new file mode 100644 index 000000000..e444dca87 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + using System; + using System.Linq.Expressions; + + /// + /// Provides options for specifying a result member to which a configuration option should apply. + /// + /// The source type to which the configuration should apply. + /// The result type to which the configuration should apply. + public interface ICustomProjectionDataSourceTargetMemberSpecifier + { + /// + /// Apply the configuration to the given . + /// + /// The target member's type. + /// The result member to which to apply the configuration. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the + /// source and target type being configured. + /// + IProjectionConfigContinuation To( + Expression> resultMember); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs b/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs new file mode 100644 index 000000000..3d3740b72 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Enables chaining of configurations for the same source and result type. + /// + /// The source type to which the configuration should apply. + /// The result type to which the configuration should apply. + public interface IProjectionConfigContinuation + { + /// + /// Perform another configuration of how this mapper projects to and from the source and result types + /// being configured. This property exists purely to provide a more fluent configuration interface. + /// + IFullProjectionConfigurator And { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index dc6ef9e4f..1b867854a 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -20,7 +20,7 @@ public interface IRootProjectionConfigurator /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom /// value should be applied. /// - CustomDataSourceTargetMemberSpecifier Map( + ICustomProjectionDataSourceTargetMemberSpecifier Map( Expression> valueFactoryExpression); /// @@ -33,6 +33,6 @@ CustomDataSourceTargetMemberSpecifier Map - CustomDataSourceTargetMemberSpecifier Map(TSourceValue value); + ICustomProjectionDataSourceTargetMemberSpecifier Map(TSourceValue value); } } \ No newline at end of file From c56ad8d79a49aa860a8c883aba9e7af087249f71 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 12:09:07 +0000 Subject: [PATCH 116/176] Test coverage for conditional custom source members in projections --- .../WhenConfiguringDataSources.cs | 4 +++ .../WhenConfiguringDataSources.cs | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index bea8b46ca..951bd3f70 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -29,5 +29,9 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() [Fact] public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); + + [Fact] + public void ShouldConditionallyApplyAConfiguredMember() + => DoShouldConditionallyApplyAConfiguredMember(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 688d1286f..fcc509bb4 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -172,5 +172,41 @@ protected Task DoShouldApplyMultipleConfiguredMembers() } }); } + + protected Task DoShouldConditionallyApplyAConfiguredMember() + { + return RunTest(async context => + { + var product1 = new Product { Name = "P.1" }; + var product2 = new Product { Name = "P.2" }; + + context.Products.Add(product1); + context.Products.Add(product2); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.ProductId > 1) + .Map(p => p.Name + "?!") + .To(dto => dto.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("P.2?!"); + } + }); + } } } \ No newline at end of file From 61a28d152fdbd74345bd84ccb6f3500d9976a64e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 13:53:38 +0000 Subject: [PATCH 117/176] Test coverage for alternative custom data sources when projecting --- .../WhenConfiguringDataSources.cs | 10 +++-- .../WhenConfiguringDataSources.cs | 39 +++++++++++++++++++ .../IProjectionConfigContinuation.cs | 6 +++ .../ComplexTypeToNullComparisonConverter.cs | 3 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index 951bd3f70..bf0eab2dd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -27,11 +27,13 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); [Fact] - public Task ShouldApplyMultipleConfiguredMembers() - => DoShouldApplyMultipleConfiguredMembers(); + public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); [Fact] - public void ShouldConditionallyApplyAConfiguredMember() - => DoShouldConditionallyApplyAConfiguredMember(); + public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index fcc509bb4..485e60cee 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -208,5 +208,44 @@ protected Task DoShouldConditionallyApplyAConfiguredMember() } }); } + + protected Task DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + { + return RunTest(async context => + { + var product1 = new Product { Name = "P.1" }; + var product2 = new Product { Name = "P.2" }; + + context.Products.Add(product1); + context.Products.Add(product2); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.Name + '!') + .To(dto => dto.Name) + .But + .If(p => p.Name.Contains("1")) + .Map(p => p.Name + '?') + .To(dto => dto.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1?"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("P.2!"); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs b/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs index 3d3740b72..2802de537 100644 --- a/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs +++ b/AgileMapper/Api/Configuration/Projection/IProjectionConfigContinuation.cs @@ -12,5 +12,11 @@ public interface IProjectionConfigContinuation /// being configured. This property exists purely to provide a more fluent configuration interface. /// IFullProjectionConfigurator And { get; } + + /// + /// Perform an alternative configuration of how this mapper projects to and from the source and result + /// types being configured. This property exists purely to provide a more fluent configuration interface. + /// + IFullProjectionConfigurator But { get; } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs index 9329b5c0e..e5ebaa841 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -15,7 +15,8 @@ public static bool TryConvert( out Expression converted) { if (context.Settings.SupportsComplexTypeToNullComparison || - !comparison.Right.Type.IsComplex()) + (comparison.Left.Type == typeof(object)) || + !comparison.Left.Type.IsComplex()) { converted = null; return false; From 95231c13d4c8310fee49da4e3afca5ebad3c8d11 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 5 Feb 2018 15:18:01 +0000 Subject: [PATCH 118/176] Removing conversion of DefaultExpressions to constants where ORM supports DefaultExpressions / Fixing MapFunc() implementation --- .../WhenConfiguringDataSources.cs | 3 ++ .../WhenConfiguringDataSources.cs | 51 ++++++++++++++++++- .../Infrastructure/OrmTestClassBase.cs | 7 +-- .../MappingConfigContinuation.cs | 3 ++ .../Configuration/ConfiguredLambdaInfo.cs | 2 +- .../Configuration/ParametersSwapper.cs | 9 ++-- .../Members/MemberMapperDataExtensions.cs | 8 ++- .../ComplexTypeConditionalConverter.cs | 9 ++-- .../DefaultValueConstantExpressionFactory.cs | 6 +-- .../Converters/GetValueOrDefaultConverter.cs | 2 +- .../Queryables/QueryProjectionModifier.cs | 2 +- .../Settings/DefaultQueryProviderSettings.cs | 11 ++-- .../Settings/Ef5QueryProviderSettings.cs | 5 +- .../Settings/Ef6QueryProviderSettings.cs | 5 +- .../Settings/IQueryProviderSettings.cs | 2 + .../QueryProviderSettingsExtensions.cs | 5 +- 16 files changed, 96 insertions(+), 34 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index bf0eab2dd..da152dfd2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -35,5 +35,8 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() [Fact] public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); + + [Fact] + public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 485e60cee..2b6c3ec8a 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -64,6 +64,7 @@ protected Task DoShouldConditionallyApplyAConfiguredConstant() var productDtos = context .Products .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) .ToArray(); productDtos.Length.ShouldBe(2); @@ -184,18 +185,21 @@ protected Task DoShouldConditionallyApplyAConfiguredMember() context.Products.Add(product2); await context.SaveChanges(); + var product1Id = product1.ProductId; + using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping .From() .ProjectedTo() - .If(p => p.ProductId > 1) + .If(p => p.ProductId > product1Id) .Map(p => p.Name + "?!") .To(dto => dto.Name); var productDtos = context .Products .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) .ToArray(); productDtos.Length.ShouldBe(2); @@ -235,6 +239,7 @@ protected Task DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder() var productDtos = context .Products .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) .ToArray(); productDtos.Length.ShouldBe(2); @@ -247,5 +252,49 @@ protected Task DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder() } }); } + + protected Task DoShouldHandleANullMemberInACondition() + { + return RunTest(async context => + { + var person1 = new Person { Name = "Frank", Address = new Address { Line1 = "Philly" } }; + var person2 = new Person { Name = "Dee" }; + + context.Persons.Add(person1); + context.Persons.Add(person2); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Address.Line2 == null) + .Map("None") + .To(dto => dto.AddressLine2); + + var personDtos = context + .Persons + .ProjectUsing(mapper).To() + .ToArray(); + + personDtos.Length.ShouldBe(2); + + personDtos.First().Id.ShouldBe(person1.PersonId); + personDtos.First().Name.ShouldBe("Frank"); + personDtos.First().AddressId.ShouldBe(person1.Address.AddressId); + personDtos.First().AddressLine1.ShouldBe("Philly"); + personDtos.First().AddressLine2.ShouldBe("None"); + personDtos.First().AddressPostcode.ShouldBeNull(); + + personDtos.Second().Id.ShouldBe(person2.PersonId); + personDtos.Second().Name.ShouldBe("Dee"); + personDtos.Second().AddressId.ShouldBeDefault(); + personDtos.Second().AddressLine1.ShouldBeNull(); + personDtos.Second().AddressLine2.ShouldBe("None"); + personDtos.Second().AddressPostcode.ShouldBeNull(); + } + }); + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 4c2452942..1b1f4ac30 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -32,7 +32,7 @@ protected async Task RunTest(Func test) } finally { - EmptyDbContext(); + await EmptyDbContext(); } } @@ -51,7 +51,7 @@ protected async Task RunTest(Func test) } } - private void EmptyDbContext() + private async Task EmptyDbContext() { Context.Companies.Clear(); Context.Employees.Clear(); @@ -66,7 +66,8 @@ private void EmptyDbContext() Context.IntItems.Clear(); Context.LongItems.Clear(); Context.StringItems.Clear(); - Context.SaveChanges(); + + await Context.SaveChanges(); } } } diff --git a/AgileMapper/Api/Configuration/MappingConfigContinuation.cs b/AgileMapper/Api/Configuration/MappingConfigContinuation.cs index 651792490..d43fc8ae2 100644 --- a/AgileMapper/Api/Configuration/MappingConfigContinuation.cs +++ b/AgileMapper/Api/Configuration/MappingConfigContinuation.cs @@ -22,6 +22,9 @@ IFullProjectionConfigurator IProjectionConfigContinuation But => CreateNewConfigurator(); + IFullProjectionConfigurator IProjectionConfigContinuation.But + => CreateNewConfigurator(); + private MappingConfigurator CreateNewConfigurator() => new MappingConfigurator(_configInfo.Clone()); } diff --git a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs index 56b58b5f5..34bc5d601 100644 --- a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs +++ b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs @@ -49,7 +49,7 @@ private static Type[] GetContextTypes(Type[] funcArguments) return funcArguments[0].GetGenericTypeArguments(); } - return new[] { funcArguments[0], typeof(object) }; + return new[] { funcArguments[0] }; } public static ConfiguredLambdaInfo ForFunc(TFunc func, params Type[] argumentTypes) diff --git a/AgileMapper/Configuration/ParametersSwapper.cs b/AgileMapper/Configuration/ParametersSwapper.cs index 97889cbcd..14f8a6342 100644 --- a/AgileMapper/Configuration/ParametersSwapper.cs +++ b/AgileMapper/Configuration/ParametersSwapper.cs @@ -18,7 +18,7 @@ internal class ParametersSwapper { new ParametersSwapper(0, (ct, ft) => true, SwapNothing), new ParametersSwapper(1, IsContext, SwapForContextParameter), - new ParametersSwapper(1, IsSource, SwapForSource), + new ParametersSwapper(1, IsSourceOnly, SwapForSource), new ParametersSwapper(2, IsSourceAndTarget, SwapForSourceAndTarget), new ParametersSwapper(3, IsSourceTargetAndIndex, SwapForSourceTargetAndIndex), new ParametersSwapper(3, IsSourceTargetAndCreatedObject, SwapForSourceTargetAndCreatedObject), @@ -54,7 +54,10 @@ private static bool Is( return parametersChecker.Invoke(contextTypes, contextTypeArgument.GetGenericTypeArguments()); } - private static bool IsSource(Type[] contextTypes, Type[] funcArguments) + private static bool IsSourceOnly(Type[] contextTypes, Type[] funcArguments) + => (contextTypes.Length == 1) && IsSource(contextTypes, funcArguments); + + private static bool IsSource(IList contextTypes, IList funcArguments) => contextTypes[0].IsAssignableTo(funcArguments[0]); private static bool IsSourceAndTarget(Type[] contextTypes, Type[] funcArguments) @@ -295,7 +298,7 @@ public SwapArgs( Func targetValueFactory) { Lambda = lambda; - ContextTypes = contextTypes; + ContextTypes = (contextTypes.Length > 1) ? contextTypes : contextTypes.Append(typeof(object)); MapperData = mapperData; TargetValueFactory = targetValueFactory; } diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index aae0252f6..1df284114 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -431,8 +431,8 @@ public static Expression GetTypedContextAccess( private static Expression GetFinalContextAccess( Expression contextAccess, - Type[] contextTypes, - Type[] contextAccessTypes = null) + IList contextTypes, + IList contextAccessTypes = null) { if ((contextAccessTypes == null) && !contextAccess.Type.IsGenericType()) { @@ -453,9 +453,7 @@ private static Expression GetFinalContextAccess( } public static Expression GetTargetMemberPopulation(this IMemberMapperData mapperData, Expression value) - { - return mapperData.TargetMember.GetPopulation(value, mapperData); - } + => mapperData.TargetMember.GetPopulation(value, mapperData); public static Expression GetAsCall(this IMemberMapperData mapperData, Type sourceType, Type targetType) => GetAsCall(mapperData.MappingDataObject, sourceType, targetType); diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs index 68b287207..c610ecbcf 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -3,6 +3,7 @@ using System.Linq.Expressions; using Extensions.Internal; using ReadableExpressions.Extensions; + using Settings; internal static class ComplexTypeConditionalConverter { @@ -17,16 +18,18 @@ public static bool TryConvert( return false; } - converted = new NonNullableMemberBinder(conditional).GuardMemberAccesses(); + converted = new NonNullableMemberBinder(modifier, conditional).GuardMemberAccesses(); return true; } public class NonNullableMemberBinder : ExpressionVisitor { + private readonly IQueryProviderSettings _settings; private readonly ConditionalExpression _conditional; - public NonNullableMemberBinder(ConditionalExpression conditional) + public NonNullableMemberBinder(IQueryProjectionModifier modifier, ConditionalExpression conditional) { + _settings = modifier.Settings; _conditional = conditional; } @@ -50,7 +53,7 @@ protected override MemberBinding VisitMemberBinding(MemberBinding binding) var bindingValueOrNull = Expression.Condition( _conditional.Test, memberBinding.Expression, - DefaultValueConstantExpressionFactory.CreateFor(memberBinding.Expression)); + _settings.GetDefaultValueFor(memberBinding.Expression)); return memberBinding.Update(bindingValueOrNull); } diff --git a/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs b/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs index fc187577b..0829df5c5 100644 --- a/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs +++ b/AgileMapper/Queryables/Converters/DefaultValueConstantExpressionFactory.cs @@ -11,10 +11,8 @@ internal static class DefaultValueConstantExpressionFactory private static readonly MethodInfo _getDefaultValueMethod = typeof(DefaultValueConstantExpressionFactory) .GetNonPublicStaticMethod("GetDefaultValue"); - public static Expression CreateFor(Expression expression) => CreateFor(expression.Type); - - public static Expression CreateFor(Type type) - => GetDefaultValueFor(type).ToConstantExpression(type); + public static Expression CreateFor(Expression expression) + => GetDefaultValueFor(expression.Type).ToConstantExpression(expression.Type); private static object GetDefaultValueFor(Type type) { diff --git a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index ad39e590a..f267627e6 100644 --- a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -21,7 +21,7 @@ public static bool TryConvert( converted = Expression.Condition( methodCall.Object.GetIsNotDefaultComparison(), Expression.Convert(methodCall.Object, methodCall.Type), - DefaultValueConstantExpressionFactory.CreateFor(methodCall.Type)); + context.Settings.GetDefaultValueFor(methodCall)); return true; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 3272e9a24..e3306d3b2 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -56,7 +56,7 @@ protected override Expression VisitConstant(ConstantExpression constant) } protected override Expression VisitDefault(DefaultExpression defaultExpression) - => DefaultValueConstantExpressionFactory.CreateFor(defaultExpression); + => Settings.GetDefaultValueFor(defaultExpression); protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index e71edccf3..614ccacf1 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -39,6 +39,9 @@ public DefaultQueryProviderSettings() public virtual bool SupportsEnumerableMaterialisation => true; + public virtual Expression GetDefaultValueFor(Expression value) + => value.NodeType != ExpressionType.Default ? value.Type.ToDefaultExpression() : value; + public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); @@ -76,18 +79,14 @@ public Expression ConvertTryParseCall(MethodCallExpression call, Expression fall protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => null; - private static Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) + private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) { var parseMethod = typeof(Guid) .GetPublicStaticMethod("Parse", parameterCount: 1); var sourceValue = guidTryParseCall.Arguments.First(); var guidConversion = Expression.Call(parseMethod, sourceValue); - - if (fallbackValue.NodeType == ExpressionType.Default) - { - fallbackValue = DefaultValueConstantExpressionFactory.CreateFor(fallbackValue); - } + fallbackValue = GetDefaultValueFor(fallbackValue); var nullString = default(string).ToConstantExpression(); var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 6c1a0cb34..4c82246dc 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -1,8 +1,9 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { + using System.Linq.Expressions; + using Converters; #if !NET_STANDARD using System; - using System.Linq.Expressions; using Extensions.Internal; using NetStandardPolyfills; #endif @@ -64,5 +65,7 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); #endif + public override Expression GetDefaultValueFor(Expression value) + => DefaultValueConstantExpressionFactory.CreateFor(value); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index d860af797..2d93d9ba3 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -2,8 +2,9 @@ { #if !NET_STANDARD using System; - using System.Linq.Expressions; #endif + using System.Linq.Expressions; + using Converters; internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { @@ -21,5 +22,7 @@ protected override Type LoadSqlFunctionsType() protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); #endif + public override Expression GetDefaultValueFor(Expression value) + => DefaultValueConstantExpressionFactory.CreateFor(value); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 94022e38a..9894b56be 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -24,6 +24,8 @@ internal interface IQueryProviderSettings bool SupportsEnumerableMaterialisation { get; } + Expression GetDefaultValueFor(Expression value); + Expression ConvertToStringCall(MethodCallExpression call); Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index b25fdbb02..2b34a3261 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -66,10 +66,7 @@ public static Expression GetCreateDateTimeFromStringOrNull( GetDatePartCall(datePartMethod, "mi", sourceValue), GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo()); - if (fallbackValue.NodeType == ExpressionType.Default) - { - fallbackValue = DefaultValueConstantExpressionFactory.CreateFor(fallbackValue); - } + fallbackValue = settings.GetDefaultValueFor(fallbackValue); var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); From 82cbb0aeb662d710c12cae520931ccc634bafec1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 6 Feb 2018 14:26:26 +0000 Subject: [PATCH 119/176] Test coverage for mapper divergence in projection --- .../WhenConfiguringDataSources.cs | 3 ++ .../WhenConfiguringDataSources.cs | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index da152dfd2..4d0e8439a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -38,5 +38,8 @@ public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() [Fact] public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); + + [Fact] + public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 2b6c3ec8a..ceb3aed1e 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -296,5 +296,46 @@ protected Task DoShouldHandleANullMemberInACondition() } }); } + + protected Task DoShouldSupportMultipleDivergedMappers() + { + return RunTest(async context => + { + var address = new Address { Line1 = "Philly", Postcode = "PH1 1LY" }; + + context.Addresses.Add(address); + await context.SaveChanges(); + + using (var mapper1 = Mapper.CreateNew()) + using (var mapper2 = Mapper.CreateNew()) + { + mapper2.WhenMapping + .From
() + .ProjectedTo() + .Map(p => p.Line1) + .To(dto => dto.Line2); + + var addressDto1 = context + .Addresses + .ProjectUsing(mapper1).To() + .ShouldHaveSingleItem(); + + addressDto1.Id.ShouldBe(address.AddressId); + addressDto1.Line1.ShouldBe("Philly"); + addressDto1.Line2.ShouldBeNull(); + addressDto1.Postcode.ShouldBe("PH1 1LY"); + + var addressDto2 = context + .Addresses + .ProjectUsing(mapper2).To() + .First(); + + addressDto2.Id.ShouldBe(address.AddressId); + addressDto2.Line1.ShouldBe("Philly"); + addressDto2.Line2.ShouldBe("Philly"); + addressDto2.Postcode.ShouldBe("PH1 1LY"); + } + }); + } } } \ No newline at end of file From 6a816d25353d1d186674a046aa4098dac48e1b1c Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 6 Feb 2018 15:11:12 +0000 Subject: [PATCH 120/176] Expanding custom data source test coverage / Providing a workaround for EF5 + EF6 not supporting all primitive constants --- ...leMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 1 + .../WhenConfiguringDataSources.cs | 24 ++++++++ .../WhenConfiguringDataSources.cs | 24 +++++++- .../WhenConfiguringDataSources.cs | 30 +++++++++- .../WhenConfiguringDataSources.cs | 30 +++++++++- .../WhenConfiguringDataSources.cs | 1 + .../Internal/StringExpressionExtensions.cs | 3 + .../Converters/StringConcatConverter.cs | 57 +++++++++++++++++++ .../Queryables/QueryProjectionModifier.cs | 5 ++ .../Settings/DefaultQueryProviderSettings.cs | 2 + .../Settings/Ef5QueryProviderSettings.cs | 3 + .../Settings/Ef6QueryProviderSettings.cs | 3 + .../Settings/IQueryProviderSettings.cs | 2 + 13 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringDataSources.cs create mode 100644 AgileMapper/Queryables/Converters/StringConcatConverter.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index 4602e6390..94b3b13da 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -80,6 +80,7 @@ VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringDataSources.cs new file mode 100644 index 000000000..31f8237f2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringDataSources.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Orms.Infrastructure; + using Xunit; + + public class WhenConfiguringDataSources : WhenConfiguringDataSources + { + public WhenConfiguringDataSources(LocalDbTestContext context) + : base(context) + { + } + + // Executed in LocalDb because the configured conditions require string conversions + + [Fact] + public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs index 3cf335cd2..46a6cc021 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs @@ -13,7 +13,27 @@ public WhenConfiguringDataSources(InMemoryEf5TestContext context) } [Fact] - public Task ShouldApplyAConfiguredConstant() - => DoShouldApplyAConfiguredConstant(); + public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredConstant() + => DoShouldConditionallyApplyAConfiguredConstant(); + + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMember() + => DoShouldApplyAConfiguredConstantToANestedMember(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); + + [Fact] + public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); + + [Fact] + public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs index 36df89667..a47822d31 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs @@ -13,7 +13,33 @@ public WhenConfiguringDataSources(InMemoryEf6TestContext context) } [Fact] - public Task ShouldApplyAConfiguredConstant() - => DoShouldApplyAConfiguredConstant(); + public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredConstant() + => DoShouldConditionallyApplyAConfiguredConstant(); + + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMember() + => DoShouldApplyAConfiguredConstantToANestedMember(); + + [Fact] + public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); + + [Fact] + public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); + + [Fact] + public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs index dc291ed58..04ba32c98 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs @@ -13,7 +13,33 @@ public WhenConfiguringDataSources(InMemoryEfCore1TestContext context) } [Fact] - public Task ShouldApplyAConfiguredConstant() - => DoShouldApplyAConfiguredConstant(); + public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredConstant() + => DoShouldConditionallyApplyAConfiguredConstant(); + + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMember() + => DoShouldApplyAConfiguredConstantToANestedMember(); + + [Fact] + public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); + + [Fact] + public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); + + [Fact] + public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); + + [Fact] + public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); + + [Fact] + public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index ceb3aed1e..16c3f5ff2 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -276,6 +276,7 @@ protected Task DoShouldHandleANullMemberInACondition() var personDtos = context .Persons .ProjectUsing(mapper).To() + .OrderBy(p => p.Id) .ToArray(); personDtos.Length.ShouldBe(2); diff --git a/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs b/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs index 92e142651..8379fd125 100644 --- a/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/StringExpressionExtensions.cs @@ -38,6 +38,9 @@ static StringExpressionExtensions() .ToArray(); } + public static MethodInfo GetConcatMethod(int parameterCount) + => _stringConcatMethods.First(m => m.GetParameters().Length == parameterCount); + public static Expression GetStringConcatCall(this IList expressions) { if (expressions.None()) diff --git a/AgileMapper/Queryables/Converters/StringConcatConverter.cs b/AgileMapper/Queryables/Converters/StringConcatConverter.cs new file mode 100644 index 000000000..766afd552 --- /dev/null +++ b/AgileMapper/Queryables/Converters/StringConcatConverter.cs @@ -0,0 +1,57 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System.Linq.Expressions; + using System.Reflection; + using Extensions.Internal; + using NetStandardPolyfills; + + internal static class StringConcatConverter + { + private static readonly MethodInfo _stringConcatObjectsMethod = typeof(string) + .GetPublicStaticMethod("Concat", typeof(object), typeof(object)); + + private static readonly MethodInfo _stringConcatStringsMethod = + StringExpressionExtensions.GetConcatMethod(parameterCount: 2); + + public static bool TryConvert( + BinaryExpression binary, + IQueryProjectionModifier context, + out Expression converted) + { + if (context.Settings.SupportsAllPrimitiveConstants || + (binary.NodeType != ExpressionType.Add) || + !ReferenceEquals(binary.Method, _stringConcatObjectsMethod) || + ((binary.Left.NodeType != ExpressionType.Convert) && (binary.Right.NodeType != ExpressionType.Convert))) + { + converted = null; + return false; + } + + var convertedLeft = ConvertOperand(binary.Left); + var convertedRight = ConvertOperand(binary.Right); + + if ((convertedLeft == binary.Left) && (convertedRight == binary.Right)) + { + converted = null; + return false; + } + + converted = Expression.Add(convertedLeft, convertedRight, _stringConcatStringsMethod); + return true; + } + + private static Expression ConvertOperand(Expression value) + { + if ((value.NodeType != ExpressionType.Convert) || (value.Type == typeof(string))) + { + return value; + } + + var conversion = (UnaryExpression)value; + + return (conversion.Operand.NodeType == ExpressionType.Constant) + ? ((ConstantExpression)conversion.Operand).Value.ToString().ToConstantExpression() + : value; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index e3306d3b2..060346352 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -32,6 +32,11 @@ protected override Expression VisitBinary(BinaryExpression binary) return converted; } + if (StringConcatConverter.TryConvert(binary, this, out converted)) + { + return converted; + } + return base.VisitBinary(binary); } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 614ccacf1..b0ca75097 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -35,6 +35,8 @@ public DefaultQueryProviderSettings() public virtual bool SupportsComplexTypeToNullComparison => true; + public virtual bool SupportsAllPrimitiveConstants => true; + public virtual bool SupportsNonEntityNullConstants => true; public virtual bool SupportsEnumerableMaterialisation => true; diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 4c82246dc..df47ae6e8 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -14,6 +14,9 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsGetValueOrDefault => false; + // Does not support character constants: + public override bool SupportsAllPrimitiveConstants => false; + public override bool SupportsNonEntityNullConstants => false; public override bool SupportsEnumerableMaterialisation => false; diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 2d93d9ba3..271d9433b 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -12,6 +12,9 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsEnumerableMaterialisation => false; + // Does not support character constants: + public override bool SupportsAllPrimitiveConstants => false; + #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 9894b56be..e81340fde 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -20,6 +20,8 @@ internal interface IQueryProviderSettings bool SupportsComplexTypeToNullComparison { get; } + bool SupportsAllPrimitiveConstants { get; } + bool SupportsNonEntityNullConstants { get; } bool SupportsEnumerableMaterialisation { get; } From 1c3a351249e7f3d617381e69ea3ece7688ad8219 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 6 Feb 2018 17:39:58 +0000 Subject: [PATCH 121/176] Adding EFCore2.NetCore2 test project / Organising EFCore test project version number sharing --- .../AgileMapper.UnitTests.NetCore2.csproj | 2 +- ...per.UnitTests.Orms.EfCore2.NetCore2.csproj | 71 +++++++++++++++++++ .../AgileMapper.UnitTests.Orms.Ef5.csproj | 4 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 4 +- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 6 ++ .../Properties/AssemblyInfo.cs | 19 +---- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 6 ++ .../Properties/AssemblyInfo.cs | 19 +---- .../AgileMapper.UnitTests.Orms.csproj | 4 +- AgileMapper.sln | 6 ++ 10 files changed, 98 insertions(+), 43 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj diff --git a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj index 505e9c2a5..26a124ab8 100644 --- a/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj +++ b/AgileMapper.UnitTests.NetCore2/AgileMapper.UnitTests.NetCore2.csproj @@ -7,7 +7,7 @@ AgileObjects.AgileMapper.UnitTests.NetCore2 AgileObjects.AgileMapper.UnitTests.NetCore2 true - 2.0.0 + 2.0.5 false false false diff --git a/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj b/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj new file mode 100644 index 000000000..bb78cf9c7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj @@ -0,0 +1,71 @@ + + + + + netcoreapp2.0 + true + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.NetCore2 + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.NetCore2 + true + 2.0.5 + false + false + false + false + false + false + false + AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.NetCore2 + + + + TRACE;DEBUG;NETCOREAPP2_0;NET_STANDARD + + + + TRACE;RELEASE;NETCOREAPP2_0;NET_STANDARD + + + + + + + + + + + + + + + + + + + + + + + + + %(RecursiveDir)%(Filename)%(Extension) + + + + + + + + + + + + + + + + + + + + diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 4e3cabffe..55acf90bd 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -80,10 +80,10 @@ - CommonAssemblyInfo.cs + Properties\CommonAssemblyInfo.cs - VersionInfo.cs + Properties\VersionInfo.cs diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 673546a16..b7d18b6ad 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -82,10 +82,10 @@ - CommonAssemblyInfo.cs + Properties\CommonAssemblyInfo.cs - VersionInfo.cs + Properties\VersionInfo.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index dda2ea585..4769f9592 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -205,6 +205,12 @@ + + Properties\CommonAssemblyInfo.cs + + + Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs index fa9cb5e39..a4f5d441d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -8,9 +7,6 @@ [assembly: AssemblyTitle("AgileMapper.UnitTests.Orms.EfCore1")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AgileMapper.UnitTests.Orms.EfCore1")] -[assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,17 +16,4 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 1d7956842..e2b4a63e5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -139,6 +139,12 @@ + + Properties\CommonAssemblyInfo.cs + + + Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs b/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs index 4c2df25a4..eb6d0e1de 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -8,9 +7,6 @@ [assembly: AssemblyTitle("AgileMapper.UnitTests.Orms.EfCore2")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AgileMapper.UnitTests.Orms.EfCore2")] -[assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,17 +16,4 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: Guid("9abbd6a9-5f95-442a-88db-3fc1ebf374a7")] \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 5d4f7c9ac..5302933df 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -68,10 +68,10 @@ - CommonAssemblyInfo.cs + Properties\CommonAssemblyInfo.cs - VersionInfo.cs + Properties\VersionInfo.cs diff --git a/AgileMapper.sln b/AgileMapper.sln index 4f72375a7..137b7e01f 100644 --- a/AgileMapper.sln +++ b/AgileMapper.sln @@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgileMapper.UnitTests.Orms.Ef6.LocalDb", "AgileMapper.UnitTests.Orms.Ef6.LocalDb\AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj", "{B75D1A61-006A-4951-82FE-A2943296A872}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AgileMapper.UnitTests.Orms.EfCore2.NetCore2", "AgileMapper.UnitTests.Orms.EFCore2.NetCore2\AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj", "{A6A9D59E-905E-4EC8-ABDD-62FA4E3B6B19}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -104,6 +106,10 @@ Global {B75D1A61-006A-4951-82FE-A2943296A872}.Debug|Any CPU.Build.0 = Debug|Any CPU {B75D1A61-006A-4951-82FE-A2943296A872}.Release|Any CPU.ActiveCfg = Release|Any CPU {B75D1A61-006A-4951-82FE-A2943296A872}.Release|Any CPU.Build.0 = Release|Any CPU + {A6A9D59E-905E-4EC8-ABDD-62FA4E3B6B19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6A9D59E-905E-4EC8-ABDD-62FA4E3B6B19}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6A9D59E-905E-4EC8-ABDD-62FA4E3B6B19}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6A9D59E-905E-4EC8-ABDD-62FA4E3B6B19}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 0748cbc1d7bed8d717aed7c7e0aec6f40ba05399 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 08:45:37 +0000 Subject: [PATCH 122/176] Start of support for configuring projection member ignores --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Configuration/WhenIgnoringMembers.cs | 18 ++++++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + .../Configuration/WhenIgnoringMembers.cs | 42 +++++++++++++++++++ .../Configuration/WhenIgnoringMembers.cs | 2 +- .../Configuration/IRootMappingConfigurator.cs | 4 +- .../Api/Configuration/MappingConfigurator.cs | 11 +++++ .../Projection/IRootProjectionConfigurator.cs | 14 +++++++ 8 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index e2b4a63e5..1da3af445 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -146,6 +146,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs new file mode 100644 index 000000000..b0d16b78d --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenIgnoringMembers : WhenIgnoringMembers + { + public WhenIgnoringMembers(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 5302933df..0e2653ab8 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,6 +73,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs new file mode 100644 index 000000000..bfe213273 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -0,0 +1,42 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + + public abstract class WhenIgnoringMembers : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenIgnoringMembers(ITestContext context) + : base(context) + { + } + + protected Task DoShouldIgnoreAConfiguredMember() + { + return RunTest(async context => + { + var product = new Product { Name = "P1" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Ignore(p => p.Name); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBeNull(); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs index 9db84a4d9..59a5e1bc0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests/Configuration/WhenIgnoringMembers.cs @@ -16,7 +16,7 @@ public void ShouldIgnoreAConfiguredMember() mapper.WhenMapping .From() .ToANew() - .Ignore(x => x.Name); + .Ignore(pvm => pvm.Name); var source = new PersonViewModel { Name = "Jon" }; var result = mapper.Map(source).ToANew(); diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index fe0c14ed0..993c214fd 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -64,8 +64,8 @@ IMappingConfigContinuation CreateInstancesUsing( IFactorySpecifier CreateInstancesOf() where TObject : class; /// - /// Ignore the target member(s) identified by the argument when mapping - /// from and to the source and target types being configured. + /// Ignore the given when mappingfrom and to the source and target types + /// being configured. /// /// The target member(s) which should be ignored. /// diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index baab26510..c785051b5 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration { using System; + using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using AgileMapper.Configuration; @@ -190,6 +191,16 @@ public IMappingConfigContinuation IgnoreTargetMembersWhere( } public IMappingConfigContinuation Ignore(params Expression>[] targetMembers) + => IgnoreMembers(targetMembers); + + IProjectionConfigContinuation IRootProjectionConfigurator.Ignore( + params Expression>[] resultMembers) + { + return IgnoreMembers(resultMembers); + } + + private MappingConfigContinuation IgnoreMembers( + IEnumerable>> targetMembers) { foreach (var targetMember in targetMembers) { diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index 1b867854a..d91aeb844 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -10,6 +10,18 @@ /// The result type to which the configuration should apply. public interface IRootProjectionConfigurator { + /// + /// Ignore the specified when projecting from and to the source and + /// result types being configured. + /// + /// The result member(s) which should be ignored. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source + /// and result type being configured. + /// + IProjectionConfigContinuation Ignore( + params Expression>[] resultMembers); + /// /// Configure a custom data source for a particular result member when mapping from and to the source and /// result types being configured. The factory expression is passed the source element being projected. @@ -34,5 +46,7 @@ ICustomProjectionDataSourceTargetMemberSpecifier /// constant value should be applied. /// ICustomProjectionDataSourceTargetMemberSpecifier Map(TSourceValue value); + + } } \ No newline at end of file From 7b35be1e81153f09eb1a2e4c5d2c991f585e4a9d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 08:58:24 +0000 Subject: [PATCH 123/176] Skipping EFCore complex type to null comparison conversions when there's no complex type id available --- .../ComplexTypeToNullComparisonConverter.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs index e5ebaa841..50dd6296c 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeToNullComparisonConverter.cs @@ -23,23 +23,26 @@ public static bool TryConvert( } converted = Convert(comparison, context); - return true; + return converted != null; } private static Expression Convert(BinaryExpression comparison, IQueryProjectionModifier context) { if (!context.MapperData.IsEntity(comparison.Left.Type, out var idMember)) { - return true.ToConstantExpression(); + return null; } var entityMemberAccess = comparison.Left; var entityParentAccess = entityMemberAccess.GetParentOrNull(); - var entityMemberIdMember = GetEntityMemberIdMemberOrNull(entityParentAccess, entityMemberAccess, idMember); - if (entityMemberIdMember == null) + if (!TryGetEntityMemberIdMemberOrNull( + entityParentAccess, + entityMemberAccess, + idMember, + out var entityMemberIdMember)) { - return true.ToConstantExpression(); + return null; } var entityMemberIdMemberAccess = entityMemberIdMember.GetAccess(entityParentAccess); @@ -47,10 +50,11 @@ private static Expression Convert(BinaryExpression comparison, IQueryProjectionM return entityMemberIdMemberAccess.GetIsNotDefaultComparison(); } - private static Member GetEntityMemberIdMemberOrNull( + private static bool TryGetEntityMemberIdMemberOrNull( Expression entityParentAccess, Expression entityMemberAccess, - Member entityIdMember) + Member entityIdMember, + out Member entityMemberIdMember) { var sourceMembers = GlobalContext .Instance @@ -61,24 +65,25 @@ private static Member GetEntityMemberIdMemberOrNull( var entityMemberName = entityMemberAccess.GetMemberName(); - if (TryGetEntityMemberIdMember(entityMemberName + entityIdMember.Name, sourceMembers, out var member)) + if (TryGetEntityMemberIdMember(entityMemberName + entityIdMember.Name, sourceMembers, out entityMemberIdMember)) { - return member; + return true; } if (!entityIdMember.Name.EqualsIgnoreCase("Id") && - TryGetEntityMemberIdMember(entityMemberName + "Id", sourceMembers, out member)) + TryGetEntityMemberIdMember(entityMemberName + "Id", sourceMembers, out entityMemberIdMember)) { - return member; + return true; } if (!entityIdMember.Name.EqualsIgnoreCase("Identifer") && - TryGetEntityMemberIdMember(entityMemberName + "Identifer", sourceMembers, out member)) + TryGetEntityMemberIdMember(entityMemberName + "Identifer", sourceMembers, out entityMemberIdMember)) { - return member; + return true; } - return null; + entityMemberIdMember = null; + return false; } private static bool TryGetEntityMemberIdMember( From 8f90f5de050cf5358ccf6d3886a4db51af9f6f1f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 09:48:22 +0000 Subject: [PATCH 124/176] Start of conditional projection ignore support --- .../Configuration/WhenIgnoringMembers.cs | 4 ++ .../Configuration/WhenIgnoringMembers.cs | 34 ++++++++++++++ .../Converters/StringConcatConverter.cs | 45 ++++++++++++++----- .../Settings/DefaultQueryProviderSettings.cs | 3 -- .../Settings/Ef5QueryProviderSettings.cs | 3 -- .../Settings/Ef6QueryProviderSettings.cs | 3 -- .../Settings/IQueryProviderSettings.cs | 2 - 7 files changed, 72 insertions(+), 22 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs index b0d16b78d..e9010c2ad 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs @@ -14,5 +14,9 @@ public WhenIgnoringMembers(InMemoryEfCore2TestContext context) [Fact] public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); + + [Fact] + public Task ShouldIgnoreAConfiguredMemberConditionally() => + DoShouldIgnoreAConfiguredMemberConditionally(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index bfe213273..75ebf0282 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration { + using System.Linq; using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -38,5 +39,38 @@ protected Task DoShouldIgnoreAConfiguredMember() } }); } + + protected Task DoShouldIgnoreAConfiguredMemberConditionally() + { + return RunTest(async context => + { + var product1 = new Product { Name = "P.1" }; + var product2 = new Product { Name = "P.2" }; + + context.Products.Add(product1); + context.Products.Add(product2); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Name.EndsWith(2 + "")) + .Ignore(p => p.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1"); + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBeNull(); + } + }); + } } } diff --git a/AgileMapper/Queryables/Converters/StringConcatConverter.cs b/AgileMapper/Queryables/Converters/StringConcatConverter.cs index 766afd552..6a41dc8ae 100644 --- a/AgileMapper/Queryables/Converters/StringConcatConverter.cs +++ b/AgileMapper/Queryables/Converters/StringConcatConverter.cs @@ -18,10 +18,9 @@ public static bool TryConvert( IQueryProjectionModifier context, out Expression converted) { - if (context.Settings.SupportsAllPrimitiveConstants || - (binary.NodeType != ExpressionType.Add) || - !ReferenceEquals(binary.Method, _stringConcatObjectsMethod) || - ((binary.Left.NodeType != ExpressionType.Convert) && (binary.Right.NodeType != ExpressionType.Convert))) + if ((binary.NodeType != ExpressionType.Add) || + !ReferenceEquals(binary.Method, _stringConcatObjectsMethod) || + ((binary.Left.NodeType != ExpressionType.Convert) && (binary.Right.NodeType != ExpressionType.Convert))) { converted = null; return false; @@ -36,22 +35,46 @@ public static bool TryConvert( return false; } + if ((convertedLeft == null) || (convertedRight == null)) + { + converted = convertedLeft ?? convertedRight; + return true; + } + converted = Expression.Add(convertedLeft, convertedRight, _stringConcatStringsMethod); return true; } private static Expression ConvertOperand(Expression value) { - if ((value.NodeType != ExpressionType.Convert) || (value.Type == typeof(string))) + while (true) { - return value; - } + switch (value.NodeType) + { + case ExpressionType.Constant: + var constant = ((ConstantExpression)value).Value; - var conversion = (UnaryExpression)value; + return (constant == null) + ? null + : (value.Type != typeof(string)) + ? constant.ToString().ToConstantExpression() + : ((string)constant != string.Empty) ? value : null; - return (conversion.Operand.NodeType == ExpressionType.Constant) - ? ((ConstantExpression)conversion.Operand).Value.ToString().ToConstantExpression() - : value; + case ExpressionType.Convert: + var conversion = (UnaryExpression)value; + + if ((conversion.Operand.NodeType == ExpressionType.Constant)) + { + value = conversion.Operand; + continue; + } + + return value; + + default: + return value; + } + } } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index b0ca75097..f6611f46e 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using System.Linq.Expressions; - using Converters; using Extensions.Internal; using NetStandardPolyfills; @@ -35,8 +34,6 @@ public DefaultQueryProviderSettings() public virtual bool SupportsComplexTypeToNullComparison => true; - public virtual bool SupportsAllPrimitiveConstants => true; - public virtual bool SupportsNonEntityNullConstants => true; public virtual bool SupportsEnumerableMaterialisation => true; diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index df47ae6e8..4c82246dc 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -14,9 +14,6 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsGetValueOrDefault => false; - // Does not support character constants: - public override bool SupportsAllPrimitiveConstants => false; - public override bool SupportsNonEntityNullConstants => false; public override bool SupportsEnumerableMaterialisation => false; diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 271d9433b..2d93d9ba3 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -12,9 +12,6 @@ internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsEnumerableMaterialisation => false; - // Does not support character constants: - public override bool SupportsAllPrimitiveConstants => false; - #if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index e81340fde..9894b56be 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -20,8 +20,6 @@ internal interface IQueryProviderSettings bool SupportsComplexTypeToNullComparison { get; } - bool SupportsAllPrimitiveConstants { get; } - bool SupportsNonEntityNullConstants { get; } bool SupportsEnumerableMaterialisation { get; } From f0c6c3ec00ae4f2faf1e4d02cf5d9135f89a7bac Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 10:48:58 +0000 Subject: [PATCH 125/176] Support for conditional projection result member ignores --- .../Members/Population/MemberPopulation.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/AgileMapper/Members/Population/MemberPopulation.cs b/AgileMapper/Members/Population/MemberPopulation.cs index 9085c56b8..fb96d0ec0 100644 --- a/AgileMapper/Members/Population/MemberPopulation.cs +++ b/AgileMapper/Members/Population/MemberPopulation.cs @@ -115,7 +115,9 @@ public Expression GetPopulation() return _dataSources.GetValueExpression(); } - var population = MapperData.UseMemberInitialisation() + var useSingleExpression = MapperData.UseMemberInitialisation(); + + var population = useSingleExpression ? GetBinding() : MapperData.TargetMember.IsReadOnly ? GetReadOnlyMemberPopulation() @@ -126,7 +128,7 @@ public Expression GetPopulation() population = Expression.Block(_dataSources.Variables, population); } - if (_populateCondition != null) + if (!useSingleExpression && (_populateCondition != null)) { population = Expression.IfThen(_populateCondition, population); } @@ -137,6 +139,15 @@ public Expression GetPopulation() private Expression GetBinding() { var bindingValue = _dataSources.GetValueExpression(); + + if (_populateCondition != null) + { + bindingValue = Expression.Condition( + _populateCondition, + bindingValue, + bindingValue.Type.ToDefaultExpression()); + } + var binding = MapperData.GetTargetMemberPopulation(bindingValue); return binding; From 150a86bc9e2e09e46cd21b246169bd7d6f81f4cc Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 11:17:03 +0000 Subject: [PATCH 126/176] Support for PropertyInfo match ignores in projections --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../Configuration/WhenIgnoringMembers.cs | 22 ++++++++++ .../Configuration/WhenIgnoringMembers.cs | 4 ++ .../Configuration/WhenIgnoringMembers.cs | 41 +++++++++++++++++++ .../Configuration/IRootMappingConfigurator.cs | 5 ++- .../Api/Configuration/MappingConfigurator.cs | 12 ++++++ .../Projection/IRootProjectionConfigurator.cs | 13 ++++++ 7 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 4769f9592..b88530cc7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -212,6 +212,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs new file mode 100644 index 000000000..5b74fce81 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs @@ -0,0 +1,22 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenIgnoringMembers : WhenIgnoringMembers + { + public WhenIgnoringMembers(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); + + [Fact] + public Task ShouldIgnoreAConfiguredMemberConditionally() => + DoShouldIgnoreAConfiguredMemberConditionally(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs index e9010c2ad..3900131a6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs @@ -18,5 +18,9 @@ public WhenIgnoringMembers(InMemoryEfCore2TestContext context) [Fact] public Task ShouldIgnoreAConfiguredMemberConditionally() => DoShouldIgnoreAConfiguredMemberConditionally(); + + [Fact] + public Task ShouldIgnorePropertiesByPropertyInfoMatcher() + => DoShouldIgnorePropertiesByPropertyInfoMatcher(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index 75ebf0282..875319825 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -72,5 +72,46 @@ protected Task DoShouldIgnoreAConfiguredMemberConditionally() } }); } + + protected Task DoShouldIgnorePropertiesByPropertyInfoMatcher() + { + return RunTest(async context => + { + var person = new Person + { + Name = "Frankie", + Address = new Address { Line1 = "1", Line2 = "2", Postcode = "3" } + }; + + context.Persons.Add(person); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .To() + .IgnoreTargetMembersWhere(member => + member.IsPropertyMatching(p => p.GetGetMethod().Name.StartsWith("get_Line2"))); + + mapper.WhenMapping + .From
() + .ProjectedTo() + .IgnoreTargetMembersWhere(member => + member.IsPropertyMatching(p => p.GetSetMethod().Name.EndsWith("Line1"))); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Frankie"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBeNull(); + personDto.Address.Line2.ShouldBeNull(); + personDto.Address.Postcode.ShouldBe("3"); + } + }); + } } } diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index 993c214fd..bbae8717b 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -92,9 +92,10 @@ IMappingConfigContinuation CreateInstancesUsing( /// The matching function with which to select target members to ignore. /// /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. + /// target types being configured. /// - IMappingConfigContinuation IgnoreTargetMembersWhere(Expression> memberFilter); + IMappingConfigContinuation IgnoreTargetMembersWhere( + Expression> memberFilter); /// /// Configure a custom data source for a particular target member when mapping from and to the source and diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index c785051b5..076cbaf09 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -182,6 +182,18 @@ public IMappingConfigContinuation IgnoreTargetMembersOfType IgnoreTargetMembersWhere( Expression> memberFilter) + { + return IgnoreMembersByFilter(memberFilter); + } + + IProjectionConfigContinuation IRootProjectionConfigurator.IgnoreTargetMembersWhere( + Expression> memberFilter) + { + return IgnoreMembersByFilter(memberFilter); + } + + private MappingConfigContinuation IgnoreMembersByFilter( + Expression> memberFilter) { var configuredIgnoredMember = new ConfiguredIgnoredMember(ConfigInfo, memberFilter); diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index d91aeb844..e7de313f4 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -2,6 +2,7 @@ { using System; using System.Linq.Expressions; + using AgileMapper.Configuration; /// /// Provides options for configuring projections from and to a given source and result type. @@ -22,6 +23,18 @@ public interface IRootProjectionConfigurator IProjectionConfigContinuation Ignore( params Expression>[] resultMembers); + /// + /// Ignore all result member(s) matching the given when projecting + /// from and to the source and result types being configured. + /// + /// The matching function with which to select result members to ignore. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source and + /// result types being configured. + /// + IProjectionConfigContinuation IgnoreTargetMembersWhere( + Expression> memberFilter); + /// /// Configure a custom data source for a particular result member when mapping from and to the source and /// result types being configured. The factory expression is passed the source element being projected. From 10c99219a1739e8cc87f10e44657a672d57143b2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 11:32:55 +0000 Subject: [PATCH 127/176] Support for ignoring target members by type in projections / Extending ignore member tests to EF5, EF6 + Ef Core 1 --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../Configuration/WhenIgnoringMembers.cs | 29 +++++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Configuration/WhenIgnoringMembers.cs | 29 +++++++++++++++ .../Configuration/WhenIgnoringMembers.cs | 7 ++++ .../Configuration/WhenIgnoringMembers.cs | 3 ++ .../Configuration/WhenIgnoringMembers.cs | 37 +++++++++++++++++++ .../Configuration/IRootMappingConfigurator.cs | 2 +- .../Api/Configuration/MappingConfigurator.cs | 7 ++-- .../Projection/IRootProjectionConfigurator.cs | 11 ++++++ 10 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 55acf90bd..60548ca0c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -86,6 +86,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs new file mode 100644 index 000000000..00a2f57d9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenIgnoringMembers : WhenIgnoringMembers + { + public WhenIgnoringMembers(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); + + [Fact] + public Task ShouldIgnoreAConfiguredMemberConditionally() => + DoShouldIgnoreAConfiguredMemberConditionally(); + + [Fact] + public Task ShouldIgnorePropertiesByPropertyInfoMatcher() + => DoShouldIgnorePropertiesByPropertyInfoMatcher(); + + [Fact] + public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index b7d18b6ad..48adbc4d0 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -88,6 +88,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs new file mode 100644 index 000000000..f6c1f1e8f --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenIgnoringMembers : WhenIgnoringMembers + { + public WhenIgnoringMembers(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); + + [Fact] + public Task ShouldIgnoreAConfiguredMemberConditionally() => + DoShouldIgnoreAConfiguredMemberConditionally(); + + [Fact] + public Task ShouldIgnorePropertiesByPropertyInfoMatcher() + => DoShouldIgnorePropertiesByPropertyInfoMatcher(); + + [Fact] + public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs index 5b74fce81..a01f9b5ae 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs @@ -18,5 +18,12 @@ public WhenIgnoringMembers(InMemoryEfCore1TestContext context) [Fact] public Task ShouldIgnoreAConfiguredMemberConditionally() => DoShouldIgnoreAConfiguredMemberConditionally(); + + [Fact] + public Task ShouldIgnorePropertiesByPropertyInfoMatcher() + => DoShouldIgnorePropertiesByPropertyInfoMatcher(); + + [Fact] + public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs index 3900131a6..573f5031e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs @@ -22,5 +22,8 @@ public Task ShouldIgnoreAConfiguredMemberConditionally() => [Fact] public Task ShouldIgnorePropertiesByPropertyInfoMatcher() => DoShouldIgnorePropertiesByPropertyInfoMatcher(); + + [Fact] + public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index 875319825..c6f6984af 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -73,6 +73,43 @@ protected Task DoShouldIgnoreAConfiguredMemberConditionally() }); } + protected Task DoShouldIgnoreMembersByTypeAndTargetType() + { + return RunTest(async context => + { + var person = new Person + { + Name = "Mac", + Address = new Address { Line1 = "1", Line2 = "2", Postcode = "3" } + }; + + context.Persons.Add(person); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From
() + .ProjectedTo() + .IgnoreTargetMembersOfType(); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Mac"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Id.ShouldNotBe(person.Address.AddressId); + personDto.Address.Id.ShouldBeDefault(); + personDto.Address.Line1.ShouldBe("1"); + personDto.Address.Line2.ShouldBe("2"); + personDto.Address.Postcode.ShouldBe("3"); + } + }); + } + protected Task DoShouldIgnorePropertiesByPropertyInfoMatcher() { return RunTest(async context => diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index bbae8717b..fa321fbd1 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -81,7 +81,7 @@ IMappingConfigContinuation CreateInstancesUsing( /// The Type of target member to ignore. /// /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. + /// target types being configured. /// IMappingConfigContinuation IgnoreTargetMembersOfType(); diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 076cbaf09..be83ff591 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -176,9 +176,10 @@ public EnumPairSpecifier PairEnum(TFir #region Ignoring Members public IMappingConfigContinuation IgnoreTargetMembersOfType() - { - return IgnoreTargetMembersWhere(member => member.HasType()); - } + => IgnoreMembersByFilter(member => member.HasType()); + + IProjectionConfigContinuation IRootProjectionConfigurator.IgnoreTargetMembersOfType() + => IgnoreMembersByFilter(member => member.HasType()); public IMappingConfigContinuation IgnoreTargetMembersWhere( Expression> memberFilter) diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index e7de313f4..94a9129b0 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -23,6 +23,17 @@ public interface IRootProjectionConfigurator IProjectionConfigContinuation Ignore( params Expression>[] resultMembers); + /// + /// Ignore all result member(s) of the given Type when projecting + /// from and to the source and result types being configured. + /// + /// The Type of result member to ignore. + /// + /// An IProjectionConfigContinuation to enable further configuration of projections from and to the source and + /// result types being configured. + /// + IProjectionConfigContinuation IgnoreTargetMembersOfType(); + /// /// Ignore all result member(s) matching the given when projecting /// from and to the source and result types being configured. From a109a092bd7af3bf435cff93bbbc3d323181e8d1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 13:58:06 +0000 Subject: [PATCH 128/176] Refactoring member population expression generation into ruleset-specific class, replacing population guarding strategies --- AgileMapper/MappingRuleSet.cs | 6 +- AgileMapper/MappingRuleSetCollection.cs | 8 +- .../Members/IChildMemberMappingData.cs | 7 - .../Members/MemberMapperDataExtensions.cs | 2 +- .../DefaultMemberPopulationFactory.cs | 36 ++++ .../Population/IMemberPopulationContext.cs | 16 ++ .../Population/IMemberPopulationFactory.cs | 9 + .../IMemberPopulationGuardFactory.cs | 10 - ...emberPopulation.cs => IMemberPopulator.cs} | 4 +- .../MemberMergePopulationFactory.cs | 54 ++++++ .../Members/Population/MemberPopulation.cs | 173 ------------------ .../Population/MemberPopulationFactoryBase.cs | 64 +++++++ .../Members/Population/MemberPopulator.cs | 102 +++++++++++ .../Population/MemberPopulatorFactory.cs} | 24 +-- .../NullMemberPopulationGuardFactory.cs | 12 -- ...istingValueMemberPopulationGuardFactory.cs | 42 ----- .../ComplexTypeMappingExpressionFactory.cs | 2 +- .../MemberInitPopulationExpressionFactory.cs | 4 +- ...ltiStatementPopulationExpressionFactory.cs | 12 +- .../PopulationExpressionFactoryBase.cs | 10 +- .../DictionaryMappingExpressionFactory.cs | 7 +- .../DictionaryPopulationBuilder.cs | 2 +- ...ExistingOrDefaultValueDataSourceFactory.cs | 2 +- 23 files changed, 323 insertions(+), 285 deletions(-) create mode 100644 AgileMapper/Members/Population/DefaultMemberPopulationFactory.cs create mode 100644 AgileMapper/Members/Population/IMemberPopulationContext.cs create mode 100644 AgileMapper/Members/Population/IMemberPopulationFactory.cs delete mode 100644 AgileMapper/Members/Population/IMemberPopulationGuardFactory.cs rename AgileMapper/Members/Population/{IMemberPopulation.cs => IMemberPopulator.cs} (71%) create mode 100644 AgileMapper/Members/Population/MemberMergePopulationFactory.cs delete mode 100644 AgileMapper/Members/Population/MemberPopulation.cs create mode 100644 AgileMapper/Members/Population/MemberPopulationFactoryBase.cs create mode 100644 AgileMapper/Members/Population/MemberPopulator.cs rename AgileMapper/{ObjectPopulation/MemberPopulationFactory.cs => Members/Population/MemberPopulatorFactory.cs} (72%) delete mode 100644 AgileMapper/Members/Population/NullMemberPopulationGuardFactory.cs delete mode 100644 AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs diff --git a/AgileMapper/MappingRuleSet.cs b/AgileMapper/MappingRuleSet.cs index de03e7d11..fdcdfdb77 100644 --- a/AgileMapper/MappingRuleSet.cs +++ b/AgileMapper/MappingRuleSet.cs @@ -12,14 +12,14 @@ public MappingRuleSet( MappingRuleSetSettings settings, IEnumerablePopulationStrategy enumerablePopulationStrategy, IRecursiveMemberMappingStrategy recursiveMemberMappingStrategy, - IMemberPopulationGuardFactory populationGuardFactory, + IMemberPopulationFactory populationFactory, IDataSourceFactory fallbackDataSourceFactory) { Name = name; Settings = settings; EnumerablePopulationStrategy = enumerablePopulationStrategy; RecursiveMemberMappingStrategy = recursiveMemberMappingStrategy; - PopulationGuardFactory = populationGuardFactory; + PopulationFactory = populationFactory; FallbackDataSourceFactory = fallbackDataSourceFactory; } @@ -31,7 +31,7 @@ public MappingRuleSet( public IRecursiveMemberMappingStrategy RecursiveMemberMappingStrategy { get; } - public IMemberPopulationGuardFactory PopulationGuardFactory { get; } + public IMemberPopulationFactory PopulationFactory { get; } public IDataSourceFactory FallbackDataSourceFactory { get; } } diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index dcf01f3c0..e1b4b6b0e 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -23,7 +23,7 @@ internal class MappingRuleSetCollection }, new CopySourceEnumerablePopulationStrategy(), MapRecursionCallRecursiveMemberMappingStrategy.Instance, - NullMemberPopulationGuardFactory.Instance, + DefaultMemberPopulationFactory.Instance, ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _merge = new MappingRuleSet( @@ -38,7 +38,7 @@ internal class MappingRuleSetCollection }, new MergeEnumerablePopulationStrategy(), MapRecursionCallRecursiveMemberMappingStrategy.Instance, - new PreserveExistingValueMemberPopulationGuardFactory(), + new MemberMergePopulationFactory(), ExistingOrDefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _overwrite = new MappingRuleSet( @@ -53,7 +53,7 @@ internal class MappingRuleSetCollection }, OverwriteEnumerablePopulationStrategy.Instance, MapRecursionCallRecursiveMemberMappingStrategy.Instance, - NullMemberPopulationGuardFactory.Instance, + DefaultMemberPopulationFactory.Instance, DefaultValueDataSourceFactory.Instance); private static readonly MappingRuleSet _project = new MappingRuleSet( @@ -67,7 +67,7 @@ internal class MappingRuleSetCollection }, new ProjectSourceEnumerablePopulationStrategy(), new MapToDepthRecursiveMemberMappingStrategy(), - NullMemberPopulationGuardFactory.Instance, + DefaultMemberPopulationFactory.Instance, DefaultValueDataSourceFactory.Instance); #endregion diff --git a/AgileMapper/Members/IChildMemberMappingData.cs b/AgileMapper/Members/IChildMemberMappingData.cs index 51b6c6fb7..9c4d0b113 100644 --- a/AgileMapper/Members/IChildMemberMappingData.cs +++ b/AgileMapper/Members/IChildMemberMappingData.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.Members { using System; - using System.Linq.Expressions; using ObjectPopulation; internal interface IChildMemberMappingData @@ -14,10 +13,4 @@ internal interface IChildMemberMappingData Type GetSourceMemberRuntimeType(IQualifiedMember sourceMember); } - - internal static class ChildMemberMappingDataExtensions - { - public static Expression GetRuleSetPopulationGuardOrNull(this IChildMemberMappingData childMappingData) - => childMappingData.RuleSet.PopulationGuardFactory.GetPopulationGuardOrNull(childMappingData.MapperData); - } } \ No newline at end of file diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index 1df284114..fe8506180 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -40,7 +40,7 @@ public static bool IsEntity(this IMemberMapperData mapperData, Type type, out Me public static bool UseSingleMappingExpression(this IBasicMapperData mapperData) => mapperData.IsRoot && mapperData.RuleSet.Settings.UseSingleRootMappingExpression; - public static bool UseMemberInitialisation(this IMemberMapperData mapperData) + public static bool UseMemberInitialisations(this IMemberMapperData mapperData) => mapperData.RuleSet.Settings.UseMemberInitialisation || mapperData.Context.IsPartOfUserStructMapping(); public static bool MapToNullCollections(this IMemberMapperData mapperData) diff --git a/AgileMapper/Members/Population/DefaultMemberPopulationFactory.cs b/AgileMapper/Members/Population/DefaultMemberPopulationFactory.cs new file mode 100644 index 000000000..1c13a98fc --- /dev/null +++ b/AgileMapper/Members/Population/DefaultMemberPopulationFactory.cs @@ -0,0 +1,36 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System.Linq.Expressions; + using Extensions.Internal; + + internal class DefaultMemberPopulationFactory : MemberPopulationFactoryBase + { + public static readonly IMemberPopulationFactory Instance = new DefaultMemberPopulationFactory(); + + protected override Expression GetPopulationGuard(IMemberPopulationContext context) + => context.PopulateCondition; + + protected override Expression GetGuardedBindingValue(Expression bindingValue, Expression populationGuard) + { + if (populationGuard == null) + { + return bindingValue; + } + + return Expression.Condition( + populationGuard, + bindingValue, + bindingValue.Type.ToDefaultExpression()); + } + + public override Expression GetGuardedPopulation( + Expression population, + Expression populationGuard, + bool useSingleExpression) + { + return useSingleExpression + ? population + : base.GetGuardedPopulation(population, populationGuard, false); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Population/IMemberPopulationContext.cs b/AgileMapper/Members/Population/IMemberPopulationContext.cs new file mode 100644 index 000000000..da75c487f --- /dev/null +++ b/AgileMapper/Members/Population/IMemberPopulationContext.cs @@ -0,0 +1,16 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System.Linq.Expressions; + using DataSources; + + internal interface IMemberPopulationContext + { + IMemberMapperData MapperData { get; } + + bool IsSuccessful { get; } + + DataSourceSet DataSources { get; } + + Expression PopulateCondition { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Population/IMemberPopulationFactory.cs b/AgileMapper/Members/Population/IMemberPopulationFactory.cs new file mode 100644 index 000000000..bec1b445e --- /dev/null +++ b/AgileMapper/Members/Population/IMemberPopulationFactory.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System.Linq.Expressions; + + internal interface IMemberPopulationFactory + { + Expression GetPopulation(IMemberPopulationContext context); + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Population/IMemberPopulationGuardFactory.cs b/AgileMapper/Members/Population/IMemberPopulationGuardFactory.cs deleted file mode 100644 index 3a94a10e1..000000000 --- a/AgileMapper/Members/Population/IMemberPopulationGuardFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace AgileObjects.AgileMapper.Members.Population -{ - using System.Linq.Expressions; - using Members; - - internal interface IMemberPopulationGuardFactory - { - Expression GetPopulationGuardOrNull(IMemberMapperData mapperData); - } -} \ No newline at end of file diff --git a/AgileMapper/Members/Population/IMemberPopulation.cs b/AgileMapper/Members/Population/IMemberPopulator.cs similarity index 71% rename from AgileMapper/Members/Population/IMemberPopulation.cs rename to AgileMapper/Members/Population/IMemberPopulator.cs index a5b2d6015..3dd455d80 100644 --- a/AgileMapper/Members/Population/IMemberPopulation.cs +++ b/AgileMapper/Members/Population/IMemberPopulator.cs @@ -2,11 +2,11 @@ namespace AgileObjects.AgileMapper.Members.Population { using System.Linq.Expressions; - internal interface IMemberPopulation + internal interface IMemberPopulator { IMemberMapperData MapperData { get; } - bool IsSuccessful { get; } + bool CanPopulate { get; } Expression GetPopulation(); } diff --git a/AgileMapper/Members/Population/MemberMergePopulationFactory.cs b/AgileMapper/Members/Population/MemberMergePopulationFactory.cs new file mode 100644 index 000000000..c349a7059 --- /dev/null +++ b/AgileMapper/Members/Population/MemberMergePopulationFactory.cs @@ -0,0 +1,54 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System.Linq.Expressions; + + internal class MemberMergePopulationFactory : MemberPopulationFactoryBase + { + protected override Expression GetPopulationGuard(IMemberPopulationContext context) + { + var mapperData = context.MapperData; + var populateCondition = context.PopulateCondition; + + if (SkipPopulationGuarding(mapperData)) + { + return populateCondition; + } + + var existingValueIsDefault = mapperData.TargetMember.GetHasDefaultValueCheck(mapperData); + + if (populateCondition == null) + { + return existingValueIsDefault; + } + + return Expression.AndAlso(populateCondition, existingValueIsDefault); + } + + private static bool SkipPopulationGuarding(IBasicMapperData mapperData) + { + var targetMember = mapperData.TargetMember; + + if (!targetMember.IsReadable) + { + return true; + } + + if (targetMember.IsSimple) + { + return false; + } + + if (targetMember.Type != typeof(object)) + { + return true; + } + + var skipObjectValueGuarding = !targetMember.GuardObjectValuePopulations; + + return skipObjectValueGuarding; + } + + protected override Expression GetGuardedBindingValue(Expression bindingValue, Expression populationGuard) + => bindingValue; + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Population/MemberPopulation.cs b/AgileMapper/Members/Population/MemberPopulation.cs deleted file mode 100644 index fb96d0ec0..000000000 --- a/AgileMapper/Members/Population/MemberPopulation.cs +++ /dev/null @@ -1,173 +0,0 @@ -namespace AgileObjects.AgileMapper.Members.Population -{ - using System; - using System.Linq; - using System.Linq.Expressions; - using Configuration; - using DataSources; - using Extensions.Internal; - using ReadableExpressions; - - internal class MemberPopulation : IMemberPopulation - { - private readonly DataSourceSet _dataSources; - private readonly Expression _populateCondition; - - private MemberPopulation(DataSourceSet dataSources, Expression populateCondition = null) - { - _dataSources = dataSources; - _populateCondition = populateCondition; - } - - #region Factory Methods - - public static IMemberPopulation WithRegistration( - IChildMemberMappingData mappingData, - DataSourceSet dataSources, - Expression populateCondition) - { - var memberPopulation = WithoutRegistration(mappingData, dataSources, populateCondition); - - memberPopulation.MapperData.RegisterTargetMemberDataSourcesIfRequired(dataSources); - - return memberPopulation; - } - - public static IMemberPopulation WithoutRegistration( - IChildMemberMappingData mappingData, - DataSourceSet dataSources, - Expression populateCondition = null) - { - if (!dataSources.None) - { - populateCondition = GetPopulateCondition(populateCondition, mappingData); - } - - return new MemberPopulation(dataSources, populateCondition); - } - - private static Expression GetPopulateCondition(Expression populateCondition, IChildMemberMappingData mappingData) - { - var populationGuard = mappingData.GetRuleSetPopulationGuardOrNull(); - - if (populationGuard == null) - { - return populateCondition; - } - - if (populateCondition == null) - { - return populationGuard; - } - - return Expression.AndAlso(populateCondition, populationGuard); - } - - public static IMemberPopulation Unmappable(IMemberMapperData mapperData, string reason) - => CreateNullMemberPopulation(mapperData, targetMember => $"No way to populate {targetMember.Name} ({reason})"); - - public static IMemberPopulation IgnoredMember(IMemberMapperData mapperData, ConfiguredIgnoredMember configuredIgnore) - => CreateNullMemberPopulation(mapperData, configuredIgnore.GetIgnoreMessage); - - public static IMemberPopulation NoDataSource(IMemberMapperData mapperData) - { - var noDataSources = CreateNullDataSourceSet(mapperData, GetNoDataSourceMessage); - - mapperData.RegisterTargetMemberDataSourcesIfRequired(noDataSources); - - return new MemberPopulation(noDataSources); - } - - private static string GetNoDataSourceMessage(QualifiedMember targetMember) - { - return targetMember.IsSimple - ? "No data source for " + targetMember.Name - : $"No data source for {targetMember.Name} or any of its child members"; - } - - private static MemberPopulation CreateNullMemberPopulation( - IMemberMapperData mapperData, - Func commentFactory) - { - return new MemberPopulation(CreateNullDataSourceSet(mapperData, commentFactory)); - } - - private static DataSourceSet CreateNullDataSourceSet( - IMemberMapperData mapperData, - Func commentFactory) - { - return new DataSourceSet( - mapperData, - new NullDataSource( - ReadableExpression.Comment(commentFactory.Invoke(mapperData.TargetMember)))); - } - - #endregion - - public IMemberMapperData MapperData => _dataSources.MapperData; - - public bool IsSuccessful => _dataSources.HasValue; - - public Expression GetPopulation() - { - if (!IsSuccessful) - { - return _dataSources.GetValueExpression(); - } - - var useSingleExpression = MapperData.UseMemberInitialisation(); - - var population = useSingleExpression - ? GetBinding() - : MapperData.TargetMember.IsReadOnly - ? GetReadOnlyMemberPopulation() - : _dataSources.GetPopulationExpression(); - - if (_dataSources.Variables.Any()) - { - population = Expression.Block(_dataSources.Variables, population); - } - - if (!useSingleExpression && (_populateCondition != null)) - { - population = Expression.IfThen(_populateCondition, population); - } - - return population; - } - - private Expression GetBinding() - { - var bindingValue = _dataSources.GetValueExpression(); - - if (_populateCondition != null) - { - bindingValue = Expression.Condition( - _populateCondition, - bindingValue, - bindingValue.Type.ToDefaultExpression()); - } - - var binding = MapperData.GetTargetMemberPopulation(bindingValue); - - return binding; - } - - private Expression GetReadOnlyMemberPopulation() - { - var dataSourcesValue = _dataSources.GetValueExpression(); - var targetMemberAccess = MapperData.GetTargetMemberAccess(); - var targetMemberNotNull = targetMemberAccess.GetIsNotDefaultComparison(); - - return Expression.IfThen(targetMemberNotNull, dataSourcesValue); - } - - #region ExcludeFromCodeCoverage -#if DEBUG - [ExcludeFromCodeCoverage] -#endif - #endregion - public override string ToString() - => $"{MapperData.TargetMember} ({_dataSources.Count()} data source(s))"; - } -} \ No newline at end of file diff --git a/AgileMapper/Members/Population/MemberPopulationFactoryBase.cs b/AgileMapper/Members/Population/MemberPopulationFactoryBase.cs new file mode 100644 index 000000000..a92dab789 --- /dev/null +++ b/AgileMapper/Members/Population/MemberPopulationFactoryBase.cs @@ -0,0 +1,64 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System.Linq.Expressions; + using Extensions.Internal; + + internal abstract class MemberPopulationFactoryBase : IMemberPopulationFactory + { + public Expression GetPopulation(IMemberPopulationContext context) + { + if (!context.IsSuccessful) + { + return context.DataSources.GetValueExpression(); + } + + var useSingleExpression = context.MapperData.UseMemberInitialisations(); + var populationGuard = GetPopulationGuard(context); + + var population = useSingleExpression + ? GetBinding(context, populationGuard) + : context.MapperData.TargetMember.IsReadOnly + ? GetReadOnlyMemberPopulation(context) + : context.DataSources.GetPopulationExpression(); + + if (context.DataSources.Variables.Any()) + { + population = Expression.Block(context.DataSources.Variables, population); + } + + return GetGuardedPopulation(population, populationGuard, useSingleExpression); + } + + protected abstract Expression GetPopulationGuard(IMemberPopulationContext context); + + private Expression GetBinding(IMemberPopulationContext context, Expression populationGuard) + { + var bindingValue = context.DataSources.GetValueExpression(); + var guardedBindingValue = GetGuardedBindingValue(bindingValue, populationGuard); + var binding = context.MapperData.GetTargetMemberPopulation(guardedBindingValue); + + return binding; + } + + protected abstract Expression GetGuardedBindingValue(Expression bindingValue, Expression populationGuard); + + private static Expression GetReadOnlyMemberPopulation(IMemberPopulationContext context) + { + var dataSourcesValue = context.DataSources.GetValueExpression(); + var targetMemberAccess = context.MapperData.GetTargetMemberAccess(); + var targetMemberNotNull = targetMemberAccess.GetIsNotDefaultComparison(); + + return Expression.IfThen(targetMemberNotNull, dataSourcesValue); + } + + public virtual Expression GetGuardedPopulation( + Expression population, + Expression populationGuard, + bool useSingleExpression) + { + return (populationGuard != null) + ? Expression.IfThen(populationGuard, population) + : population; + } + } +} \ No newline at end of file diff --git a/AgileMapper/Members/Population/MemberPopulator.cs b/AgileMapper/Members/Population/MemberPopulator.cs new file mode 100644 index 000000000..4ef9a06a1 --- /dev/null +++ b/AgileMapper/Members/Population/MemberPopulator.cs @@ -0,0 +1,102 @@ +namespace AgileObjects.AgileMapper.Members.Population +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using Configuration; + using DataSources; + using ReadableExpressions; + + internal class MemberPopulator : IMemberPopulationContext, IMemberPopulator + { + private MemberPopulator(DataSourceSet dataSources, Expression populateCondition = null) + { + DataSources = dataSources; + PopulateCondition = populateCondition; + } + + #region Factory Methods + + public static IMemberPopulator WithRegistration( + IChildMemberMappingData mappingData, + DataSourceSet dataSources, + Expression populateCondition) + { + var memberPopulation = WithoutRegistration(mappingData, dataSources, populateCondition); + + memberPopulation.MapperData.RegisterTargetMemberDataSourcesIfRequired(dataSources); + + return memberPopulation; + } + + public static IMemberPopulator WithoutRegistration( + IChildMemberMappingData mappingData, + DataSourceSet dataSources, + Expression populateCondition = null) + { + return new MemberPopulator(dataSources, populateCondition); + } + + public static IMemberPopulator Unmappable(IMemberMapperData mapperData, string reason) + => CreateNullMemberPopulation(mapperData, targetMember => $"No way to populate {targetMember.Name} ({reason})"); + + public static IMemberPopulator IgnoredMember(IMemberMapperData mapperData, ConfiguredIgnoredMember configuredIgnore) + => CreateNullMemberPopulation(mapperData, configuredIgnore.GetIgnoreMessage); + + public static IMemberPopulator NoDataSource(IMemberMapperData mapperData) + { + var noDataSources = CreateNullDataSourceSet(mapperData, GetNoDataSourceMessage); + + mapperData.RegisterTargetMemberDataSourcesIfRequired(noDataSources); + + return new MemberPopulator(noDataSources); + } + + private static string GetNoDataSourceMessage(QualifiedMember targetMember) + { + return targetMember.IsSimple + ? "No data source for " + targetMember.Name + : $"No data source for {targetMember.Name} or any of its child members"; + } + + private static MemberPopulator CreateNullMemberPopulation( + IMemberMapperData mapperData, + Func commentFactory) + { + return new MemberPopulator(CreateNullDataSourceSet(mapperData, commentFactory)); + } + + private static DataSourceSet CreateNullDataSourceSet( + IMemberMapperData mapperData, + Func commentFactory) + { + return new DataSourceSet( + mapperData, + new NullDataSource( + ReadableExpression.Comment(commentFactory.Invoke(mapperData.TargetMember)))); + } + + #endregion + + public IMemberMapperData MapperData => DataSources.MapperData; + + public bool IsSuccessful => CanPopulate; + + public bool CanPopulate => DataSources.HasValue; + + public DataSourceSet DataSources { get; } + + public Expression PopulateCondition { get; } + + public Expression GetPopulation() + => MapperData.RuleSet.PopulationFactory.GetPopulation(this); + + #region ExcludeFromCodeCoverage +#if DEBUG + [ExcludeFromCodeCoverage] +#endif + #endregion + public override string ToString() + => $"{MapperData.TargetMember} ({DataSources.Count()} data source(s))"; + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs b/AgileMapper/Members/Population/MemberPopulatorFactory.cs similarity index 72% rename from AgileMapper/ObjectPopulation/MemberPopulationFactory.cs rename to AgileMapper/Members/Population/MemberPopulatorFactory.cs index 0b4162af8..be52511de 100644 --- a/AgileMapper/ObjectPopulation/MemberPopulationFactory.cs +++ b/AgileMapper/Members/Population/MemberPopulatorFactory.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.ObjectPopulation +namespace AgileObjects.AgileMapper.Members.Population { using System; using System.Collections.Generic; @@ -8,11 +8,11 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using DataSources; using Extensions.Internal; using Members; - using Members.Population; + using ObjectPopulation; - internal class MemberPopulationFactory + internal class MemberPopulatorFactory { - public static readonly MemberPopulationFactory Default = new MemberPopulationFactory(mapperData => + public static readonly MemberPopulatorFactory Default = new MemberPopulatorFactory(mapperData => GlobalContext.Instance .MemberCache .GetTargetMembers(mapperData.TargetType) @@ -20,12 +20,12 @@ internal class MemberPopulationFactory private readonly Func> _targetMembersFactory; - public MemberPopulationFactory(Func> targetMembersFactory) + public MemberPopulatorFactory(Func> targetMembersFactory) { _targetMembersFactory = targetMembersFactory; } - public IEnumerable Create(IObjectMappingData mappingData) + public IEnumerable Create(IObjectMappingData mappingData) { return _targetMembersFactory .Invoke(mappingData.MapperData) @@ -33,7 +33,7 @@ public IEnumerable Create(IObjectMappingData mappingData) { var memberPopulation = Create(tm, mappingData); - if (memberPopulation.IsSuccessful || + if (memberPopulation.CanPopulate || mappingData.MappingContext.AddUnsuccessfulMemberPopulations) { return memberPopulation; @@ -44,13 +44,13 @@ public IEnumerable Create(IObjectMappingData mappingData) .WhereNotNull(); } - private static IMemberPopulation Create(QualifiedMember targetMember, IObjectMappingData mappingData) + private static IMemberPopulator Create(QualifiedMember targetMember, IObjectMappingData mappingData) { var childMapperData = new ChildMemberMapperData(targetMember, mappingData.MapperData); if (childMapperData.TargetMemberIsUnmappable(out var reason)) { - return MemberPopulation.Unmappable(childMapperData, reason); + return MemberPopulator.Unmappable(childMapperData, reason); } if (TargetMemberIsUnconditionallyIgnored( @@ -58,7 +58,7 @@ private static IMemberPopulation Create(QualifiedMember targetMember, IObjectMap out var configuredIgnore, out var populateCondition)) { - return MemberPopulation.IgnoredMember(childMapperData, configuredIgnore); + return MemberPopulator.IgnoredMember(childMapperData, configuredIgnore); } var childMappingData = mappingData.GetChildMappingData(childMapperData); @@ -66,10 +66,10 @@ private static IMemberPopulation Create(QualifiedMember targetMember, IObjectMap if (dataSources.None) { - return MemberPopulation.NoDataSource(childMapperData); + return MemberPopulator.NoDataSource(childMapperData); } - return MemberPopulation.WithRegistration(childMappingData, dataSources, populateCondition); + return MemberPopulator.WithRegistration(childMappingData, dataSources, populateCondition); } private static bool TargetMemberIsUnconditionallyIgnored( diff --git a/AgileMapper/Members/Population/NullMemberPopulationGuardFactory.cs b/AgileMapper/Members/Population/NullMemberPopulationGuardFactory.cs deleted file mode 100644 index cefd003e6..000000000 --- a/AgileMapper/Members/Population/NullMemberPopulationGuardFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AgileObjects.AgileMapper.Members.Population -{ - using System.Linq.Expressions; - using Members; - - internal class NullMemberPopulationGuardFactory : IMemberPopulationGuardFactory - { - public static readonly IMemberPopulationGuardFactory Instance = new NullMemberPopulationGuardFactory(); - - public Expression GetPopulationGuardOrNull(IMemberMapperData mapperData) => null; - } -} \ No newline at end of file diff --git a/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs b/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs deleted file mode 100644 index fb1e9dc31..000000000 --- a/AgileMapper/Members/Population/PreserveExistingValueMemberPopulationGuardFactory.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace AgileObjects.AgileMapper.Members.Population -{ - using System.Linq.Expressions; - using Members; - - internal class PreserveExistingValueMemberPopulationGuardFactory : IMemberPopulationGuardFactory - { - public Expression GetPopulationGuardOrNull(IMemberMapperData mapperData) - { - if (SkipPopulateCondition(mapperData)) - { - return null; - } - - var existingValueIsDefault = mapperData.TargetMember.GetHasDefaultValueCheck(mapperData); - - return existingValueIsDefault; - } - - private static bool SkipPopulateCondition(IBasicMapperData mapperData) - { - if (!mapperData.TargetMember.IsReadable) - { - return true; - } - - if (mapperData.TargetMember.IsSimple) - { - return false; - } - - if (mapperData.TargetMember.Type != typeof(object)) - { - return true; - } - - var skipObjectValueGuarding = !mapperData.TargetMember.GuardObjectValuePopulations; - - return skipObjectValueGuarding; - } - } -} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 76fc58c2a..522fe6532 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -136,7 +136,7 @@ protected override Expression GetDerivedTypeMappings(IObjectMappingData mappingD protected override IEnumerable GetObjectPopulation(IObjectMappingData mappingData) { - var expressionFactory = mappingData.MapperData.UseMemberInitialisation() + var expressionFactory = mappingData.MapperData.UseMemberInitialisations() ? _memberInitPopulationFactory : _multiStatementPopulationFactory; diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs index 11a678208..461dfef8f 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs @@ -8,10 +8,10 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes internal class MemberInitPopulationExpressionFactory : PopulationExpressionFactoryBase { protected override IEnumerable GetPopulationExpressionsFor( - IMemberPopulation memberPopulation, + IMemberPopulator memberPopulator, IObjectMappingData mappingData) { - yield return memberPopulation.GetPopulation(); + yield return memberPopulator.GetPopulation(); } protected override Expression GetNewObjectCreation(IObjectMappingData mappingData, IList memberPopulations) diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs index 64d259fdd..fe368a0a8 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MultiStatementPopulationExpressionFactory.cs @@ -9,19 +9,19 @@ namespace AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes internal class MultiStatementPopulationExpressionFactory : PopulationExpressionFactoryBase { protected override IEnumerable GetPopulationExpressionsFor( - IMemberPopulation memberPopulation, + IMemberPopulator memberPopulator, IObjectMappingData mappingData) { - var prePopulationCallback = GetPopulationCallbackOrNull(Before, memberPopulation, mappingData); + var prePopulationCallback = GetPopulationCallbackOrNull(Before, memberPopulator, mappingData); if (prePopulationCallback != null) { yield return prePopulationCallback; } - yield return memberPopulation.GetPopulation(); + yield return memberPopulator.GetPopulation(); - var postPopulationCallback = GetPopulationCallbackOrNull(After, memberPopulation, mappingData); + var postPopulationCallback = GetPopulationCallbackOrNull(After, memberPopulator, mappingData); if (postPopulationCallback != null) { @@ -31,10 +31,10 @@ protected override IEnumerable GetPopulationExpressionsFor( private static Expression GetPopulationCallbackOrNull( CallbackPosition position, - IMemberPopulation memberPopulation, + IMemberPopulator memberPopulator, IObjectMappingData mappingData) { - return memberPopulation.MapperData.GetMappingCallbackOrNull(position, mappingData.MapperData); + return memberPopulator.MapperData.GetMappingCallbackOrNull(position, mappingData.MapperData); } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs index 27151d9b6..657dd897f 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs @@ -44,15 +44,15 @@ private static Expression GetCreationCallbackOrNull(CallbackPosition callbackPos private IEnumerable GetPopulationsAndCallbacks(IObjectMappingData mappingData) { - foreach (var memberPopulation in MemberPopulationFactory.Default.Create(mappingData)) + foreach (var memberPopulator in MemberPopulatorFactory.Default.Create(mappingData)) { - if (!memberPopulation.IsSuccessful) + if (!memberPopulator.CanPopulate) { - yield return memberPopulation.GetPopulation(); + yield return memberPopulator.GetPopulation(); continue; } - foreach (var expression in GetPopulationExpressionsFor(memberPopulation, mappingData)) + foreach (var expression in GetPopulationExpressionsFor(memberPopulator, mappingData)) { yield return expression; } @@ -60,7 +60,7 @@ private IEnumerable GetPopulationsAndCallbacks(IObjectMappingData ma } protected abstract IEnumerable GetPopulationExpressionsFor( - IMemberPopulation memberPopulation, + IMemberPopulator memberPopulator, IObjectMappingData mappingData); private Expression GetLocalVariableInstantiation( diff --git a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs index 5c326165f..a7558292d 100644 --- a/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/DictionaryMappingExpressionFactory.cs @@ -11,6 +11,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using Members; using Members.Dictionaries; + using Members.Population; using NetStandardPolyfills; using ReadableExpressions; @@ -18,11 +19,11 @@ internal class DictionaryMappingExpressionFactory : MappingExpressionFactoryBase { public static readonly MappingExpressionFactoryBase Instance = new DictionaryMappingExpressionFactory(); - private readonly MemberPopulationFactory _memberPopulationFactory; + private readonly MemberPopulatorFactory _memberPopulatorFactory; private DictionaryMappingExpressionFactory() { - _memberPopulationFactory = new MemberPopulationFactory(GetAllTargetMembers); + _memberPopulatorFactory = new MemberPopulatorFactory(GetAllTargetMembers); } #region Target Member Generation @@ -457,7 +458,7 @@ private Expression GetDictionaryPopulation(IObjectMappingData mappingData) return GetEnumerableToDictionaryMapping(mappingData); } - var memberPopulations = _memberPopulationFactory + var memberPopulations = _memberPopulatorFactory .Create(mappingData) .Select(memberPopulation => memberPopulation.GetPopulation()) .ToArray(); diff --git a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs index f5838d2f0..ceb47013a 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/Dictionaries/DictionaryPopulationBuilder.cs @@ -223,7 +223,7 @@ private Expression GetPopulation( var mappingDataSource = new AdHocDataSource(sourceMember, elementMapping); var mappingDataSources = new DataSourceSet(elementMapperData, mappingDataSource); - var memberPopulation = MemberPopulation.WithoutRegistration(elementMappingData, mappingDataSources); + var memberPopulation = MemberPopulator.WithoutRegistration(elementMappingData, mappingDataSources); var populationExpression = memberPopulation.GetPopulation(); return populationExpression; diff --git a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs index 382c34717..f9e3b90c3 100644 --- a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs +++ b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs @@ -29,7 +29,7 @@ private static Expression GetValue(IMemberMapperData mapperData) : mapperData.GetFallbackCollectionValue(); } - if (mapperData.TargetMember.IsReadable && !mapperData.UseMemberInitialisation()) + if (mapperData.TargetMember.IsReadable && !mapperData.UseMemberInitialisations()) { return mapperData.GetTargetMemberAccess(); } From 40667d9a95c8de3c41c35208652a61c4de1b4e2d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 18:38:19 +0000 Subject: [PATCH 129/176] Filtering get- and set-method use out of query projections --- .../WhenProjectingToFlatTypes.cs | 6 +++++ .../WhenProjectingToFlatTypes.cs | 6 +++++ .../WhenProjectingToFlatTypes.cs | 6 +++++ .../WhenProjectingToFlatTypes.cs | 6 +++++ .../TestClasses/Person.cs | 2 ++ .../TestClasses/PersonViewModel.cs | 4 +++ .../WhenProjectingToFlatTypes.cs | 3 +-- AgileMapper/Api/PlanTargetSelector.cs | 2 +- AgileMapper/MappingRuleSetCollection.cs | 12 ++++++--- AgileMapper/MappingRuleSetSettings.cs | 5 ++++ .../Members/MemberMapperDataExtensions.cs | 7 +++++ AgileMapper/Members/MemberTypeExtensions.cs | 12 +++++++++ AgileMapper/Members/SourceMemberMatcher.cs | 27 ++++++++++--------- 13 files changed, 80 insertions(+), 18 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs index 3358a6c4c..bf8d71501 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { + using System.Threading.Tasks; using Infrastructure; + using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -8,5 +10,9 @@ public WhenProjectingToFlatTypes(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() + => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs index bf0a20123..3badfe79c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { + using System.Threading.Tasks; using Infrastructure; + using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -8,5 +10,9 @@ public WhenProjectingToFlatTypes(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() + => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs index 9961a87a2..c786c668e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { + using System.Threading.Tasks; using Infrastructure; + using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -8,5 +10,9 @@ public WhenProjectingToFlatTypes(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() + => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs index e32bcc557..9c885149e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs @@ -1,6 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { + using System.Threading.Tasks; using Infrastructure; + using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -8,5 +10,9 @@ public WhenProjectingToFlatTypes(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() + => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Person.cs b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs index 622478fbb..6cf030069 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Person.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Person.cs @@ -7,6 +7,8 @@ public class Person [Key] public int PersonId { get; set; } + public string GetTitle() => "Dr"; + public string Name { get; set; } public int? AddressId { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs index 80c756144..63078d785 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/PersonViewModel.cs @@ -4,8 +4,12 @@ public class PersonViewModel { public int Id { get; set; } + public string Title { get; set; } + public string Name { get; set; } + public void SetName(string name) => Name = name; + public int? AddressId { get; set; } public string AddressLine1 { get; set; } diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs index dc7e2b0f3..33b2f934d 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs @@ -14,8 +14,7 @@ protected WhenProjectingToFlatTypes(ITestContext context) { } - [Fact] - public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() + protected Task DoShouldProjectAComplexTypeMemberToAFlatTypeList() { return RunTest(async context => { diff --git a/AgileMapper/Api/PlanTargetSelector.cs b/AgileMapper/Api/PlanTargetSelector.cs index b86ffdbce..624ab53b1 100644 --- a/AgileMapper/Api/PlanTargetSelector.cs +++ b/AgileMapper/Api/PlanTargetSelector.cs @@ -82,7 +82,7 @@ private MappingPlan GetMappingPlan( private static MappingPlan GetMappingPlan( IMappingContext planContext, Func mappingDataFactory, - IEnumerable>>> configurations = null) + ICollection>>> configurations = null) { if (configurations?.Any() == true) { diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index e1b4b6b0e..06dd8cfc6 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -19,7 +19,9 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = value => true, - AllowObjectTracking = true + AllowObjectTracking = true, + AllowGetMethods = true, + AllowSetMethods = true }, new CopySourceEnumerablePopulationStrategy(), MapRecursionCallRecursiveMemberMappingStrategy.Instance, @@ -34,7 +36,9 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = value => true, - AllowObjectTracking = true + AllowObjectTracking = true, + AllowGetMethods = true, + AllowSetMethods = true }, new MergeEnumerablePopulationStrategy(), MapRecursionCallRecursiveMemberMappingStrategy.Instance, @@ -49,7 +53,9 @@ internal class MappingRuleSetCollection SourceElementsCouldBeNull = true, UseTryCatch = true, GuardMemberAccesses = value => true, - AllowObjectTracking = true + AllowObjectTracking = true, + AllowGetMethods = true, + AllowSetMethods = true }, OverwriteEnumerablePopulationStrategy.Instance, MapRecursionCallRecursiveMemberMappingStrategy.Instance, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index 670035903..f579fbcad 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -2,6 +2,7 @@ namespace AgileObjects.AgileMapper { using System; using System.Linq.Expressions; + using Members; internal class MappingRuleSetSettings { @@ -20,5 +21,9 @@ internal class MappingRuleSetSettings public bool AllowEnumerableAssignment { get; set; } public bool AllowObjectTracking { get; set; } + + public bool AllowGetMethods { get; set; } + + public bool AllowSetMethods { get; set; } } } \ No newline at end of file diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index fe8506180..efb594820 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -162,6 +162,13 @@ public static void RegisterTargetMemberDataSourcesIfRequired( public static bool TargetMemberIsUnmappable(this IMemberMapperData mapperData, out string reason) { + if (!mapperData.RuleSet.Settings.AllowSetMethods && + (mapperData.TargetMember.LeafMember.MemberType == MemberType.SetMethod)) + { + reason = "Set methods are unsupported by rule set '" + mapperData.RuleSet.Name + "'"; + return true; + } + return TargetMemberIsUnmappable( mapperData, mapperData.TargetMember, diff --git a/AgileMapper/Members/MemberTypeExtensions.cs b/AgileMapper/Members/MemberTypeExtensions.cs index dfec01b0a..693ab6f14 100644 --- a/AgileMapper/Members/MemberTypeExtensions.cs +++ b/AgileMapper/Members/MemberTypeExtensions.cs @@ -13,5 +13,17 @@ public static bool IsReadable(this MemberType memberType) return true; } + + public static bool IsMethod(this MemberType memberType) + { + switch (memberType) + { + case MemberType.GetMethod: + case MemberType.SetMethod: + return true; + } + + return false; + } } } \ No newline at end of file diff --git a/AgileMapper/Members/SourceMemberMatcher.cs b/AgileMapper/Members/SourceMemberMatcher.cs index 8372046be..5905e5d05 100644 --- a/AgileMapper/Members/SourceMemberMatcher.cs +++ b/AgileMapper/Members/SourceMemberMatcher.cs @@ -70,8 +70,9 @@ private static bool ExactMatchingSourceMemberExists( { var sourceMember = QuerySourceMembers( parentSourceMember, - m => targetData.MapperData.TargetMember.LeafMember.Equals(m) || - targetData.MapperData.TargetMember.JoinedNames.Match(new[] { m.Name })) + targetData, + (m, data) => data.MapperData.TargetMember.LeafMember.Equals(m) || + data.MapperData.TargetMember.JoinedNames.Match(new[] { m.Name })) .FirstOrDefault(); if ((sourceMember == null) || @@ -87,13 +88,18 @@ private static bool ExactMatchingSourceMemberExists( private static IEnumerable QuerySourceMembers( IQualifiedMember parentMember, - Func filter) + IChildMemberMappingData mappingData, + Func filter) { - return GlobalContext + var members = GlobalContext .Instance .MemberCache .GetSourceMembers(parentMember.Type) - .Where(filter); + .Where(m => filter.Invoke(m, mappingData)); + + return mappingData.RuleSet.Settings.AllowGetMethods + ? members + : members.Where(m => m.MemberType != MemberType.GetMethod); } private static IQualifiedMember GetFinalSourceMember( @@ -133,7 +139,8 @@ private static IEnumerable EnumerateSourceMembers( var relevantSourceMembers = QuerySourceMembers( parentMember, - sourceMember => MembersHaveCompatibleTypes(sourceMember, rootData)); + rootData, + MembersHaveCompatibleTypes); foreach (var sourceMember in relevantSourceMembers) { @@ -170,13 +177,9 @@ private static bool MembersHaveCompatibleTypes(Member sourceMember, IChildMember } private static bool IsMatchingMember(IQualifiedMember sourceMember, IMemberMapperData mapperData) - { - return mapperData.TargetMember.Matches(sourceMember) && TypesAreCompatible(sourceMember.Type, mapperData); - } + => mapperData.TargetMember.Matches(sourceMember) && TypesAreCompatible(sourceMember.Type, mapperData); private static bool TypesAreCompatible(Type sourceType, IMemberMapperData mapperData) - { - return mapperData.MapperContext.ValueConverters.CanConvert(sourceType, mapperData.TargetMember.Type); - } + => mapperData.MapperContext.ValueConverters.CanConvert(sourceType, mapperData.TargetMember.Type); } } \ No newline at end of file From 1e0e7ea39dd654a1a520adad33233630b260a008 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 20:52:48 +0000 Subject: [PATCH 130/176] Test coverage for projecting to structs with an explicit constructor --- .../WhenProjectingFlatTypes.cs | 6 ++++ .../WhenProjectingToFlatTypes.cs | 6 ---- .../WhenProjectingFlatTypes.cs | 6 ++++ .../WhenProjectingToFlatTypes.cs | 6 ---- .../WhenProjectingFlatTypes.cs | 5 ++++ .../WhenProjectingToFlatTypes.cs | 6 ---- .../WhenProjectingFlatTypes.cs | 5 ++++ .../WhenProjectingToFlatTypes.cs | 6 ---- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../WhenProjectingToEnumerableMembers.cs | 2 +- .../TestClasses/ProductDtoStruct.cs | 15 ++++++++++ .../WhenProjectingFlatTypes.cs | 30 +++++++++++++++++-- .../WhenProjectingToFlatTypes.cs | 3 +- AgileMapper/Api/IProjectionResultSpecifier.cs | 6 ++-- 14 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs index 60a239b9f..fa1abb5cd 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingFlatTypes.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { + using System.Threading.Tasks; using Infrastructure; using Orms; + using Xunit; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { @@ -9,5 +11,9 @@ public WhenProjectingFlatTypes(InMemoryEf5TestContext context) : base(context) { } + + [Fact] + public Task ShouldErrorProjectingStructCtorParameters() + => RunShouldErrorProjectingStructCtorParameters(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs index bf8d71501..3358a6c4c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToFlatTypes.cs @@ -1,8 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 { - using System.Threading.Tasks; using Infrastructure; - using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -10,9 +8,5 @@ public WhenProjectingToFlatTypes(InMemoryEf5TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() - => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs index 9d39b1d0a..aefe6c03e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingFlatTypes.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { + using System.Threading.Tasks; using Infrastructure; using Orms; + using Xunit; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { @@ -9,5 +11,9 @@ public WhenProjectingFlatTypes(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public Task ShouldErrorProjectingStructCtorParameters() + => RunShouldErrorProjectingStructCtorParameters(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs index 3badfe79c..bf0a20123 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToFlatTypes.cs @@ -1,8 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 { - using System.Threading.Tasks; using Infrastructure; - using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -10,9 +8,5 @@ public WhenProjectingToFlatTypes(InMemoryEf6TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() - => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs index 3a5b17561..a7f9381ae 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingFlatTypes.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { + using System.Threading.Tasks; using Infrastructure; using Orms; + using Xunit; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { @@ -9,5 +11,8 @@ public WhenProjectingFlatTypes(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectStructCtorParameters() => RunShouldProjectStructCtorParameters(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs index c786c668e..9961a87a2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToFlatTypes.cs @@ -1,8 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 { - using System.Threading.Tasks; using Infrastructure; - using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -10,9 +8,5 @@ public WhenProjectingToFlatTypes(InMemoryEfCore1TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() - => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs index e1e36a384..b7aa3bae2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingFlatTypes.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { + using System.Threading.Tasks; using Infrastructure; using Orms; + using Xunit; public class WhenProjectingFlatTypes : WhenProjectingFlatTypes { @@ -9,5 +11,8 @@ public WhenProjectingFlatTypes(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectStructCtorParameters() => RunShouldProjectStructCtorParameters(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs index 9c885149e..e32bcc557 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToFlatTypes.cs @@ -1,8 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 { - using System.Threading.Tasks; using Infrastructure; - using Xunit; public class WhenProjectingToFlatTypes : WhenProjectingToFlatTypes { @@ -10,9 +8,5 @@ public WhenProjectingToFlatTypes(InMemoryEfCore2TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() - => DoShouldProjectAComplexTypeMemberToAFlatTypeList(); } } diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 0e2653ab8..8e8049d4f 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -109,6 +109,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index f18b87fdd..e51a16ab9 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -23,7 +23,7 @@ protected Task RunShouldProjectToAComplexTypeCollectionMember() protected Task RunShouldErrorProjectingToAComplexTypeCollectionMember() => RunTestAndExpectThrow(ProjectToComplexTypeCollectionMember); - protected async Task ProjectToComplexTypeCollectionMember(TOrmContext context) + private static async Task ProjectToComplexTypeCollectionMember(TOrmContext context) { var rotaEntry1 = new RotaEntry { diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs b/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs new file mode 100644 index 000000000..961f78b32 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs @@ -0,0 +1,15 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public struct ProductDtoStruct + { + public ProductDtoStruct(int productId) + { + ProductId = productId; + Name = default(string); + } + + public int ProductId { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index dce9a0d6b..7c527f9e0 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -15,7 +15,7 @@ protected WhenProjectingFlatTypes(ITestContext context) } [Fact] - public Task ShouldProjectAFlatTypeToAFlatTypeArray() + public Task ShouldProjectToAFlatTypeArray() { return RunTest(async context => { @@ -42,8 +42,34 @@ public Task ShouldProjectAFlatTypeToAFlatTypeArray() }); } + #region Project -> Struct Ctor Parameters + + protected Task RunShouldProjectStructCtorParameters() + => RunTest(DoShouldProjectStructCtorParameters); + + protected Task RunShouldErrorProjectingStructCtorParameters() + => RunTestAndExpectThrow(DoShouldProjectStructCtorParameters); + + private static async Task DoShouldProjectStructCtorParameters(TOrmContext context) + { + var product = new Product { Name = "Product One" }; + + context.Products.Add(product); + await context.SaveChanges(); + + var productDto = context + .Products + .Project().To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("Product One"); + } + + #endregion + [Fact] - public Task ShouldProjectAFlatTypeToANonMatchingTypeList() + public Task ShouldProjectToANonMatchingTypeList() { return RunTest(async context => { diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs index 33b2f934d..dc7e2b0f3 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs @@ -14,7 +14,8 @@ protected WhenProjectingToFlatTypes(ITestContext context) { } - protected Task DoShouldProjectAComplexTypeMemberToAFlatTypeList() + [Fact] + public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() { return RunTest(async context => { diff --git a/AgileMapper/Api/IProjectionResultSpecifier.cs b/AgileMapper/Api/IProjectionResultSpecifier.cs index c9a705251..f2dde5097 100644 --- a/AgileMapper/Api/IProjectionResultSpecifier.cs +++ b/AgileMapper/Api/IProjectionResultSpecifier.cs @@ -26,8 +26,7 @@ public interface IProjectionResultSpecifier /// . The projection is not performed until the Queryable is /// enumerated by a call to .ToArray() or similar. /// - IQueryable To() - where TResultElement : class; + IQueryable To(); /// /// Project the elements of the source IQueryable{T} to instances of the given @@ -48,7 +47,6 @@ IQueryable To() /// enumerated by a call to .ToArray() or similar. /// IQueryable To( - Expression>> configuration) - where TResultElement : class; + Expression>> configuration); } } \ No newline at end of file From 17a49e60b12694fee7d77e01af1084886b231db6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 21:13:06 +0000 Subject: [PATCH 131/176] Start of support for configuring projection constructor parameters by type / Organising custom projection data source tests --- .../WhenConfiguringDataSources.cs | 26 ----------- .../WhenConfiguringDataSources.cs | 24 ---------- .../WhenConfiguringDataSources.cs | 24 ---------- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringConstructorDataSources.cs | 13 ++++++ .../WhenConfiguringDataSources.cs | 24 ---------- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../WhenConfiguringConstructorDataSources.cs | 45 +++++++++++++++++++ .../WhenConfiguringDataSources.cs | 22 ++++++--- .../TestClasses/ProductDtoStruct.cs | 6 +-- .../CustomDataSourceTargetMemberSpecifier.cs | 3 ++ ...ojectionDataSourceTargetMemberSpecifier.cs | 10 +++++ 12 files changed, 91 insertions(+), 108 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs index 46a6cc021..7dd282ace 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDataSources.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration { - using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; - using Xunit; public class WhenConfiguringDataSources : WhenConfiguringDataSources { @@ -11,29 +9,5 @@ public WhenConfiguringDataSources(InMemoryEf5TestContext context) : base(context) { } - - [Fact] - public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredConstant() - => DoShouldConditionallyApplyAConfiguredConstant(); - - [Fact] - public Task ShouldApplyAConfiguredConstantToANestedMember() - => DoShouldApplyAConfiguredConstantToANestedMember(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); - - [Fact] - public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() - => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); - - [Fact] - public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); - - [Fact] - public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs index a47822d31..ab04c4430 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDataSources.cs @@ -12,34 +12,10 @@ public WhenConfiguringDataSources(InMemoryEf6TestContext context) { } - [Fact] - public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredConstant() - => DoShouldConditionallyApplyAConfiguredConstant(); - - [Fact] - public Task ShouldApplyAConfiguredConstantToANestedMember() - => DoShouldApplyAConfiguredConstantToANestedMember(); - [Fact] public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); [Fact] public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); - - [Fact] - public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() - => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); - - [Fact] - public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); - - [Fact] - public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs index 04ba32c98..2226f765b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDataSources.cs @@ -12,34 +12,10 @@ public WhenConfiguringDataSources(InMemoryEfCore1TestContext context) { } - [Fact] - public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredConstant() - => DoShouldConditionallyApplyAConfiguredConstant(); - - [Fact] - public Task ShouldApplyAConfiguredConstantToANestedMember() - => DoShouldApplyAConfiguredConstantToANestedMember(); - [Fact] public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); [Fact] public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); - - [Fact] - public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() - => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); - - [Fact] - public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); - - [Fact] - public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 1da3af445..f4a1ccdd1 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -145,6 +145,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs new file mode 100644 index 000000000..d417688bc --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringConstructorDataSources : WhenConfiguringConstructorDataSources + { + public WhenConfiguringConstructorDataSources(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs index 4d0e8439a..ce2369a9c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDataSources.cs @@ -12,34 +12,10 @@ public WhenConfiguringDataSources(InMemoryEfCore2TestContext context) { } - [Fact] - public Task ShouldApplyAConfiguredConstant() => DoShouldApplyAConfiguredConstant(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredConstant() - => DoShouldConditionallyApplyAConfiguredConstant(); - - [Fact] - public Task ShouldApplyAConfiguredConstantToANestedMember() - => DoShouldApplyAConfiguredConstantToANestedMember(); - [Fact] public Task ShouldApplyAConfiguredMember() => DoShouldApplyAConfiguredMember(); [Fact] public Task ShouldApplyMultipleConfiguredMembers() => DoShouldApplyMultipleConfiguredMembers(); - - [Fact] - public Task ShouldConditionallyApplyAConfiguredMember() => DoShouldConditionallyApplyAConfiguredMember(); - - [Fact] - public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() - => DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder(); - - [Fact] - public Task ShouldHandleANullMemberInACondition() => DoShouldHandleANullMemberInACondition(); - - [Fact] - public Task ShouldSupportMultipleDivergedMappers() => DoShouldSupportMultipleDivergedMappers(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 8e8049d4f..d7aa544e0 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,6 +73,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs new file mode 100644 index 000000000..49d490970 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs @@ -0,0 +1,45 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenConfiguringConstructorDataSources : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringConstructorDataSources(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstantByParameterType() + { + return RunTest(async context => + { + var product = new Product { Name = "Prod.One" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("Bananas!") + .ToCtor(); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("Bananas!"); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 16c3f5ff2..4f4caa541 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; + using Xunit; public abstract class WhenConfiguringDataSources : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -13,7 +14,8 @@ protected WhenConfiguringDataSources(ITestContext context) { } - protected Task DoShouldApplyAConfiguredConstant() + [Fact] + public Task ShouldApplyAConfiguredConstant() { return RunTest(async context => { @@ -41,7 +43,8 @@ protected Task DoShouldApplyAConfiguredConstant() }); } - protected Task DoShouldConditionallyApplyAConfiguredConstant() + [Fact] + public Task ShouldConditionallyApplyAConfiguredConstant() { return RunTest(async context => { @@ -78,7 +81,8 @@ protected Task DoShouldConditionallyApplyAConfiguredConstant() }); } - protected Task DoShouldApplyAConfiguredConstantToANestedMember() + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMember() { return RunTest(async context => { @@ -174,7 +178,8 @@ protected Task DoShouldApplyMultipleConfiguredMembers() }); } - protected Task DoShouldConditionallyApplyAConfiguredMember() + [Fact] + public Task ShouldConditionallyApplyAConfiguredMember() { return RunTest(async context => { @@ -213,7 +218,8 @@ protected Task DoShouldConditionallyApplyAConfiguredMember() }); } - protected Task DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder() + [Fact] + public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() { return RunTest(async context => { @@ -253,7 +259,8 @@ protected Task DoShouldApplyConditionalAndUnconditionalDataSourcesInOrder() }); } - protected Task DoShouldHandleANullMemberInACondition() + [Fact] + public Task ShouldHandleANullMemberInACondition() { return RunTest(async context => { @@ -298,7 +305,8 @@ protected Task DoShouldHandleANullMemberInACondition() }); } - protected Task DoShouldSupportMultipleDivergedMappers() + [Fact] + public Task ShouldSupportMultipleDivergedMappers() { return RunTest(async context => { diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs b/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs index 961f78b32..537b0a6d4 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs @@ -2,10 +2,10 @@ { public struct ProductDtoStruct { - public ProductDtoStruct(int productId) + public ProductDtoStruct(string name) { - ProductId = productId; - Name = default(string); + ProductId = default(int); + Name = name; } public int ProductId { get; set; } diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index 807be5e60..e8610764e 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -142,6 +142,9 @@ private ConfiguredLambdaInfo GetValueLambda() public IMappingConfigContinuation ToCtor() => RegisterDataSource(CreateForCtorParam); + IProjectionConfigContinuation ICustomProjectionDataSourceTargetMemberSpecifier.ToCtor() + => RegisterDataSource(CreateForCtorParam); + public IMappingConfigContinuation ToCtor(string parameterName) => RegisterDataSource(() => CreateForCtorParam(parameterName)); diff --git a/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs index e444dca87..dfd7414b0 100644 --- a/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs @@ -21,5 +21,15 @@ public interface ICustomProjectionDataSourceTargetMemberSpecifier IProjectionConfigContinuation To( Expression> resultMember); + + /// + /// Apply the configuration to the constructor parameter with the type specified by the type argument. + /// + /// The result constructor parameter's type. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source + /// and result type being configured. + /// + IProjectionConfigContinuation ToCtor(); } } \ No newline at end of file From de8521a47140931e7dda2f6014925f6f9b4408d7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 7 Feb 2018 21:22:05 +0000 Subject: [PATCH 132/176] Support for configuring projection constructor parameters by name --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringConstructorDataSources.cs | 23 +++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringConstructorDataSources.cs | 23 +++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringConstructorDataSources.cs | 23 +++++ .../WhenConfiguringConstructorDataSources.cs | 10 ++ .../WhenConfiguringConstructorDataSources.cs | 91 +++++++++++++------ .../CustomDataSourceTargetMemberSpecifier.cs | 6 ++ ...ojectionDataSourceTargetMemberSpecifier.cs | 10 ++ 10 files changed, 163 insertions(+), 26 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringConstructorDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringConstructorDataSources.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringConstructorDataSources.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 60548ca0c..5748cfe16 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -85,6 +85,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringConstructorDataSources.cs new file mode 100644 index 000000000..bc95bc683 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringConstructorDataSources.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringConstructorDataSources : WhenConfiguringConstructorDataSources + { + public WhenConfiguringConstructorDataSources(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorApplyingAConfiguredConstantByParameterType() + => RunShouldErrorApplyingAConfiguredConstantByParameterType(); + + [Fact] + public Task ShouldErrorApplyingAConfiguredExpressionByParameterName() + => RunShouldErrorApplyingAConfiguredExpressionByParameterName(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 48adbc4d0..b14372bd8 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,6 +87,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringConstructorDataSources.cs new file mode 100644 index 000000000..0addef066 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringConstructorDataSources.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringConstructorDataSources : WhenConfiguringConstructorDataSources + { + public WhenConfiguringConstructorDataSources(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorApplyingAConfiguredConstantByParameterType() + => RunShouldErrorApplyingAConfiguredConstantByParameterType(); + + [Fact] + public Task ShouldErrorApplyingAConfiguredExpressionByParameterName() + => RunShouldErrorApplyingAConfiguredExpressionByParameterName(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index b88530cc7..ee1077d41 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -211,6 +211,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringConstructorDataSources.cs new file mode 100644 index 000000000..44d2106ae --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringConstructorDataSources.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringConstructorDataSources : WhenConfiguringConstructorDataSources + { + public WhenConfiguringConstructorDataSources(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstantByParameterType() + => RunShouldApplyAConfiguredConstantByParameterType(); + + [Fact] + public Task ShouldApplyAConfiguredExpressionByParameterName() + => RunShouldApplyAConfiguredExpressionByParameterName(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs index d417688bc..d5715f78f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringConstructorDataSources.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; + using Xunit; public class WhenConfiguringConstructorDataSources : WhenConfiguringConstructorDataSources { @@ -9,5 +11,13 @@ public WhenConfiguringConstructorDataSources(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldApplyAConfiguredConstantByParameterType() + => RunShouldApplyAConfiguredConstantByParameterType(); + + [Fact] + public Task ShouldApplyAConfiguredExpressionByParameterName() + => RunShouldApplyAConfiguredExpressionByParameterName(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs index 49d490970..d7563f66b 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; - using Xunit; public abstract class WhenConfiguringConstructorDataSources : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -13,33 +12,73 @@ protected WhenConfiguringConstructorDataSources(ITestContext contex { } - [Fact] - public Task ShouldApplyAConfiguredConstantByParameterType() + #region Project -> Ctor Parameter by Type + + protected Task RunShouldApplyAConfiguredConstantByParameterType() + => RunTest(DoShouldApplyAConfiguredConstantByParameterType); + + protected Task RunShouldErrorApplyingAConfiguredConstantByParameterType() + => RunTestAndExpectThrow(DoShouldApplyAConfiguredConstantByParameterType); + + private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context) { - return RunTest(async context => + var product = new Product { Name = "Prod.One" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) { - var product = new Product { Name = "Prod.One" }; - - context.Products.Add(product); - await context.SaveChanges(); - - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map("Bananas!") - .ToCtor(); - - var productDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - productDto.ProductId.ShouldBe(product.ProductId); - productDto.Name.ShouldBe("Bananas!"); - } - }); + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("Bananas!") + .ToCtor(); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("Bananas!"); + } } + + #endregion + + #region Project -> Ctor Parameter by Name + protected Task RunShouldApplyAConfiguredExpressionByParameterName() + => RunTest(DoShouldApplyAConfiguredExpressionByParameterName); + + protected Task RunShouldErrorApplyingAConfiguredExpressionByParameterName() + => RunTestAndExpectThrow(DoShouldApplyAConfiguredExpressionByParameterName); + + private static async Task DoShouldApplyAConfiguredExpressionByParameterName(TOrmContext context) + { + var product = new Product { Name = "Prod.One" }; + + context.Products.Add(product); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => "2 * 3 = " + (2 * 3)) + .ToCtor("name"); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("2 * 3 = 6"); + } + } + + #endregion } } diff --git a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs index e8610764e..20418ac6d 100644 --- a/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/CustomDataSourceTargetMemberSpecifier.cs @@ -148,6 +148,12 @@ IProjectionConfigContinuation ICustomProjectionDataSourceTarge public IMappingConfigContinuation ToCtor(string parameterName) => RegisterDataSource(() => CreateForCtorParam(parameterName)); + IProjectionConfigContinuation ICustomProjectionDataSourceTargetMemberSpecifier.ToCtor( + string parameterName) + { + return RegisterDataSource(() => CreateForCtorParam(parameterName)); + } + #region Ctor Helpers private ConfiguredDataSourceFactory CreateForCtorParam() diff --git a/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs index dfd7414b0..7edd754e1 100644 --- a/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs +++ b/AgileMapper/Api/Configuration/Projection/ICustomProjectionDataSourceTargetMemberSpecifier.cs @@ -31,5 +31,15 @@ IProjectionConfigContinuation To( /// and result type being configured. /// IProjectionConfigContinuation ToCtor(); + + /// + /// Apply the configuration to the constructor parameter with the specified . + /// + /// The result constructor parameter's name. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source and + /// result type being configured. + /// + IProjectionConfigContinuation ToCtor(string parameterName); } } \ No newline at end of file From eb11c2c41d5c3dbb7bbd37f3afa3bfa3318bc7e2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 8 Feb 2018 10:27:24 +0000 Subject: [PATCH 133/176] Start of projection enum pairing / Organising projection configuration API interfaces / Support for using string operators to map to bools and enums --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringEnumMapping.cs | 13 +++++ .../AgileMapper.UnitTests.Orms.csproj | 4 +- .../WhenConfiguringConstructorDataSources.cs | 8 +-- .../WhenConfiguringEnumMapping.cs | 50 +++++++++++++++++ .../TestClasses/Order.cs | 3 + .../TestClasses/OrderViewModel.cs | 12 ++++ .../TestClasses/Product.cs | 2 + .../{ProductDtoStruct.cs => ProductStruct.cs} | 7 ++- .../WhenProjectingFlatTypes.cs | 2 +- .../Api/Configuration/EnumPairSpecifier.cs | 55 ++++++++++--------- .../Api/Configuration/IFullMappingSettings.cs | 9 +-- .../IMappingEnumPairSpecifier.cs | 29 ++++++++++ .../MappingConfigStartingPoint.cs | 22 ++++---- .../Api/Configuration/MappingConfigurator.cs | 14 ++++- .../IConditionalProjectionConfigurator.cs | 24 ++++++++ .../IConditionalRootProjectionConfigurator.cs | 1 - .../Projection/IFullProjectionConfigurator.cs | 24 +------- .../Projection/IFullProjectionSettings.cs | 41 ++++++++++++++ .../IProjectionEnumPairSpecifier.cs | 29 ++++++++++ .../Projection/IRootProjectionConfigurator.cs | 2 - AgileMapper/TypeConversion/ToBoolConverter.cs | 10 +++- AgileMapper/TypeConversion/ToEnumConverter.cs | 7 +-- .../TypeConversion/ToNumericConverter.cs | 2 - .../TypeConversion/ToStringConverter.cs | 35 ++++++++---- .../TypeConversion/TryParseConverter.cs | 4 +- 26 files changed, 311 insertions(+), 99 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringEnumMapping.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs rename AgileMapper.UnitTests.Orms/TestClasses/{ProductDtoStruct.cs => ProductStruct.cs} (61%) create mode 100644 AgileMapper/Api/Configuration/IMappingEnumPairSpecifier.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IConditionalProjectionConfigurator.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IFullProjectionSettings.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionEnumPairSpecifier.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index f4a1ccdd1..a08ba6aa0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -147,6 +147,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringEnumMapping.cs new file mode 100644 index 000000000..3e66e7c93 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringEnumMapping.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringEnumMapping : WhenConfiguringEnumMapping + { + public WhenConfiguringEnumMapping(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index d7aa544e0..79b4f1e8b 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -74,6 +74,7 @@ Properties\VersionInfo.cs + @@ -107,10 +108,11 @@ + - + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs index d7563f66b..6fc278ab8 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs @@ -31,13 +31,13 @@ private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmCo { mapper.WhenMapping .From() - .ProjectedTo() + .ProjectedTo() .Map("Bananas!") .ToCtor(); var productDto = context .Products - .ProjectUsing(mapper).To() + .ProjectUsing(mapper).To() .ShouldHaveSingleItem(); productDto.ProductId.ShouldBe(product.ProductId); @@ -65,13 +65,13 @@ private static async Task DoShouldApplyAConfiguredExpressionByParameterName(TOrm { mapper.WhenMapping .From() - .ProjectedTo() + .ProjectedTo() .Map(p => "2 * 3 = " + (2 * 3)) .ToCtor("name"); var productDto = context .Products - .ProjectUsing(mapper).To() + .ProjectUsing(mapper).To() .ShouldHaveSingleItem(); productDto.ProductId.ShouldBe(product.ProductId); diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs new file mode 100644 index 000000000..77f1ec49e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs @@ -0,0 +1,50 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System; + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + using PaymentTypeUk = UnitTests.TestClasses.PaymentTypeUk; + using PaymentTypeUs = UnitTests.TestClasses.PaymentTypeUs; + + public abstract class WhenConfiguringEnumMapping : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringEnumMapping(ITestContext context) : base(context) + { + } + + [Fact] + public Task ShouldPairEnumMembers() + { + return RunTest(async context => + { + var order = new Order + { + DatePlaced = DateTime.Now, + PaymentType = PaymentTypeUk.Cheque + }; + + context.Orders.Add(order); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check); + + var orderVm = context + .Orders + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + orderVm.DatePlaced.ShouldBe(order.DatePlaced); + orderVm.PaymentType.ShouldBe(PaymentTypeUs.Check); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs index cb7aa02b6..65dca9f45 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Order.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; + using UnitTests.TestClasses; public class Order { @@ -11,6 +12,8 @@ public class Order public DateTime DatePlaced { get; set; } + public PaymentTypeUk PaymentType { get; set; } + public ICollection Items { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs new file mode 100644 index 000000000..59ddf49b9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using UnitTests.TestClasses; + + public class OrderViewModel + { + public DateTime DatePlaced { get; set; } + + public PaymentTypeUs PaymentType { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Product.cs b/AgileMapper.UnitTests.Orms/TestClasses/Product.cs index fad662e16..329c12de8 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Product.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/Product.cs @@ -8,5 +8,7 @@ public class Product public int ProductId { get; set; } public string Name { get; set; } + + public double Price { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs b/AgileMapper.UnitTests.Orms/TestClasses/ProductStruct.cs similarity index 61% rename from AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs rename to AgileMapper.UnitTests.Orms/TestClasses/ProductStruct.cs index 537b0a6d4..1f6b14e29 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/ProductDtoStruct.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/ProductStruct.cs @@ -1,15 +1,18 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses { - public struct ProductDtoStruct + public struct ProductStruct { - public ProductDtoStruct(string name) + public ProductStruct(string name) { ProductId = default(int); Name = name; + Price = default(double); } public int ProductId { get; set; } public string Name { get; set; } + + public double Price { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 7c527f9e0..2e47f3f6e 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -59,7 +59,7 @@ private static async Task DoShouldProjectStructCtorParameters(TOrmContext contex var productDto = context .Products - .Project().To() + .Project().To() .ShouldHaveSingleItem(); productDto.ProductId.ShouldBe(product.ProductId); diff --git a/AgileMapper/Api/Configuration/EnumPairSpecifier.cs b/AgileMapper/Api/Configuration/EnumPairSpecifier.cs index e26a0a8f2..58bdbc83b 100644 --- a/AgileMapper/Api/Configuration/EnumPairSpecifier.cs +++ b/AgileMapper/Api/Configuration/EnumPairSpecifier.cs @@ -6,15 +6,12 @@ using AgileMapper.Configuration; using Extensions.Internal; using NetStandardPolyfills; + using Projection; using ReadableExpressions.Extensions; - /// - /// Provides options for specifying the enum member to which the configured enum member should be paired. - /// - /// The source type being configured. - /// The target type being configured. - /// The type of the first enum being paired. - public class EnumPairSpecifier + internal class EnumPairSpecifier : + IMappingEnumPairSpecifier, + IProjectionEnumPairSpecifier { private readonly MappingConfigInfo _configInfo; private readonly TFirstEnum[] _firstEnumMembers; @@ -29,9 +26,9 @@ private EnumPairSpecifier( #region Factory Method - internal static EnumPairSpecifier For( + public static EnumPairSpecifier For( MappingConfigInfo configInfo, - TFirstEnum[] firstEnumMembers) + params TFirstEnum[] firstEnumMembers) { ThrowIfNotEnumType(); ThrowIfEmpty(firstEnumMembers); @@ -39,8 +36,6 @@ internal static EnumPairSpecifier For( return new EnumPairSpecifier(configInfo, firstEnumMembers); } - private MapperContext MapperContext => _configInfo.MapperContext; - private static void ThrowIfNotEnumType() { if (!typeof(T).IsEnum()) @@ -60,25 +55,33 @@ private static void ThrowIfEmpty(ICollection firstEnumMembers) #endregion - /// - /// Configure this mapper to map the specified first enum member to the given . - /// - /// The type of the second enum being paired. - /// The second enum member in the pair. - /// An IMappingConfigContinuation with which to configure other aspects of mapping. + private MapperContext MapperContext => _configInfo.MapperContext; + public IMappingConfigContinuation With(TSecondEnum secondEnumMember) where TSecondEnum : struct - => With(new[] { secondEnumMember }); - - /// - /// Configure this mapper to map the previously-specified set of enum members to the given - /// . - /// - /// The type of the second enum being paired. - /// The second set of enum members in the pairs. - /// An IMappingConfigContinuation with which to configure other aspects of mapping. + { + return PairEnums(secondEnumMember); + } + + IProjectionConfigContinuation IProjectionEnumPairSpecifier.With( + TSecondEnum secondEnumMember) + { + return PairEnums(secondEnumMember); + } + public IMappingConfigContinuation With(params TSecondEnum[] secondEnumMembers) where TSecondEnum : struct + { + return PairEnums(secondEnumMembers); + } + + IProjectionConfigContinuation IProjectionEnumPairSpecifier.With( + params TSecondEnum[] secondEnumMembers) + { + return PairEnums(secondEnumMembers); + } + + private MappingConfigContinuation PairEnums(params TSecondEnum[] secondEnumMembers) { ThrowIfNotEnumType(); ThrowIfSameTypes(); diff --git a/AgileMapper/Api/Configuration/IFullMappingSettings.cs b/AgileMapper/Api/Configuration/IFullMappingSettings.cs index 7d89e2a3b..a63623a04 100644 --- a/AgileMapper/Api/Configuration/IFullMappingSettings.cs +++ b/AgileMapper/Api/Configuration/IFullMappingSettings.cs @@ -71,15 +71,16 @@ public interface IFullMappingSettings : IConditionalMappingCon IFullMappingSettings MapNullCollectionsToNull(); /// - /// Configure this mapper to pair the given with a member of another enum Type. + /// Configure this mapper to pair the given with a member of another + /// enum Type. /// /// The type of the first enum being paired. /// The first enum member in the pair. /// - /// An EnumPairSpecifier with which to specify the enum member to which the given - /// should be paired. + /// An IMappingEnumPairSpecifier with which to specify the enum member to which the given + /// should be paired. /// - EnumPairSpecifier PairEnum(TFirstEnum enumMember) + IMappingEnumPairSpecifier PairEnum(TFirstEnum enumMember) where TFirstEnum : struct; /// diff --git a/AgileMapper/Api/Configuration/IMappingEnumPairSpecifier.cs b/AgileMapper/Api/Configuration/IMappingEnumPairSpecifier.cs new file mode 100644 index 000000000..09acba857 --- /dev/null +++ b/AgileMapper/Api/Configuration/IMappingEnumPairSpecifier.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.Api.Configuration +{ + /// + /// Provides options for specifying the enum member with which the configured enum member should be paired. + /// + /// The source type being configured. + /// The target type being configured. + public interface IMappingEnumPairSpecifier + { + /// + /// Configure this mapper to map the specified first enum member to the given . + /// + /// The type of the second enum being paired. + /// The second enum member in the pair. + /// An IMappingConfigContinuation with which to configure other aspects of mapping. + IMappingConfigContinuation With(TSecondEnum secondEnumMember) + where TSecondEnum : struct; + + /// + /// Configure this mapper to map the previously-specified set of enum members to the given + /// . + /// + /// The type of the second enum being paired. + /// The second set of enum members in the pairs. + /// An IMappingConfigContinuation with which to configure other aspects of mapping. + IMappingConfigContinuation With(params TSecondEnum[] secondEnumMembers) + where TSecondEnum : struct; + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs index 75b6dd52d..b32e0338c 100644 --- a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs +++ b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs @@ -202,29 +202,31 @@ public IGlobalConfigSettings ThrowIfAnyMappingPlanIsIncomplete() } /// - /// Configure this mapper to pair the given with a member of another enum Type. - /// This pairing will apply to mappings between all types and MappingRuleSets (create new, overwrite, etc). + /// Configure this mapper to pair the given with a member of another + /// enum Type. This pairing will apply to mappings between all types and MappingRuleSets (create new, + /// overwrite, etc). /// /// The type of the first enum being paired. /// The first enum member in the pair. /// - /// An EnumPairSpecifier with which to specify the enum member to which the given - /// should be paired. + /// An IMappingEnumPairSpecifier with which to specify the enum member to which the given + /// should be paired. /// - public EnumPairSpecifier PairEnum(TFirstEnum enumMember) where TFirstEnum : struct + public IMappingEnumPairSpecifier PairEnum(TFirstEnum enumMember) where TFirstEnum : struct => PairEnums(enumMember); /// - /// Configure this mapper to pair the given with members of another enum Type. - /// Pairings will apply to mappings between all types and MappingRuleSets (create new, overwrite, etc). + /// Configure this mapper to pair the given with members of another + /// enum Type. Pairings will apply to mappings between all types and MappingRuleSets (create new, + /// overwrite, etc). /// /// The type of the first set of enum members being paired. /// The first set of enum members to pair. /// - /// An EnumPairSpecifier with which to specify the set of enum members to which the given - /// should be paired. + /// An IMappingEnumPairSpecifier with which to specify the set of enum members to which the given + /// should be paired. /// - public EnumPairSpecifier PairEnums(params TFirstEnum[] enumMembers) + public IMappingEnumPairSpecifier PairEnums(params TFirstEnum[] enumMembers) where TFirstEnum : struct { return EnumPairSpecifier.For(GlobalConfigInfo, enumMembers); diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index be83ff591..bb18ffe7c 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -167,12 +167,22 @@ public IFullMappingSettings MapNullCollectionsToNull() return this; } - public EnumPairSpecifier PairEnum(TFirstEnum enumMember) + public IMappingEnumPairSpecifier PairEnum(TFirstEnum enumMember) where TFirstEnum : struct - => EnumPairSpecifier.For(ConfigInfo, new[] { enumMember }); + { + return EnumPairSpecifier.For(ConfigInfo, enumMember); + } + + IProjectionEnumPairSpecifier IFullProjectionSettings.PairEnum( + TFirstEnum enumMember) + { + return EnumPairSpecifier.For(ConfigInfo, enumMember); + } IFullMappingConfigurator IFullMappingSettings.And => this; + IFullProjectionConfigurator IFullProjectionSettings.And => this; + #region Ignoring Members public IMappingConfigContinuation IgnoreTargetMembersOfType() diff --git a/AgileMapper/Api/Configuration/Projection/IConditionalProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IConditionalProjectionConfigurator.cs new file mode 100644 index 000000000..83726461d --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IConditionalProjectionConfigurator.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + using System; + using System.Linq.Expressions; + + /// + /// Provides options for configuring a condition which must evaluate to true for the configuration to apply + /// to mappings from and to the source and result types being configured. + /// + /// The source type to which the configuration should apply. + /// The result type to which the configuration should apply. + public interface IConditionalProjectionConfigurator + : IRootProjectionConfigurator + { + /// + /// Configure a condition which must evaluate to true for the configuration to apply. The condition + /// expression is passed the source element being projected. + /// + /// The condition to evaluate. + /// An IConditionalRootProjectionConfigurator with which to complete the configuration. + IConditionalRootProjectionConfigurator If( + Expression> condition); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs index 7f869d908..7da8e7c36 100644 --- a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs @@ -8,6 +8,5 @@ public interface IConditionalRootProjectionConfigurator : IRootProjectionConfigurator { - } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs index d4bee1769..ef7f4984a 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionConfigurator.cs @@ -1,34 +1,12 @@ namespace AgileObjects.AgileMapper.Api.Configuration.Projection { - using System; - using System.Linq.Expressions; - /// /// Provides options for configuring query projections from and to given source and result element Types. /// /// The source element Type to which the configuration should apply. /// The result element Type to which the configuration should apply. public interface IFullProjectionConfigurator : - IRootProjectionConfigurator + IFullProjectionSettings { - /// - /// Project recursive relationships to the specified . - /// For example, when projecting a Category entity which has a SubCategories property of Type - /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories - /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the - /// sub-categories of the sub-categories of the top-level Category selected, etc. The default is zero, - /// which only populates the first level of sub-categories. - /// - /// The depth to which to populate projected recursive relationships. - IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); - - /// - /// Configure a condition which must evaluate to true for the configuration to apply. The condition - /// expression is passed the source element being projected. - /// - /// The condition to evaluate. - /// An IConditionalRootProjectionConfigurator with which to complete the configuration. - IConditionalRootProjectionConfigurator If( - Expression> condition); } } diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionSettings.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionSettings.cs new file mode 100644 index 000000000..b0920eef0 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionSettings.cs @@ -0,0 +1,41 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for configuring settings for mappings from and to a given source and result type. + /// + /// The source type to which the configured settings should apply. + /// The result type to which the configured settings should apply. + public interface IFullProjectionSettings + : IConditionalProjectionConfigurator + { + /// + /// Project recursive relationships to the specified . + /// For example, when projecting a Category entity which has a SubCategories property of Type + /// IEnumerable{Category}, a recursion depth of 1 will populate the sub-categories of the sub-categories + /// of the top-level Category selected; a recursion depth of 2 will populate the sub-categories of the + /// sub-categories of the sub-categories of the top-level Category selected, etc. The default is zero, + /// which only populates the first level of sub-categories. + /// + /// The depth to which to populate projected recursive relationships. + IFullProjectionInlineConfigurator RecurseToDepth(int recursionDepth); + + /// + /// Configure this mapper to pair the given with a member of another + /// enum Type. + /// + /// The type of the first enum being paired. + /// The first enum member in the pair. + /// + /// An IProjectionEnumPairSpecifier with which to specify the enum member to which the given + /// should be paired. + /// + IProjectionEnumPairSpecifier PairEnum(TFirstEnum enumMember) + where TFirstEnum : struct; + + /// + /// Gets a link back to the full , for + /// api fluency. + /// + IFullProjectionConfigurator And { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionEnumPairSpecifier.cs b/AgileMapper/Api/Configuration/Projection/IProjectionEnumPairSpecifier.cs new file mode 100644 index 000000000..ebb029d37 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionEnumPairSpecifier.cs @@ -0,0 +1,29 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for specifying the enum member with which the configured enum member should be paired. + /// + /// The source type being configured. + /// The target type being configured. + public interface IProjectionEnumPairSpecifier + { + /// + /// Configure this mapper to project the specified first enum member to the given . + /// + /// The type of the second enum being paired. + /// The second enum member in the pair. + /// An IProjectionConfigContinuation with which to configure other aspects of mapping. + IProjectionConfigContinuation With(TSecondEnum secondEnumMember) + where TSecondEnum : struct; + + /// + /// Configure this mapper to project the previously-specified set of enum members to the given + /// . + /// + /// The type of the second enum being paired. + /// The second set of enum members in the pairs. + /// An IProjectionConfigContinuation with which to configure other aspects of mapping. + IProjectionConfigContinuation With(params TSecondEnum[] secondEnumMembers) + where TSecondEnum : struct; + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index 94a9129b0..cc47b6549 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -70,7 +70,5 @@ ICustomProjectionDataSourceTargetMemberSpecifier /// constant value should be applied. /// ICustomProjectionDataSourceTargetMemberSpecifier Map(TSourceValue value); - - } } \ No newline at end of file diff --git a/AgileMapper/TypeConversion/ToBoolConverter.cs b/AgileMapper/TypeConversion/ToBoolConverter.cs index 7abee406b..db029147a 100644 --- a/AgileMapper/TypeConversion/ToBoolConverter.cs +++ b/AgileMapper/TypeConversion/ToBoolConverter.cs @@ -12,7 +12,7 @@ internal class ToBoolConverter : ValueConverterBase { private static readonly Type[] _supportedSourceTypes = Constants .NumericTypes - .Append(typeof(bool), typeof(string), typeof(object), typeof(char)); + .Append(typeof(bool)); private readonly ToStringConverter _toStringConverter; @@ -22,7 +22,11 @@ public ToBoolConverter(ToStringConverter toStringConverter) } public override bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) - => (nonNullableTargetType == typeof(bool)) && _supportedSourceTypes.Contains(nonNullableSourceType); + { + return (nonNullableTargetType == typeof(bool)) && + ((_supportedSourceTypes.Contains(nonNullableSourceType)) || + _toStringConverter.HasNativeStringRepresentation(nonNullableSourceType)); + } public override Expression GetConversion(Expression sourceValue, Type targetType) { @@ -102,7 +106,7 @@ private Expression GetSourceEqualsTests(Expression sourceValue, IEnumerable nonNullableTargetType == typeof(string); + public override bool CanConvert(Type nonNullableSourceType, Type nonNullableTargetType) + => nonNullableTargetType == typeof(string); + + public bool HasNativeStringRepresentation(Type nonNullableType) + { + return (nonNullableType == typeof(string)) || + (nonNullableType == typeof(object)) || + nonNullableType.IsEnum() || + (nonNullableType == typeof(char)) || + HasToStringOperator(nonNullableType, out var _); + } + + private static bool HasToStringOperator(Type nonNullableSourceType, out MethodInfo operatorMethod) + { + operatorMethod = nonNullableSourceType + .GetOperators(o => o.To()) + .FirstOrDefault(); + + return operatorMethod != null; + } public override Expression GetConversion(Expression sourceValue, Type targetType) { @@ -20,6 +39,11 @@ public override Expression GetConversion(Expression sourceValue, Type targetType public Expression GetConversion(Expression sourceValue) { + if (sourceValue.Type == typeof(string)) + { + return sourceValue; + } + if (sourceValue.Type == typeof(byte[])) { return GetByteArrayToBase64StringConversion(sourceValue); @@ -50,15 +74,6 @@ public Expression GetConversion(Expression sourceValue) return toStringCall; } - public bool HasToStringOperator(Type nonNullableSourceType, out MethodInfo operatorMethod) - { - operatorMethod = nonNullableSourceType - .GetOperators(o => o.To()) - .FirstOrDefault(); - - return operatorMethod != null; - } - #region Byte[] Conversion private static readonly MethodInfo _toBase64String = typeof(Convert) diff --git a/AgileMapper/TypeConversion/TryParseConverter.cs b/AgileMapper/TypeConversion/TryParseConverter.cs index d4e0f8d50..5aef9833f 100644 --- a/AgileMapper/TypeConversion/TryParseConverter.cs +++ b/AgileMapper/TypeConversion/TryParseConverter.cs @@ -34,9 +34,7 @@ public override bool CanConvert(Type nonNullableSourceType, Type nonNullableTarg protected virtual bool CanConvert(Type nonNullableSourceType) { return (nonNullableSourceType == _nonNullableTargetType) || - (nonNullableSourceType == typeof(string)) || - (nonNullableSourceType == typeof(object)) || - _toStringConverter.HasToStringOperator(nonNullableSourceType, out var _); + _toStringConverter.HasNativeStringRepresentation(nonNullableSourceType); } public override Expression GetConversion(Expression sourceValue, Type targetType) From 3d07a9292299a4f46118ef34813c4b8c5019a800 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 8 Feb 2018 18:59:13 +0000 Subject: [PATCH 134/176] Ensuring string constant values are converted to lower case when converting ignore case string comparisons / Extending test enum-pairing coverage to EF5, EF6 + EF Core 1 / Tweaking DbSetWrapper implementations --- ...per.UnitTests.Orms.EfCore2.NetCore2.csproj | 2 +- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringEnumMapping.cs | 13 ++++++ .../Infrastructure/Ef5DbSetWrapper.cs | 10 ++--- .../Infrastructure/Ef5TestDbContext.cs | 45 +++++++------------ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringEnumMapping.cs | 13 ++++++ .../Infrastructure/Ef6DbSetWrapper.cs | 10 ++--- .../Infrastructure/Ef6TestDbContext.cs | 45 +++++++------------ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 9 ++-- .../WhenConfiguringEnumMapping.cs | 13 ++++++ .../Infrastructure/EfCore1DbSetWrapper.cs | 10 ++--- .../Infrastructure/EfCore1TestDbContext.cs | 45 +++++++------------ AgileMapper.UnitTests.Orms.EfCore1/app.config | 4 ++ .../packages.config | 2 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 9 +++- .../Infrastructure/EfCore2DbSetWrapper.cs | 10 ++--- .../Infrastructure/EfCore2TestDbContext.cs | 30 ++++++------- AgileMapper.UnitTests.Orms.EfCore2/app.config | 23 ++++++++++ .../packages.config | 2 +- .../WhenConfiguringEnumMapping.cs | 24 +++++++--- .../Infrastructure/DbSetWrapperBase.cs | 1 + .../Infrastructure/IDbSetWrapper.cs | 1 + .../Infrastructure/OrmTestClassBase.cs | 2 +- AgileMapper.UnitTests.Orms/TestExtensions.cs | 8 +++- .../StringEqualsIgnoreCaseConverter.cs | 16 ++++++- 26 files changed, 203 insertions(+), 146 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringEnumMapping.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringEnumMapping.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringEnumMapping.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/app.config diff --git a/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj b/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj index bb78cf9c7..8dcd8dc39 100644 --- a/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EFCore2.NetCore2/AgileMapper.UnitTests.Orms.EfCore2.NetCore2.csproj @@ -32,7 +32,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 5748cfe16..6f050d2e5 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -87,6 +87,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringEnumMapping.cs new file mode 100644 index 000000000..de013d1b4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringEnumMapping.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringEnumMapping : WhenConfiguringEnumMapping + { + public WhenConfiguringEnumMapping(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs index 9bfb6c8d5..11a0d83b4 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -11,16 +11,14 @@ public class Ef5DbSetWrapper : DbSetWrapperBase { private readonly DbSet _dbSet; - public Ef5DbSetWrapper(DbSet dbSet) - : base(dbSet) + public Ef5DbSetWrapper(DbContext context) + : base(context.Set()) { - _dbSet = dbSet; + _dbSet = context.Set(); } public override void Include(Expression> navigationPropertyPath) - { - _dbSet.Include(navigationPropertyPath); - } + => _dbSet.Include(navigationPropertyPath); public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 01b92c421..0efd654e7 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -62,50 +62,35 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members - IDbSetWrapper ITestDbContext.Companies - => new Ef5DbSetWrapper(Companies); + IDbSetWrapper ITestDbContext.Companies => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Employees - => new Ef5DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Employees => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Categories - => new Ef5DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Categories => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Products - => new Ef5DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Persons - => new Ef5DbSetWrapper(Persons); + IDbSetWrapper ITestDbContext.Persons => new Ef5DbSetWrapper(this); - IDbSetWrapper
ITestDbContext.Addresses - => new Ef5DbSetWrapper
(Addresses); + IDbSetWrapper
ITestDbContext.Addresses => new Ef5DbSetWrapper
(this); - IDbSetWrapper ITestDbContext.Rotas - => new Ef5DbSetWrapper(Rotas); + IDbSetWrapper ITestDbContext.Rotas => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.RotaEntries - => new Ef5DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.RotaEntries => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders - => new Ef5DbSetWrapper(Orders); + IDbSetWrapper ITestDbContext.Orders => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.OrderItems - => new Ef5DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.OrderItems => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.BoolItems - => new Ef5DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.ShortItems - => new Ef5DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.IntItems - => new Ef5DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.LongItems - => new Ef5DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.StringItems - => new Ef5DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems => new Ef5DbSetWrapper(this); Task ITestDbContext.SaveChanges() { diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index b14372bd8..6570e7cb1 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringEnumMapping.cs new file mode 100644 index 000000000..1e89390b6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringEnumMapping.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringEnumMapping : WhenConfiguringEnumMapping + { + public WhenConfiguringEnumMapping(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs index e2d317980..37850f10a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -10,16 +10,14 @@ public class Ef6DbSetWrapper : DbSetWrapperBase { private readonly DbSet _dbSet; - public Ef6DbSetWrapper(DbSet dbSet) - : base(dbSet) + public Ef6DbSetWrapper(DbContext context) + : base(context.Set()) { - _dbSet = dbSet; + _dbSet = context.Set(); } public override void Include(Expression> navigationPropertyPath) - { - _dbSet.Include(navigationPropertyPath); - } + => _dbSet.Include(navigationPropertyPath); public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 01ea2707c..00f99e60e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -62,50 +62,35 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members - IDbSetWrapper ITestDbContext.Companies - => new Ef6DbSetWrapper(Companies); + IDbSetWrapper ITestDbContext.Companies => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Employees - => new Ef6DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Employees => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Categories - => new Ef6DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Categories => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Products - => new Ef6DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Persons - => new Ef6DbSetWrapper(Persons); + IDbSetWrapper ITestDbContext.Persons => new Ef6DbSetWrapper(this); - IDbSetWrapper
ITestDbContext.Addresses - => new Ef6DbSetWrapper
(Addresses); + IDbSetWrapper
ITestDbContext.Addresses => new Ef6DbSetWrapper
(this); - IDbSetWrapper ITestDbContext.Rotas - => new Ef6DbSetWrapper(Rotas); + IDbSetWrapper ITestDbContext.Rotas => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.RotaEntries - => new Ef6DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.RotaEntries => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders - => new Ef6DbSetWrapper(Orders); + IDbSetWrapper ITestDbContext.Orders => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.OrderItems - => new Ef6DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.OrderItems => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.BoolItems - => new Ef6DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.ShortItems - => new Ef6DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.IntItems - => new Ef6DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.LongItems - => new Ef6DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.StringItems - => new Ef6DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(this); Task ITestDbContext.SaveChanges() => SaveChangesAsync(); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index ee1077d41..915d46afc 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -47,8 +47,8 @@ ..\AgileMapper.snk - - ..\packages\Microsoft.EntityFrameworkCore.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.dll + + ..\packages\Microsoft.EntityFrameworkCore.1.1.5\lib\net451\Microsoft.EntityFrameworkCore.dll ..\packages\Microsoft.EntityFrameworkCore.InMemory.1.1.3\lib\net451\Microsoft.EntityFrameworkCore.InMemory.dll @@ -189,7 +189,9 @@ - + + Designer + Designer @@ -213,6 +215,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringEnumMapping.cs new file mode 100644 index 000000000..a3af883c8 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringEnumMapping.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringEnumMapping : WhenConfiguringEnumMapping + { + public WhenConfiguringEnumMapping(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs index e3c7bde77..afd5fbd67 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs @@ -10,16 +10,14 @@ public class EfCore1DbSetWrapper : DbSetWrapperBase { private readonly DbSet _dbSet; - public EfCore1DbSetWrapper(DbSet dbSet) - : base(dbSet) + public EfCore1DbSetWrapper(DbContext context) + : base(context.Set()) { - _dbSet = dbSet; + _dbSet = context.Set(); } public override void Include(Expression> navigationPropertyPath) - { - _dbSet.Include(navigationPropertyPath); - } + => _dbSet.Include(navigationPropertyPath); public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 477dcbdec..d4341c1c5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -66,50 +66,35 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members - IDbSetWrapper ITestDbContext.Companies - => new EfCore1DbSetWrapper(Companies); + IDbSetWrapper ITestDbContext.Companies => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Employees - => new EfCore1DbSetWrapper(Employees); + IDbSetWrapper ITestDbContext.Employees => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Categories - => new EfCore1DbSetWrapper(Categories); + IDbSetWrapper ITestDbContext.Categories => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Products - => new EfCore1DbSetWrapper(Products); + IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Persons - => new EfCore1DbSetWrapper(Persons); + IDbSetWrapper ITestDbContext.Persons => new EfCore1DbSetWrapper(this); - IDbSetWrapper
ITestDbContext.Addresses - => new EfCore1DbSetWrapper
(Addresses); + IDbSetWrapper
ITestDbContext.Addresses => new EfCore1DbSetWrapper
(this); - IDbSetWrapper ITestDbContext.Rotas - => new EfCore1DbSetWrapper(Rotas); + IDbSetWrapper ITestDbContext.Rotas => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.RotaEntries - => new EfCore1DbSetWrapper(RotaEntries); + IDbSetWrapper ITestDbContext.RotaEntries => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders - => new EfCore1DbSetWrapper(Orders); + IDbSetWrapper ITestDbContext.Orders => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.OrderItems - => new EfCore1DbSetWrapper(OrderItems); + IDbSetWrapper ITestDbContext.OrderItems => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.BoolItems - => new EfCore1DbSetWrapper(BoolItems); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.ShortItems - => new EfCore1DbSetWrapper(ShortItems); + IDbSetWrapper ITestDbContext.ShortItems => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.IntItems - => new EfCore1DbSetWrapper(IntItems); + IDbSetWrapper ITestDbContext.IntItems => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.LongItems - => new EfCore1DbSetWrapper(LongItems); + IDbSetWrapper ITestDbContext.LongItems => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.StringItems - => new EfCore1DbSetWrapper(StringItems); + IDbSetWrapper ITestDbContext.StringItems => new EfCore1DbSetWrapper(this); Task ITestDbContext.SaveChanges() => SaveChangesAsync(); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index d1cf0bd97..1c6e20dd0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -62,6 +62,10 @@ + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/packages.config b/AgileMapper.UnitTests.Orms.EfCore1/packages.config index f2c58b94f..0b32f134e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/packages.config @@ -1,6 +1,6 @@  - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index a08ba6aa0..8b416adcf 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -47,8 +47,9 @@ ..\AgileMapper.snk - - ..\packages\Microsoft.EntityFrameworkCore.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll + + + ..\packages\Microsoft.EntityFrameworkCore.2.0.1\lib\netstandard2.0\Microsoft.EntityFrameworkCore.dll ..\packages\Microsoft.EntityFrameworkCore.InMemory.2.0.0\lib\netstandard2.0\Microsoft.EntityFrameworkCore.InMemory.dll @@ -59,6 +60,9 @@ ..\packages\Microsoft.Extensions.Caching.Memory.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Caching.Memory.dll + + ..\packages\Microsoft.Extensions.Configuration.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll + ..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll @@ -169,6 +173,7 @@ + Designer diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs index 4f197b4e7..cee4e1e7a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs @@ -10,16 +10,14 @@ public class EfCore2DbSetWrapper : DbSetWrapperBase { private readonly DbSet _dbSet; - public EfCore2DbSetWrapper(DbSet dbSet) - : base(dbSet) + public EfCore2DbSetWrapper(DbContext context) + : base(context.Set()) { - _dbSet = dbSet; + _dbSet = context.Set(); } public override void Include(Expression> navigationPropertyPath) - { - _dbSet.Include(navigationPropertyPath); - } + => _dbSet.Include(navigationPropertyPath); public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index e872a7adf..06fd53d46 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -75,49 +75,49 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Companies - => new EfCore2DbSetWrapper(Companies); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees - => new EfCore2DbSetWrapper(Employees); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Categories - => new EfCore2DbSetWrapper(Categories); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Products - => new EfCore2DbSetWrapper(Products); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Persons - => new EfCore2DbSetWrapper(Persons); + => new EfCore2DbSetWrapper(this); IDbSetWrapper
ITestDbContext.Addresses - => new EfCore2DbSetWrapper
(Addresses); + => new EfCore2DbSetWrapper
(this); IDbSetWrapper ITestDbContext.Rotas - => new EfCore2DbSetWrapper(Rotas); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.RotaEntries - => new EfCore2DbSetWrapper(RotaEntries); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Orders - => new EfCore2DbSetWrapper(Orders); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.OrderItems - => new EfCore2DbSetWrapper(OrderItems); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.BoolItems - => new EfCore2DbSetWrapper(BoolItems); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.ShortItems - => new EfCore2DbSetWrapper(ShortItems); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.IntItems - => new EfCore2DbSetWrapper(IntItems); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.LongItems - => new EfCore2DbSetWrapper(LongItems); + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.StringItems - => new EfCore2DbSetWrapper(StringItems); + => new EfCore2DbSetWrapper(this); Task ITestDbContext.SaveChanges() => SaveChangesAsync(); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/app.config b/AgileMapper.UnitTests.Orms.EfCore2/app.config new file mode 100644 index 000000000..f278537bb --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/app.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/packages.config b/AgileMapper.UnitTests.Orms.EfCore2/packages.config index 63a62f333..de00ae8a4 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/packages.config +++ b/AgileMapper.UnitTests.Orms.EfCore2/packages.config @@ -1,6 +1,6 @@  - + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs index efb08d9e2..70bb33cf4 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration { using System; + using System.Linq; using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -20,13 +21,20 @@ public Task ShouldPairEnumMembers() { return RunTest(async context => { - var order = new Order + var order1 = new Order { DatePlaced = DateTime.Now, PaymentType = PaymentTypeUk.Cheque }; - context.Orders.Add(order); + var order2 = new Order + { + DatePlaced = DateTime.Now.AddMinutes(1), + PaymentType = PaymentTypeUk.Cash + }; + + context.Orders.Add(order1); + context.Orders.Add(order2); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -38,13 +46,17 @@ public Task ShouldPairEnumMembers() .And .PairEnum(PaymentTypeUk.Card).With(PaymentTypeUs.Check); - var orderVm = context + var orderVms = context .Orders .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); + .OrderBy(o => o.DatePlaced) + .ToArray(); + + orderVms.First().DatePlaced.ShouldBe(order1.DatePlaced); + orderVms.First().PaymentType.ShouldBe(PaymentTypeUs.Check); - orderVm.DatePlaced.ShouldBe(order.DatePlaced); - orderVm.PaymentType.ShouldBe(PaymentTypeUs.Check); + orderVms.Second().DatePlaced.ShouldBe(order2.DatePlaced); + orderVms.Second().PaymentType.ShouldBe(PaymentTypeUs.Cash); } }); } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs index 561a29c1b..ac24e5388 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; + using System.Threading.Tasks; public abstract class DbSetWrapperBase : IDbSetWrapper { diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs index 361819619..fec23313a 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Linq.Expressions; + using System.Threading.Tasks; public interface IDbSetWrapper : IQueryable { diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 51b0a079a..b699b3176 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -47,7 +47,7 @@ protected async Task RunTest(Func test) } finally { - EmptyDbContext(); + await EmptyDbContext(); } } diff --git a/AgileMapper.UnitTests.Orms/TestExtensions.cs b/AgileMapper.UnitTests.Orms/TestExtensions.cs index 7aa34117b..ab7d8984f 100644 --- a/AgileMapper.UnitTests.Orms/TestExtensions.cs +++ b/AgileMapper.UnitTests.Orms/TestExtensions.cs @@ -2,11 +2,17 @@ { using System.Collections.Generic; using System.Linq; + using System.Text.RegularExpressions; - internal static class TestExtensions + public static class TestExtensions { + private static readonly Regex _tableNameMatcher = new Regex(@"FROM\s+(?.+)\s+AS"); + public static T Second(this IEnumerable items) => items.ElementAt(1); public static T Third(this IEnumerable items) => items.ElementAt(2); + + public static string GetTableName(this string traceString) + => _tableNameMatcher.Match(traceString).Groups["table"].Value; } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs index 00541a562..5e55cc24e 100644 --- a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; + using Extensions.Internal; using NetStandardPolyfills; internal static class StringEqualsIgnoreCaseConverter @@ -34,9 +35,22 @@ private static Expression Convert(MethodCallExpression methodCall) methodCall.Arguments[0], typeof(string).GetPublicInstanceMethod("ToLower", parameterCount: 0)); - var comparison = Expression.Equal(subjectToLower, methodCall.Arguments[1]); + var comparisonValue = GetComparisonValue(methodCall.Arguments[1]); + var comparison = Expression.Equal(subjectToLower, comparisonValue); return comparison; } + + private static Expression GetComparisonValue(Expression value) + { + if (value.NodeType != ExpressionType.Constant) + { + return value; + } + + var stringValue = ((ConstantExpression)value).Value?.ToString().ToLowerInvariant(); + + return stringValue.ToConstantExpression(); + } } } \ No newline at end of file From 7dac067a9f0a2ba6415bb0247e8d6a5b3441024e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 9 Feb 2018 17:12:34 +0000 Subject: [PATCH 135/176] Start of projection validation support + tests --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 2 +- .../Infrastructure/Ef5TestDbContext.cs | 4 +-- .../WhenViewingMappingPlans.cs | 12 ------- .../WhenViewingProjectionPlans.cs | 12 +++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- .../Infrastructure/Ef6TestDbContext.cs | 4 +-- .../WhenViewingMappingPlans.cs | 12 ------- .../WhenViewingProjectionPlans.cs | 12 +++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 2 +- .../Infrastructure/EfCore1TestDbContext.cs | 4 +-- .../WhenViewingMappingPlans.cs | 12 ------- .../WhenViewingProjectionPlans.cs | 12 +++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 3 +- .../Infrastructure/EfCore2TestDbContext.cs | 6 ++-- .../WhenValidatingProjections.cs | 12 +++++++ .../WhenViewingMappingPlans.cs | 12 ------- .../WhenViewingProjectionPlans.cs | 12 +++++++ .../AgileMapper.UnitTests.Orms.csproj | 7 ++-- .../WhenConfiguringEnumMapping.cs | 22 ++++++++++--- .../WhenProjectingToEnumerableMembers.cs | 2 +- .../Infrastructure/ITestDbContext.cs | 2 +- .../TestClasses/{Order.cs => OrderUk.cs} | 2 +- ...{OrderViewModel.cs => OrderUsViewModel.cs} | 2 +- .../WhenValidatingProjections.cs | 32 +++++++++++++++++++ ...Plans.cs => WhenViewingProjectionPlans.cs} | 4 +-- .../WhenValidatingMappings.cs | 2 +- 26 files changed, 134 insertions(+), 76 deletions(-) delete mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenViewingProjectionPlans.cs delete mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenViewingProjectionPlans.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenViewingProjectionPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenValidatingProjections.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenViewingProjectionPlans.cs rename AgileMapper.UnitTests.Orms/TestClasses/{Order.cs => OrderUk.cs} (94%) rename AgileMapper.UnitTests.Orms/TestClasses/{OrderViewModel.cs => OrderUsViewModel.cs} (87%) create mode 100644 AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs rename AgileMapper.UnitTests.Orms/{WhenViewingMappingPlans.cs => WhenViewingProjectionPlans.cs} (92%) diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 6f050d2e5..e93621a9d 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -104,7 +104,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 0efd654e7..59a0e4df9 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -35,7 +35,7 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet RotaEntries { get; set; } - public DbSet Orders { get; set; } + public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } @@ -78,7 +78,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.RotaEntries => new Ef5DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Orders => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.OrderItems => new Ef5DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs deleted file mode 100644 index b0eeafd3f..000000000 --- a/AgileMapper.UnitTests.Orms.Ef5/WhenViewingMappingPlans.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 -{ - using Infrastructure; - - public class WhenViewingMappingPlans : WhenViewingMappingPlans - { - public WhenViewingMappingPlans(InMemoryEf5TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenViewingProjectionPlans.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenViewingProjectionPlans.cs new file mode 100644 index 000000000..39396c1cb --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenViewingProjectionPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + + public class WhenViewingProjectionPlans : WhenViewingProjectionPlans + { + public WhenViewingProjectionPlans(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 6570e7cb1..807d94e39 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -107,7 +107,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 00f99e60e..cdd11293e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -35,7 +35,7 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet RotaEntries { get; set; } - public DbSet Orders { get; set; } + public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } @@ -78,7 +78,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.RotaEntries => new Ef6DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Orders => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.OrderItems => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs deleted file mode 100644 index d6b352c63..000000000 --- a/AgileMapper.UnitTests.Orms.Ef6/WhenViewingMappingPlans.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 -{ - using Infrastructure; - - public class WhenViewingMappingPlans : WhenViewingMappingPlans - { - public WhenViewingMappingPlans(InMemoryEf6TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenViewingProjectionPlans.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenViewingProjectionPlans.cs new file mode 100644 index 000000000..aea83afa9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenViewingProjectionPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + + public class WhenViewingProjectionPlans : WhenViewingProjectionPlans + { + public WhenViewingProjectionPlans(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 915d46afc..95eb2d5c8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -233,7 +233,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index d4341c1c5..f9a3cd449 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -33,7 +33,7 @@ public EfCore1TestDbContext() public DbSet RotaEntries { get; set; } - public DbSet Orders { get; set; } + public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } @@ -82,7 +82,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.RotaEntries => new EfCore1DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Orders => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.OrderItems => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs deleted file mode 100644 index 8f1af7427..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingMappingPlans.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 -{ - using Infrastructure; - - public class WhenViewingMappingPlans : WhenViewingMappingPlans - { - public WhenViewingMappingPlans(InMemoryEfCore1TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingProjectionPlans.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingProjectionPlans.cs new file mode 100644 index 000000000..a7b60bd6f --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenViewingProjectionPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + + public class WhenViewingProjectionPlans : WhenViewingProjectionPlans + { + public WhenViewingProjectionPlans(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 8b416adcf..9bd125773 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -164,13 +164,14 @@ + - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 06fd53d46..1a21494a8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -41,7 +41,7 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet RotaEntries { get; set; } - public DbSet Orders { get; set; } + public DbSet Orders { get; set; } public DbSet OrderItems { get; set; } @@ -98,8 +98,8 @@ IDbSetWrapper ITestDbContext.Rotas IDbSetWrapper ITestDbContext.RotaEntries => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Orders + => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.OrderItems => new EfCore2DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenValidatingProjections.cs new file mode 100644 index 000000000..9f2909606 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenValidatingProjections.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + + public class WhenValidatingProjections : WhenValidatingProjections + { + public WhenValidatingProjections(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs deleted file mode 100644 index 1ba74b7ce..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingMappingPlans.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 -{ - using Infrastructure; - - public class WhenViewingMappingPlans : WhenViewingMappingPlans - { - public WhenViewingMappingPlans(InMemoryEfCore2TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingProjectionPlans.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingProjectionPlans.cs new file mode 100644 index 000000000..494dbab5b --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenViewingProjectionPlans.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using Infrastructure; + + public class WhenViewingProjectionPlans : WhenViewingProjectionPlans + { + public WhenViewingProjectionPlans(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 79b4f1e8b..32ec834f3 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -104,11 +104,11 @@ - + - + @@ -141,7 +141,8 @@ - + + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs index 70bb33cf4..39d96d28d 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs @@ -21,42 +21,54 @@ public Task ShouldPairEnumMembers() { return RunTest(async context => { - var order1 = new Order + var order1 = new OrderUk { DatePlaced = DateTime.Now, PaymentType = PaymentTypeUk.Cheque }; - var order2 = new Order + var order2 = new OrderUk { DatePlaced = DateTime.Now.AddMinutes(1), PaymentType = PaymentTypeUk.Cash }; + var order3 = new OrderUk + { + DatePlaced = DateTime.Now.AddMinutes(2), + PaymentType = PaymentTypeUk.Card + }; + context.Orders.Add(order1); context.Orders.Add(order2); + context.Orders.Add(order3); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping - .From() - .ProjectedTo() + .From() + .ProjectedTo() .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check) .And .PairEnum(PaymentTypeUk.Card).With(PaymentTypeUs.Check); var orderVms = context .Orders - .ProjectUsing(mapper).To() + .ProjectUsing(mapper).To() .OrderBy(o => o.DatePlaced) .ToArray(); + orderVms.Length.ShouldBe(3); + orderVms.First().DatePlaced.ShouldBe(order1.DatePlaced); orderVms.First().PaymentType.ShouldBe(PaymentTypeUs.Check); orderVms.Second().DatePlaced.ShouldBe(order2.DatePlaced); orderVms.Second().PaymentType.ShouldBe(PaymentTypeUs.Cash); + + orderVms.Third().DatePlaced.ShouldBe(order3.DatePlaced); + orderVms.Third().PaymentType.ShouldBe(PaymentTypeUs.Check); } }); } diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index e51a16ab9..9b27c17d1 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -103,7 +103,7 @@ protected async Task ProjectToComplexTypeEnumerableMember(TOrmContext context) var item1 = new OrderItem(); var item2 = new OrderItem(); - var order = new Order + var order = new OrderUk { DatePlaced = DateTime.Now, Items = new List { item1, item2 } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index d5f6d12f5..0f4ccda3a 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -22,7 +22,7 @@ public interface ITestDbContext : IDisposable IDbSetWrapper RotaEntries { get; } - IDbSetWrapper Orders { get; } + IDbSetWrapper Orders { get; } IDbSetWrapper OrderItems { get; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderUk.cs similarity index 94% rename from AgileMapper.UnitTests.Orms/TestClasses/Order.cs rename to AgileMapper.UnitTests.Orms/TestClasses/OrderUk.cs index 65dca9f45..d6bed8efd 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/Order.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderUk.cs @@ -5,7 +5,7 @@ using System.ComponentModel.DataAnnotations; using UnitTests.TestClasses; - public class Order + public class OrderUk { [Key] public int Id { get; set; } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/OrderUsViewModel.cs similarity index 87% rename from AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs rename to AgileMapper.UnitTests.Orms/TestClasses/OrderUsViewModel.cs index 59ddf49b9..e3a845a71 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/OrderViewModel.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/OrderUsViewModel.cs @@ -3,7 +3,7 @@ using System; using UnitTests.TestClasses; - public class OrderViewModel + public class OrderUsViewModel { public DateTime DatePlaced { get; set; } diff --git a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs new file mode 100644 index 000000000..417743a32 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs @@ -0,0 +1,32 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenValidatingProjections : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenValidatingProjections(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldSupportCachedProjectionValidation() + { + return RunTest(context => + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanForProjecting(context.Addresses).To(); + + Should.NotThrow(() => mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); + } + + return Task.CompletedTask; + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs b/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs similarity index 92% rename from AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs rename to AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs index 2739e5937..14bf7d69a 100644 --- a/AgileMapper.UnitTests.Orms/WhenViewingMappingPlans.cs +++ b/AgileMapper.UnitTests.Orms/WhenViewingProjectionPlans.cs @@ -8,10 +8,10 @@ using TestClasses; using Xunit; - public abstract class WhenViewingMappingPlans : OrmTestClassBase + public abstract class WhenViewingProjectionPlans : OrmTestClassBase where TOrmContext : ITestDbContext, new() { - protected WhenViewingMappingPlans(ITestContext context) + protected WhenViewingProjectionPlans(ITestContext context) : base(context) { } diff --git a/AgileMapper.UnitTests/WhenValidatingMappings.cs b/AgileMapper.UnitTests/WhenValidatingMappings.cs index 66bf120ac..4c31baecc 100644 --- a/AgileMapper.UnitTests/WhenValidatingMappings.cs +++ b/AgileMapper.UnitTests/WhenValidatingMappings.cs @@ -9,7 +9,7 @@ public class WhenValidatingMappings { [Fact] - public void ShouldSupportCachedMappingMemberValidation() + public void ShouldSupportCachedMapperValidation() { using (var mapper = Mapper.CreateNew()) { From 99f6a3fad9ea46885b5904f85f621be42a8ac55d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Fri, 9 Feb 2018 19:05:56 +0000 Subject: [PATCH 136/176] Test coverage for projection unmapped member validation --- .../TestClasses/RotaEntryDto.cs | 4 ++++ .../WhenValidatingProjections.cs | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs index dd659209e..95fd32e27 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/RotaEntryDto.cs @@ -14,8 +14,12 @@ public class RotaEntryDto public int StartMinute { get; set; } + public DateTime StartTime { get; set; } + public int EndHour { get; set; } public int EndMinute { get; set; } + + public DateTime EndTime { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs index 417743a32..7a5de3457 100644 --- a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs +++ b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; + using Validation; using Xunit; public abstract class WhenValidatingProjections : OrmTestClassBase @@ -28,5 +29,28 @@ public Task ShouldSupportCachedProjectionValidation() return Task.CompletedTask; }); } + + [Fact] + public Task ShouldErrorIfCachedProjectionMembersHaveNoDataSources() + { + return RunTest(context => + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanForProjecting(context.RotaEntries).To(); + + var validationEx = Should.Throw(() => + mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); + + validationEx.Message.ShouldContain("IQueryable -> IQueryable"); + validationEx.Message.ShouldContain("Rule set: Project"); + validationEx.Message.ShouldContain("Unmapped target members"); + validationEx.Message.ShouldContain("IQueryable[i].StartTime"); + validationEx.Message.ShouldContain("IQueryable[i].EndTime"); + } + + return Task.CompletedTask; + }); + } } } From 0ba2ad49b344b023167ab425c7fbb2095ffa1264 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 08:12:21 +0000 Subject: [PATCH 137/176] Start to to-enum conversion tests + support --- .../Infrastructure/Ef5TestDbContext.cs | 8 ++ .../Infrastructure/Ef6TestDbContext.cs | 8 ++ .../Infrastructure/EfCore1TestDbContext.cs | 8 ++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Infrastructure/EfCore2TestDbContext.cs | 53 +++++------ .../WhenConvertingToEnums.cs | 13 +++ .../AgileMapper.UnitTests.Orms.csproj | 5 ++ .../Infrastructure/ITestDbContext.cs | 4 + .../Infrastructure/OrmTestClassBase.cs | 2 + .../WhenConvertingToEnums.cs | 87 +++++++++++++++++++ .../TestClasses/PublicByte.cs | 12 +++ .../TestClasses/PublicByteDto.cs | 9 ++ .../TestClasses/PublicTitle.cs | 13 +++ .../TestClasses/PublicTitleDto.cs | 11 +++ .../Converters/TryParseAssignmentConverter.cs | 3 +- 15 files changed, 206 insertions(+), 31 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs create mode 100644 AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicByte.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicByteDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicTitle.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicTitleDto.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 59a0e4df9..3456e56e4 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -41,6 +41,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet BoolItems { get; set; } + public DbSet ByteItems { get; set; } + public DbSet ShortItems { get; set; } public DbSet IntItems { get; set; } @@ -49,6 +51,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet StringItems { get; set; } + public DbSet TitleItems { get; set; } + protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder @@ -84,6 +88,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.BoolItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ByteItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ShortItems => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.IntItems => new Ef5DbSetWrapper(this); @@ -92,6 +98,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.StringItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.TitleItems => new Ef5DbSetWrapper(this); + Task ITestDbContext.SaveChanges() { SaveChanges(); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index cdd11293e..efb2ed15c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -41,6 +41,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet BoolItems { get; set; } + public DbSet ByteItems { get; set; } + public DbSet ShortItems { get; set; } public DbSet IntItems { get; set; } @@ -49,6 +51,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet StringItems { get; set; } + public DbSet TitleItems { get; set; } + protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder @@ -84,6 +88,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.BoolItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ByteItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ShortItems => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.IntItems => new Ef6DbSetWrapper(this); @@ -92,6 +98,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.TitleItems => new Ef6DbSetWrapper(this); + Task ITestDbContext.SaveChanges() => SaveChangesAsync(); #endregion diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index f9a3cd449..b9893307e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -39,6 +39,8 @@ public EfCore1TestDbContext() public DbSet BoolItems { get; set; } + public DbSet ByteItems { get; set; } + public DbSet ShortItems { get; set; } public DbSet IntItems { get; set; } @@ -47,6 +49,8 @@ public EfCore1TestDbContext() public DbSet StringItems { get; set; } + public DbSet TitleItems { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder @@ -88,6 +92,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.BoolItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ByteItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ShortItems => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.IntItems => new EfCore1DbSetWrapper(this); @@ -96,6 +102,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.StringItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.TitleItems => new EfCore1DbSetWrapper(this); + Task ITestDbContext.SaveChanges() => SaveChangesAsync(); #endregion diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 9bd125773..4b7550cbf 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -158,6 +158,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 1a21494a8..ecf077134 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -47,6 +47,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet BoolItems { get; set; } + public DbSet ByteItems { get; set; } + public DbSet ShortItems { get; set; } public DbSet IntItems { get; set; } @@ -55,6 +57,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet StringItems { get; set; } + public DbSet TitleItems { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder @@ -74,50 +78,39 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members - IDbSetWrapper ITestDbContext.Companies - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new EfCore2DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.Employees => new EfCore2DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.Categories => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Employees - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Categories - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Persons => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Products - => new EfCore2DbSetWrapper(this); + IDbSetWrapper
ITestDbContext.Addresses => new EfCore2DbSetWrapper
(this); - IDbSetWrapper ITestDbContext.Persons - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Rotas => new EfCore2DbSetWrapper(this); - IDbSetWrapper
ITestDbContext.Addresses - => new EfCore2DbSetWrapper
(this); + IDbSetWrapper ITestDbContext.RotaEntries => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Rotas - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Orders => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.RotaEntries - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.OrderItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.Orders - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.BoolItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.OrderItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ByteItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.BoolItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.ShortItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.ShortItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.IntItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.IntItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.LongItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.LongItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.StringItems => new EfCore2DbSetWrapper(this); - IDbSetWrapper ITestDbContext.StringItems - => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.TitleItems => new EfCore2DbSetWrapper(this); Task ITestDbContext.SaveChanges() => SaveChangesAsync(); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs new file mode 100644 index 000000000..405f6b8e2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToEnums : WhenConvertingToEnums + { + public WhenConvertingToEnums(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 32ec834f3..0e7dc563f 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -92,6 +92,7 @@ + @@ -113,10 +114,14 @@ + + + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 0f4ccda3a..4d6a84172 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -28,6 +28,8 @@ public interface ITestDbContext : IDisposable IDbSetWrapper BoolItems { get; } + IDbSetWrapper ByteItems { get; } + IDbSetWrapper ShortItems { get; } IDbSetWrapper IntItems { get; } @@ -36,6 +38,8 @@ public interface ITestDbContext : IDisposable IDbSetWrapper StringItems { get; } + IDbSetWrapper TitleItems { get; } + Task SaveChanges(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index b699b3176..e8fb180b3 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -64,10 +64,12 @@ private async Task EmptyDbContext() Context.Rotas.Clear(); Context.RotaEntries.Clear(); Context.BoolItems.Clear(); + Context.ByteItems.Clear(); Context.ShortItems.Clear(); Context.IntItems.Clear(); Context.LongItems.Clear(); Context.StringItems.Clear(); + Context.TitleItems.Clear(); await Context.SaveChanges(); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs new file mode 100644 index 000000000..674180f84 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -0,0 +1,87 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + using static UnitTests.TestClasses.Title; + + public abstract class WhenConvertingToEnums : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConvertingToEnums(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectAByteToAnEnum() + { + return RunTest(async context => + { + context.ByteItems.Add(new PublicByte { Value = (byte)Dr }); + await context.SaveChanges(); + + var enumItem = context.ByteItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Dr); + }); + } + + [Fact] + public Task ShouldProjectAShortToAnEnum() + { + return RunTest(async context => + { + context.ShortItems.Add(new PublicShort { Value = (short)Count }); + await context.SaveChanges(); + + var enumItem = context.ShortItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Count); + }); + } + + [Fact] + public Task ShouldProjectAnIntToAnEnum() + { + return RunTest(async context => + { + context.IntItems.Add(new PublicInt { Value = (int)Duke }); + await context.SaveChanges(); + + var enumItem = context.IntItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Duke); + }); + } + + [Fact] + public Task ShouldProjectALongToAnEnum() + { + return RunTest(async context => + { + context.LongItems.Add(new PublicLong { Value = (long)Ms }); + await context.SaveChanges(); + + var enumItem = context.LongItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Ms); + }); + } + + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() + { + return RunTest(async context => + { + context.StringItems.Add(new PublicString { Value = Mr.ToString() }); + await context.SaveChanges(); + + var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Mr); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicByte.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicByte.cs new file mode 100644 index 000000000..c48ecdb7b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicByte.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicByte + { + [Key] + public int Id { get; set; } + + public byte Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicByteDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicByteDto.cs new file mode 100644 index 000000000..28a0af9c9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicByteDto.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class PublicByteDto + { + public int Id { get; set; } + + public byte Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicTitle.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicTitle.cs new file mode 100644 index 000000000..b762e6a3d --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicTitle.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + using UnitTests.TestClasses; + + public class PublicTitle + { + [Key] + public int Id { get; set; } + + public Title Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicTitleDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicTitleDto.cs new file mode 100644 index 000000000..80f1aab3e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicTitleDto.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using UnitTests.TestClasses; + + public class PublicTitleDto + { + public int Id { get; set; } + + public Title Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs index dfc604265..b49cd0d4d 100644 --- a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs @@ -27,7 +27,8 @@ public static bool TryConvert( var tryParseOrDefault = (ConditionalExpression)finalExpression; - if (tryParseOrDefault.Test.NodeType != ExpressionType.Call) + if ((tryParseOrDefault.Test.NodeType != ExpressionType.Call) || + (tryParseOrDefault.IfTrue.NodeType != ExpressionType.Parameter)) { converted = null; return false; From 749b081b4935814ac429b87e8c238a566365a647 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 10:04:57 +0000 Subject: [PATCH 138/176] Extending stirng-to-enum projection test coverage --- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConvertingToEnums.cs | 13 +++++++++ .../WhenConvertingToEnums.cs | 29 +++++++++++++++++++ .../Converters/GetValueOrDefaultConverter.cs | 3 +- .../StringEqualsIgnoreCaseConverter.cs | 2 +- .../Converters/ToStringConverter.cs | 2 +- .../Converters/TryParseAssignmentConverter.cs | 2 +- 7 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 95eb2d5c8..e7d015ef8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -224,6 +224,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs new file mode 100644 index 000000000..3101249bd --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion +{ + using Infrastructure; + using Orms.SimpleTypeConversion; + + public class WhenConvertingToEnums : WhenConvertingToEnums + { + public WhenConvertingToEnums(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs index 674180f84..7b5592c75 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; + using UnitTests.TestClasses; using Xunit; using static UnitTests.TestClasses.Title; @@ -83,5 +84,33 @@ public Task ShouldProjectAMatchingStringToAnEnum() enumItem.Value.ShouldBe(Mr); }); } + + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() + { + return RunTest(async context => + { + context.StringItems.Add(new PublicString { Value = ((int)Mrs).ToString() }); + await context.SaveChanges(); + + var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(Mrs); + }); + } + + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() + { + return RunTest(async context => + { + context.StringItems.Add(new PublicString { Value = "Horse Pills" }); + await context.SaveChanges(); + + var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); + + enumItem.Value.ShouldBe(default(Title)); + }); + } } } diff --git a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index f267627e6..8a136f94a 100644 --- a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { + using System; using System.Linq.Expressions; using Extensions.Internal; using ReadableExpressions.Extensions; @@ -32,7 +33,7 @@ private static bool IsNotGetValueOrDefaultCall(MethodCallExpression methodCall) return methodCall.Arguments.Any() || methodCall.Method.IsStatic || !methodCall.Object.Type.IsNullableType() || - (methodCall.Method.Name != "GetValueOrDefault"); + (methodCall.Method.Name != nameof(Nullable.GetValueOrDefault)); } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs index 5e55cc24e..5427ab9a1 100644 --- a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs @@ -26,7 +26,7 @@ private static bool IsNotEqualsIgnoreCaseCall(MethodCallExpression methodCall) return !methodCall.Method.IsStatic || (methodCall.Arguments.Count != 3) || (methodCall.Method.DeclaringType != typeof(string)) || - (methodCall.Method.Name != "Equals"); + (methodCall.Method.Name != nameof(string.Equals)); } private static Expression Convert(MethodCallExpression methodCall) diff --git a/AgileMapper/Queryables/Converters/ToStringConverter.cs b/AgileMapper/Queryables/Converters/ToStringConverter.cs index 94d4711ea..653145090 100644 --- a/AgileMapper/Queryables/Converters/ToStringConverter.cs +++ b/AgileMapper/Queryables/Converters/ToStringConverter.cs @@ -24,7 +24,7 @@ private static bool IsNotToStringCall(MethodCallExpression methodCall) { return methodCall.Arguments.Any() || methodCall.Method.IsStatic || - (methodCall.Method.Name != "ToString"); + (methodCall.Method.Name != nameof(ToString)); } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs index b49cd0d4d..1fa630608 100644 --- a/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs +++ b/AgileMapper/Queryables/Converters/TryParseAssignmentConverter.cs @@ -36,7 +36,7 @@ public static bool TryConvert( var methodCall = (MethodCallExpression)tryParseOrDefault.Test; - if (!methodCall.Method.IsStatic || (methodCall.Method.Name != "TryParse")) + if (!methodCall.Method.IsStatic || (methodCall.Method.Name != nameof(int.TryParse))) { converted = null; return false; From f627f78808ac75bdd935c0f2febe5b38b3eddc94 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 10:25:26 +0000 Subject: [PATCH 139/176] Supporting EF5 + EF6 Canonical and Sql Functions in .NET Standard --- .../Settings/DefaultQueryProviderSettings.cs | 15 +++++-------- .../Settings/Ef5QueryProviderSettings.cs | 19 ++++++++++------- .../Settings/Ef6QueryProviderSettings.cs | 21 +++++++++++++------ .../Settings/IQueryProviderSettings.cs | 4 ---- .../Settings/QueryProviderSettings.cs | 6 +++--- .../QueryProviderSettingsExtensions.cs | 5 ----- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index f6611f46e..c93c50d55 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -3,12 +3,12 @@ using System; using System.Linq; using System.Linq.Expressions; + using System.Reflection; using Extensions.Internal; using NetStandardPolyfills; internal class DefaultQueryProviderSettings : IQueryProviderSettings { -#if !NET_STANDARD private readonly Lazy _canonicalFunctionsTypeLoader; private readonly Lazy _sqlFunctionsTypeLoader; @@ -25,7 +25,7 @@ public DefaultQueryProviderSettings() public Type CanonicalFunctionsType => _canonicalFunctionsTypeLoader.Value; public Type SqlFunctionsType => _sqlFunctionsTypeLoader.Value; -#endif + public virtual bool SupportsStringEqualsIgnoreCase => false; public virtual bool SupportsToString => true; @@ -94,14 +94,9 @@ private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, return convertedOrFallback; } -#if !NET_STANDARD protected static Type GetTypeOrNull(string loadedAssemblyName, string typeName) - { - return AppDomain.CurrentDomain - .GetAssemblies() - .FirstOrDefault(assembly => assembly.GetName().Name == loadedAssemblyName)? - .GetType(typeName); - } -#endif + => GetTypeOrNull(Assembly.Load(new AssemblyName(loadedAssemblyName)), typeName); + + protected static Type GetTypeOrNull(Assembly assembly, string typeName) => assembly.GetType(typeName); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 4c82246dc..5c4301b0e 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -1,12 +1,10 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { + using System; using System.Linq.Expressions; using Converters; -#if !NET_STANDARD - using System; using Extensions.Internal; using NetStandardPolyfills; -#endif internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings { @@ -18,12 +16,19 @@ internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings public override bool SupportsEnumerableMaterialisation => false; -#if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() - => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.EntityFunctions"); + { + return GetTypeOrNull( + "System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "System.Data.Objects.EntityFunctions"); + } protected override Type LoadSqlFunctionsType() - => GetTypeOrNull("System.Data.Entity", "System.Data.Objects.SqlClient.SqlFunctions"); + { + return GetTypeOrNull( + "System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "System.Data.Objects.SqlClient.SqlFunctions"); + } public override Expression ConvertToStringCall(MethodCallExpression call) { @@ -64,7 +69,7 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); -#endif + public override Expression GetDefaultValueFor(Expression value) => DefaultValueConstantExpressionFactory.CreateFor(value); } diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index 2d93d9ba3..af51b86ac 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -1,27 +1,36 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { -#if !NET_STANDARD using System; -#endif using System.Linq.Expressions; + using System.Reflection; using Converters; internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings { + private readonly Assembly _entityFrameworkAssembly; + + public Ef6QueryProviderSettings(Assembly entityFrameworkAssembly) + { + _entityFrameworkAssembly = entityFrameworkAssembly; + } + public override bool SupportsGetValueOrDefault => false; public override bool SupportsEnumerableMaterialisation => false; -#if !NET_STANDARD protected override Type LoadCanonicalFunctionsType() - => GetTypeOrNull("EntityFramework", "System.Data.Entity.DbFunctions"); + => GetTypeOrNull(_entityFrameworkAssembly, "System.Data.Entity.DbFunctions"); protected override Type LoadSqlFunctionsType() - => GetTypeOrNull("EntityFramework.SqlServer", "System.Data.Entity.SqlServer.SqlFunctions"); + { + return GetTypeOrNull( + "EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "System.Data.Entity.SqlServer.SqlFunctions"); + } protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); -#endif + public override Expression GetDefaultValueFor(Expression value) => DefaultValueConstantExpressionFactory.CreateFor(value); } diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 9894b56be..24c14eb60 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -1,17 +1,13 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { -#if !NET_STANDARD using System; -#endif using System.Linq.Expressions; internal interface IQueryProviderSettings { -#if !NET_STANDARD Type CanonicalFunctionsType { get; } Type SqlFunctionsType { get; } -#endif bool SupportsStringEqualsIgnoreCase { get; } bool SupportsToString { get; } diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs index 456df74a2..f65155166 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs @@ -6,13 +6,13 @@ internal static class QueryProviderSettings { private static readonly IQueryProviderSettings _ef5Settings = new Ef5QueryProviderSettings(); - private static readonly IQueryProviderSettings _ef6Settings = new Ef6QueryProviderSettings(); private static readonly IQueryProviderSettings _efCore2Settings = new EfCore2QueryProviderSettings(); private static readonly IQueryProviderSettings _defaultSettings = new DefaultQueryProviderSettings(); public static IQueryProviderSettings For(Type queryProviderType) { - var queryableProviderAssemblyName = queryProviderType.GetAssembly().GetName(); + var queryProviderAssembly = queryProviderType.GetAssembly(); + var queryableProviderAssemblyName = queryProviderAssembly.GetName(); var queryableProviderName = queryableProviderAssemblyName.FullName; if (queryableProviderName.Contains("EntityFrameworkCore")) @@ -28,7 +28,7 @@ public static IQueryProviderSettings For(Type queryProviderType) return _ef5Settings; case 6: - return _ef6Settings; + return new Ef6QueryProviderSettings(queryProviderAssembly); } } diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 2b34a3261..1c068cff9 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -1,13 +1,10 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { -#if !NET_STANDARD using System.Linq.Expressions; using System.Linq; using System.Reflection; - using Converters; using Extensions.Internal; using NetStandardPolyfills; -#endif using ObjectPopulation; internal static class QueryProviderSettingsExtensions @@ -25,7 +22,6 @@ public static IQueryProviderSettings GetQueryProviderSettings(this IObjectMappin return providerSettings; } -#if !NET_STANDARD public static Expression GetCreateDateTimeFromStringOrNull( this IQueryProviderSettings settings, MethodCallExpression dateTimeTryParseCall, @@ -107,6 +103,5 @@ private static Expression GetGuardedDateCreation( return createdDateOrFallback; } -#endif } } \ No newline at end of file From 189612df1952dee56eb7be18fd094103bece5923 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 12:07:19 +0000 Subject: [PATCH 140/176] Support for converting strings to enums in EF6 + EF5 / Adding legacy EF base settings class --- ...leMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 1 + .../WhenConvertingToEnums.cs | 25 ++ ...leMapper.UnitTests.Orms.Ef6.LocalDb.csproj | 1 + .../WhenConvertingToEnums.cs | 25 ++ .../WhenConvertingToEnums.cs | 11 + .../WhenConvertingToEnums.cs | 11 + .../Infrastructure/OrmTestClassBase.cs | 6 + .../WhenConvertingToEnums.cs | 9 +- .../StringEqualsIgnoreCaseConverter.cs | 32 +-- .../StringToEnumConversionConverter.cs | 38 ++++ .../Queryables/QueryProjectionModifier.cs | 5 + .../Settings/DefaultQueryProviderSettings.cs | 16 +- .../Settings/Ef5QueryProviderSettings.cs | 13 +- .../Settings/Ef6QueryProviderSettings.cs | 14 +- .../Settings/IQueryProviderSettings.cs | 9 +- .../Settings/LegacyEfQueryProviderSettings.cs | 214 ++++++++++++++++++ .../QueryProviderSettingsExtensions.cs | 87 ------- 17 files changed, 364 insertions(+), 153 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs create mode 100644 AgileMapper/Queryables/Converters/StringToEnumConversionConverter.cs create mode 100644 AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index 94b3b13da..53e2ffb20 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -85,6 +85,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs new file mode 100644 index 000000000..e791e078e --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToEnums : WhenConvertingToEnums + { + public WhenConvertingToEnums(LocalDbTestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); + + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); + + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj index fc54e51f9..ae3b39de6 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/AgileMapper.UnitTests.Orms.Ef6.LocalDb.csproj @@ -86,6 +86,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs new file mode 100644 index 000000000..2a53b85fa --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Infrastructure; + using Orms.SimpleTypeConversion; + using Xunit; + + public class WhenConvertingToEnums : WhenConvertingToEnums + { + public WhenConvertingToEnums(LocalDbTestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); + + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); + + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs index 3101249bd..3115e222c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -9,5 +11,14 @@ public WhenConvertingToEnums(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); + + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); + + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs index 405f6b8e2..bcbbf7252 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -9,5 +11,14 @@ public WhenConvertingToEnums(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); + + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); + + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index e8fb180b3..c6619e8fb 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; + using System.Diagnostics; using System.Threading.Tasks; using Xunit; @@ -30,6 +31,11 @@ protected async Task RunTest(Func test) { await test.Invoke(Context); } + catch (Exception ex) + { + Debug.WriteLine(ex); + throw; + } finally { await EmptyDbContext(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs index 7b5592c75..8761e20a9 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -71,8 +71,7 @@ public Task ShouldProjectALongToAnEnum() }); } - [Fact] - public Task ShouldProjectAMatchingStringToAnEnum() + protected Task DoShouldProjectAMatchingStringToAnEnum() { return RunTest(async context => { @@ -85,8 +84,7 @@ public Task ShouldProjectAMatchingStringToAnEnum() }); } - [Fact] - public Task ShouldProjectAMatchingNumericStringToAnEnum() + protected Task DoShouldProjectAMatchingNumericStringToAnEnum() { return RunTest(async context => { @@ -99,8 +97,7 @@ public Task ShouldProjectAMatchingNumericStringToAnEnum() }); } - [Fact] - public Task ShouldProjectANonMatchingStringToAnEnum() + protected Task DoShouldProjectANonMatchingStringToAnEnum() { return RunTest(async context => { diff --git a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs index 5427ab9a1..904ea923f 100644 --- a/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs +++ b/AgileMapper/Queryables/Converters/StringEqualsIgnoreCaseConverter.cs @@ -1,23 +1,21 @@ namespace AgileObjects.AgileMapper.Queryables.Converters { using System.Linq.Expressions; - using Extensions.Internal; - using NetStandardPolyfills; internal static class StringEqualsIgnoreCaseConverter { public static bool TryConvert( MethodCallExpression methodCall, - IQueryProjectionModifier context, + IQueryProjectionModifier modifier, out Expression converted) { - if (context.Settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) + if (modifier.Settings.SupportsStringEqualsIgnoreCase || IsNotEqualsIgnoreCaseCall(methodCall)) { converted = null; return false; } - converted = Convert(methodCall); + converted = modifier.Settings.ConvertStringEqualsIgnoreCase(methodCall); return true; } @@ -28,29 +26,5 @@ private static bool IsNotEqualsIgnoreCaseCall(MethodCallExpression methodCall) (methodCall.Method.DeclaringType != typeof(string)) || (methodCall.Method.Name != nameof(string.Equals)); } - - private static Expression Convert(MethodCallExpression methodCall) - { - var subjectToLower = Expression.Call( - methodCall.Arguments[0], - typeof(string).GetPublicInstanceMethod("ToLower", parameterCount: 0)); - - var comparisonValue = GetComparisonValue(methodCall.Arguments[1]); - var comparison = Expression.Equal(subjectToLower, comparisonValue); - - return comparison; - } - - private static Expression GetComparisonValue(Expression value) - { - if (value.NodeType != ExpressionType.Constant) - { - return value; - } - - var stringValue = ((ConstantExpression)value).Value?.ToString().ToLowerInvariant(); - - return stringValue.ToConstantExpression(); - } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Converters/StringToEnumConversionConverter.cs b/AgileMapper/Queryables/Converters/StringToEnumConversionConverter.cs new file mode 100644 index 000000000..c57ffa224 --- /dev/null +++ b/AgileMapper/Queryables/Converters/StringToEnumConversionConverter.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System.Linq.Expressions; + using NetStandardPolyfills; + + internal static class StringToEnumConversionConverter + { + public static bool TryConvert( + ConditionalExpression conditional, + IQueryProjectionModifier modifier, + out Expression converted) + { + if (modifier.Settings.SupportsStringToEnumConversion || IsNotStringToEnumConversion(conditional)) + { + converted = null; + return false; + } + + converted = modifier.Settings.ConvertStringToEnumConversion(conditional); + return true; + } + + private static bool IsNotStringToEnumConversion(ConditionalExpression conditional) + { + if (!conditional.Type.IsEnum() || + (conditional.Test.NodeType != ExpressionType.Call)) + { + return true; + } + + var testMethodCall = (MethodCallExpression)conditional.Test; + + return !testMethodCall.Method.IsStatic || + (testMethodCall.Method.DeclaringType != typeof(string)) || + testMethodCall.Method.Name != nameof(string.IsNullOrWhiteSpace); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 060346352..ec372d431 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -47,6 +47,11 @@ protected override Expression VisitConditional(ConditionalExpression conditional return Modify(converted); } + if (StringToEnumConversionConverter.TryConvert(conditional, this, out converted)) + { + return converted; + } + return base.VisitConditional(conditional); } diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index c93c50d55..7a17f4d5e 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -26,9 +26,11 @@ public DefaultQueryProviderSettings() public Type SqlFunctionsType => _sqlFunctionsTypeLoader.Value; + public virtual bool SupportsToString => true; + public virtual bool SupportsStringEqualsIgnoreCase => false; - public virtual bool SupportsToString => true; + public virtual bool SupportsStringToEnumConversion => true; public virtual bool SupportsGetValueOrDefault => true; @@ -73,10 +75,9 @@ public Expression ConvertTryParseCall(MethodCallExpression call, Expression fall return conversion; } - public virtual Expression ConvertEmptyArrayCreation(NewArrayExpression newEmptyArray) => newEmptyArray; + public virtual Expression ConvertStringEqualsIgnoreCase(MethodCallExpression call) => call; - protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) - => null; + #region ConvertTryParseCall Helpers private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) { @@ -94,6 +95,13 @@ private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, return convertedOrFallback; } + protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) + => null; + + #endregion + + public virtual Expression ConvertStringToEnumConversion(ConditionalExpression conversion) => conversion; + protected static Type GetTypeOrNull(string loadedAssemblyName, string typeName) => GetTypeOrNull(Assembly.Load(new AssemblyName(loadedAssemblyName)), typeName); diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs index 5c4301b0e..f9cde1acb 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs @@ -2,20 +2,15 @@ { using System; using System.Linq.Expressions; - using Converters; using Extensions.Internal; using NetStandardPolyfills; - internal class Ef5QueryProviderSettings : DefaultQueryProviderSettings + internal class Ef5QueryProviderSettings : LegacyEfQueryProviderSettings { public override bool SupportsToString => false; - public override bool SupportsGetValueOrDefault => false; - public override bool SupportsNonEntityNullConstants => false; - public override bool SupportsEnumerableMaterialisation => false; - protected override Type LoadCanonicalFunctionsType() { return GetTypeOrNull( @@ -66,11 +61,5 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(double?)), subject); } - - protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) - => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); - - public override Expression GetDefaultValueFor(Expression value) - => DefaultValueConstantExpressionFactory.CreateFor(value); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs index af51b86ac..793e15b64 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs @@ -1,11 +1,9 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { using System; - using System.Linq.Expressions; using System.Reflection; - using Converters; - internal class Ef6QueryProviderSettings : DefaultQueryProviderSettings + internal class Ef6QueryProviderSettings : LegacyEfQueryProviderSettings { private readonly Assembly _entityFrameworkAssembly; @@ -14,10 +12,6 @@ public Ef6QueryProviderSettings(Assembly entityFrameworkAssembly) _entityFrameworkAssembly = entityFrameworkAssembly; } - public override bool SupportsGetValueOrDefault => false; - - public override bool SupportsEnumerableMaterialisation => false; - protected override Type LoadCanonicalFunctionsType() => GetTypeOrNull(_entityFrameworkAssembly, "System.Data.Entity.DbFunctions"); @@ -27,11 +21,5 @@ protected override Type LoadSqlFunctionsType() "EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Entity.SqlServer.SqlFunctions"); } - - protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) - => this.GetCreateDateTimeFromStringOrNull(call, fallbackValue); - - public override Expression GetDefaultValueFor(Expression value) - => DefaultValueConstantExpressionFactory.CreateFor(value); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index 24c14eb60..e3d4d2f10 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -8,10 +8,13 @@ internal interface IQueryProviderSettings Type CanonicalFunctionsType { get; } Type SqlFunctionsType { get; } - bool SupportsStringEqualsIgnoreCase { get; } bool SupportsToString { get; } + bool SupportsStringEqualsIgnoreCase { get; } + + bool SupportsStringToEnumConversion { get; } + bool SupportsGetValueOrDefault { get; } bool SupportsComplexTypeToNullComparison { get; } @@ -26,6 +29,8 @@ internal interface IQueryProviderSettings Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); - Expression ConvertEmptyArrayCreation(NewArrayExpression newEmptyArray); + Expression ConvertStringEqualsIgnoreCase(MethodCallExpression call); + + Expression ConvertStringToEnumConversion(ConditionalExpression conversion); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs new file mode 100644 index 000000000..d400972ca --- /dev/null +++ b/AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs @@ -0,0 +1,214 @@ +namespace AgileObjects.AgileMapper.Queryables.Settings +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + using Converters; + using Extensions.Internal; + using NetStandardPolyfills; + + internal abstract class LegacyEfQueryProviderSettings : DefaultQueryProviderSettings + { + public override bool SupportsStringToEnumConversion => false; + + public override bool SupportsGetValueOrDefault => false; + + public override bool SupportsEnumerableMaterialisation => false; + + public override Expression GetDefaultValueFor(Expression value) + => DefaultValueConstantExpressionFactory.CreateFor(value); + + protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) + { + if ((CanonicalFunctionsType == null) || (SqlFunctionsType == null)) + { + return null; + } + + var createDateTimeMethod = CanonicalFunctionsType.GetPublicStaticMethod("CreateDateTime"); + + if (createDateTimeMethod == null) + { + return null; + } + + var datePartMethod = SqlFunctionsType + .GetPublicStaticMethods("DatePart") + .FirstOrDefault(m => m.GetParameters().All(p => p.ParameterType == typeof(string))); + + if (datePartMethod == null) + { + return null; + } + + var sourceValue = call.Arguments[0]; + + var createDateTimeCall = Expression.Call( + createDateTimeMethod, + GetDatePartCall(datePartMethod, "yy", sourceValue), + GetDatePartCall(datePartMethod, "mm", sourceValue), + GetDatePartCall(datePartMethod, "dd", sourceValue), + GetDatePartCall(datePartMethod, "hh", sourceValue), + GetDatePartCall(datePartMethod, "mi", sourceValue), + GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo()); + + fallbackValue = GetDefaultValueFor(fallbackValue); + + var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue); + + var nullString = default(string).ToConstantExpression(); + var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); + var convertedOrFallback = Expression.Condition(sourceIsNotNull, createdDateTime, fallbackValue); + + return convertedOrFallback; + } + + #region GetCreateDateTimeFromStringOrNull Helpers + + private static Expression GetDatePartCall( + MethodInfo datePartMethod, + string datePart, + Expression sourceValue) + { + return Expression.Call(datePartMethod, datePart.ToConstantExpression(), sourceValue); + } + + private Expression GetGuardedDateCreation( + Expression createDateTimeCall, + Expression sourceValue, + Expression fallbackValue) + { + var createdDateTime = Expression.Property(createDateTimeCall, "Value"); + + var isDateMethod = SqlFunctionsType.GetPublicStaticMethod("IsDate"); + + if (isDateMethod == null) + { + return createDateTimeCall; + } + + var isDateCall = Expression.Call(isDateMethod, sourceValue); + var one = 1.ToConstantExpression(typeof(int?)); + var isDateIsTrue = Expression.Equal(isDateCall, one); + var createdDateOrFallback = Expression.Condition(isDateIsTrue, createdDateTime, fallbackValue); + + return createdDateOrFallback; + } + + #endregion + + public override Expression ConvertStringEqualsIgnoreCase(MethodCallExpression call) + { + var subjectToLower = Expression.Call( + call.Arguments[0], + typeof(string).GetPublicInstanceMethod("ToLower", parameterCount: 0)); + + var comparisonValue = GetComparisonValue(call.Arguments[1]); + var comparison = Expression.Equal(subjectToLower, comparisonValue); + + return comparison; + } + + #region GetStringEqualsIgnoreCaseConversion Helpers + + private static Expression GetComparisonValue(Expression value) + { + if (value.NodeType != ExpressionType.Constant) + { + return value; + } + + var stringValue = ((ConstantExpression)value).Value?.ToString().ToLowerInvariant(); + + return stringValue.ToConstantExpression(); + } + + #endregion + + public override Expression ConvertStringToEnumConversion(ConditionalExpression conversion) + { + var enumType = conversion.Type; + var underlyingEnumType = Enum.GetUnderlyingType(enumType); + + var enumNumericValuesByField = enumType.GetPublicStaticFields().ToDictionary( + f => f, + f => Convert.ChangeType(f.GetValue(null), underlyingEnumType).ToString().ToConstantExpression()); + + var fallbackValue = GetDefaultValueFor(conversion.IfTrue); + + while (IsNotStringEqualsCheck(conversion.Test)) + { + conversion = (ConditionalExpression)conversion.IfFalse; + } + + var stringEqualsCheck = (MethodCallExpression)conversion.Test; + var stringValue = stringEqualsCheck.Arguments.First(); + + var stringToEnumConversions = new List(enumNumericValuesByField.Count); + + while (true) + { + if (conversion.IfFalse.NodeType == ExpressionType.Default) + { + break; + } + + var conversionTest = ConvertStringEqualsIgnoreCase((MethodCallExpression)conversion.Test); + + stringToEnumConversions.Insert(0, new StringToEnumConversion(conversionTest, conversion.IfTrue)); + + conversion = (ConditionalExpression)conversion.IfFalse; + } + + var noParseConversion = stringToEnumConversions.Aggregate( + fallbackValue, + (conversionSoFar, stringToEnumConversion) => + { + var enumNumericValue = enumNumericValuesByField[stringToEnumConversion.EnumValueField]; + var stringIsNumericValue = Expression.Equal(stringValue, enumNumericValue); + + return Expression.Condition( + Expression.OrElse(stringIsNumericValue, stringToEnumConversion.Test), + stringToEnumConversion.EnumValue, + conversionSoFar); + }); + + return noParseConversion; + } + + #region GetNoParseStringToEnumConversion Helpers + + private static bool IsNotStringEqualsCheck(Expression test) + { + if (test.NodeType != ExpressionType.Call) + { + return true; + } + + var testMethodCall = (MethodCallExpression)test; + + return !testMethodCall.Method.IsStatic || + (testMethodCall.Method.DeclaringType != typeof(string)) || + (testMethodCall.Method.Name != nameof(string.Equals)); + } + + private class StringToEnumConversion + { + public StringToEnumConversion(Expression test, Expression enumValue) + { + Test = test; + EnumValue = (MemberExpression)enumValue; + } + + public Expression Test { get; } + + public MemberExpression EnumValue { get; } + + public FieldInfo EnumValueField => (FieldInfo)EnumValue.Member; + } + + #endregion + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs index 1c068cff9..24ff28ab2 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettingsExtensions.cs @@ -1,10 +1,5 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { - using System.Linq.Expressions; - using System.Linq; - using System.Reflection; - using Extensions.Internal; - using NetStandardPolyfills; using ObjectPopulation; internal static class QueryProviderSettingsExtensions @@ -21,87 +16,5 @@ public static IQueryProviderSettings GetQueryProviderSettings(this IObjectMappin return providerSettings; } - - public static Expression GetCreateDateTimeFromStringOrNull( - this IQueryProviderSettings settings, - MethodCallExpression dateTimeTryParseCall, - Expression fallbackValue) - { - if ((settings.CanonicalFunctionsType == null) || (settings.SqlFunctionsType == null)) - { - return null; - } - - var createDateTimeMethod = settings - .CanonicalFunctionsType - .GetPublicStaticMethod("CreateDateTime"); - - if (createDateTimeMethod == null) - { - return null; - } - - var datePartMethod = settings - .SqlFunctionsType - .GetPublicStaticMethods("DatePart") - .FirstOrDefault(m => m.GetParameters().All(p => p.ParameterType == typeof(string))); - - if (datePartMethod == null) - { - return null; - } - - var sourceValue = dateTimeTryParseCall.Arguments[0]; - - var createDateTimeCall = Expression.Call( - createDateTimeMethod, - GetDatePartCall(datePartMethod, "yy", sourceValue), - GetDatePartCall(datePartMethod, "mm", sourceValue), - GetDatePartCall(datePartMethod, "dd", sourceValue), - GetDatePartCall(datePartMethod, "hh", sourceValue), - GetDatePartCall(datePartMethod, "mi", sourceValue), - GetDatePartCall(datePartMethod, "ss", sourceValue).GetConversionTo()); - - fallbackValue = settings.GetDefaultValueFor(fallbackValue); - - var createdDateTime = GetGuardedDateCreation(createDateTimeCall, sourceValue, fallbackValue, settings); - - var nullString = default(string).ToConstantExpression(); - var sourceIsNotNull = Expression.NotEqual(sourceValue, nullString); - var convertedOrFallback = Expression.Condition(sourceIsNotNull, createdDateTime, fallbackValue); - - return convertedOrFallback; - } - - private static Expression GetDatePartCall( - MethodInfo datePartMethod, - string datePart, - Expression sourceValue) - { - return Expression.Call(datePartMethod, datePart.ToConstantExpression(), sourceValue); - } - - private static Expression GetGuardedDateCreation( - Expression createDateTimeCall, - Expression sourceValue, - Expression fallbackValue, - IQueryProviderSettings settings) - { - var createdDateTime = Expression.Property(createDateTimeCall, "Value"); - - var isDateMethod = settings.SqlFunctionsType.GetPublicStaticMethod("IsDate"); - - if (isDateMethod == null) - { - return createDateTimeCall; - } - - var isDateCall = Expression.Call(isDateMethod, sourceValue); - var one = 1.ToConstantExpression(typeof(int?)); - var isDateIsTrue = Expression.Equal(isDateCall, one); - var createdDateOrFallback = Expression.Condition(isDateIsTrue, createdDateTime, fallbackValue); - - return createdDateOrFallback; - } } } \ No newline at end of file From a6dd3a64ec0176aa181bf2bcc7bf0f49841df728 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 16:53:59 +0000 Subject: [PATCH 141/176] Extending mapping validation test coverage --- .../WhenConvertingToEnums.cs | 11 ---------- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenValidatingProjections.cs | 12 ++++++++++ .../WhenConvertingToEnums.cs | 11 ---------- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenValidatingProjections.cs | 12 ++++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConvertingToEnums.cs | 11 ---------- .../WhenValidatingProjections.cs | 12 ++++++++++ .../WhenConvertingToEnums.cs | 11 ---------- .../WhenConvertingToEnums.cs | 9 +++++--- .../WhenValidatingProjections.cs | 22 +++++++++++++++++++ .../Ef5QueryProviderSettings.cs | 2 +- .../Ef6QueryProviderSettings.cs | 2 +- .../EfCore2QueryProviderSettings.cs | 2 +- .../LegacyEfQueryProviderSettings.cs | 2 +- .../Settings/QueryProviderSettings.cs | 1 + 17 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenValidatingProjections.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenValidatingProjections.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenValidatingProjections.cs rename AgileMapper/Queryables/Settings/{ => EntityFramework}/Ef5QueryProviderSettings.cs (96%) rename AgileMapper/Queryables/Settings/{ => EntityFramework}/Ef6QueryProviderSettings.cs (91%) rename AgileMapper/Queryables/Settings/{ => EntityFramework}/EfCore2QueryProviderSettings.cs (87%) rename AgileMapper/Queryables/Settings/{ => EntityFramework}/LegacyEfQueryProviderSettings.cs (99%) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs index e791e078e..779292894 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,10 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { - using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; - using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -12,14 +10,5 @@ public WhenConvertingToEnums(LocalDbTestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); - - [Fact] - public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); - - [Fact] - public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index e93621a9d..5bdbce79c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -104,6 +104,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenValidatingProjections.cs new file mode 100644 index 000000000..567452973 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenValidatingProjections.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using Infrastructure; + + public class WhenValidatingProjections : WhenValidatingProjections + { + public WhenValidatingProjections(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs index 2a53b85fa..2cdc0fd75 100644 --- a/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.Ef6.LocalDb/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,10 +1,8 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.LocalDb.SimpleTypeConversion { - using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; - using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -12,14 +10,5 @@ public WhenConvertingToEnums(LocalDbTestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); - - [Fact] - public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); - - [Fact] - public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 807d94e39..a1ddbc8e2 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -107,6 +107,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenValidatingProjections.cs new file mode 100644 index 000000000..34fef99c7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenValidatingProjections.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using Infrastructure; + + public class WhenValidatingProjections : WhenValidatingProjections + { + public WhenValidatingProjections(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index e7d015ef8..a7f08d82e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -234,6 +234,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs index 3115e222c..3101249bd 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { - using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; - using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -11,14 +9,5 @@ public WhenConvertingToEnums(InMemoryEfCore1TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); - - [Fact] - public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); - - [Fact] - public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenValidatingProjections.cs new file mode 100644 index 000000000..dbeb19c87 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenValidatingProjections.cs @@ -0,0 +1,12 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using Infrastructure; + + public class WhenValidatingProjections : WhenValidatingProjections + { + public WhenValidatingProjections(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs index bcbbf7252..405f6b8e2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { - using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; - using Xunit; public class WhenConvertingToEnums : WhenConvertingToEnums { @@ -11,14 +9,5 @@ public WhenConvertingToEnums(InMemoryEfCore2TestContext context) : base(context) { } - - [Fact] - public Task ShouldProjectAMatchingStringToAnEnum() => DoShouldProjectAMatchingStringToAnEnum(); - - [Fact] - public Task ShouldProjectAMatchingNumericStringToAnEnum() => DoShouldProjectAMatchingNumericStringToAnEnum(); - - [Fact] - public Task ShouldProjectANonMatchingStringToAnEnum() => DoShouldProjectANonMatchingStringToAnEnum(); } } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs index 8761e20a9..7b5592c75 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -71,7 +71,8 @@ public Task ShouldProjectALongToAnEnum() }); } - protected Task DoShouldProjectAMatchingStringToAnEnum() + [Fact] + public Task ShouldProjectAMatchingStringToAnEnum() { return RunTest(async context => { @@ -84,7 +85,8 @@ protected Task DoShouldProjectAMatchingStringToAnEnum() }); } - protected Task DoShouldProjectAMatchingNumericStringToAnEnum() + [Fact] + public Task ShouldProjectAMatchingNumericStringToAnEnum() { return RunTest(async context => { @@ -97,7 +99,8 @@ protected Task DoShouldProjectAMatchingNumericStringToAnEnum() }); } - protected Task DoShouldProjectANonMatchingStringToAnEnum() + [Fact] + public Task ShouldProjectANonMatchingStringToAnEnum() { return RunTest(async context => { diff --git a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs index 7a5de3457..446b1a54c 100644 --- a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs +++ b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs @@ -52,5 +52,27 @@ public Task ShouldErrorIfCachedProjectionMembersHaveNoDataSources() return Task.CompletedTask; }); } + + [Fact] + public Task ShouldErrorIfCachedProjectionTargetTypeIsUnconstructable() + { + return RunTest(context => + { + using (var mapper = Mapper.CreateNew()) + { + mapper.GetPlanForProjecting(context.Addresses).To(); + + var validationEx = Should.Throw(() => + mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); + + validationEx.Message.ShouldContain("IQueryable
-> IQueryable"); + validationEx.Message.ShouldContain("Rule set: Project"); + validationEx.Message.ShouldContain("Unconstructable target Types"); + validationEx.Message.ShouldContain("Address -> ProductStruct"); + } + + return Task.CompletedTask; + }); + } } } diff --git a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs similarity index 96% rename from AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs rename to AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs index f9cde1acb..647bfe818 100644 --- a/AgileMapper/Queryables/Settings/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables.Settings +namespace AgileObjects.AgileMapper.Queryables.Settings.EntityFramework { using System; using System.Linq.Expressions; diff --git a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/Ef6QueryProviderSettings.cs similarity index 91% rename from AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs rename to AgileMapper/Queryables/Settings/EntityFramework/Ef6QueryProviderSettings.cs index 793e15b64..77813a0cd 100644 --- a/AgileMapper/Queryables/Settings/Ef6QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/Ef6QueryProviderSettings.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables.Settings +namespace AgileObjects.AgileMapper.Queryables.Settings.EntityFramework { using System; using System.Reflection; diff --git a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs similarity index 87% rename from AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs rename to AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs index 9b28885d5..96c1227fe 100644 --- a/AgileMapper/Queryables/Settings/EfCore2QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables.Settings +namespace AgileObjects.AgileMapper.Queryables.Settings.EntityFramework { internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings { diff --git a/AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs similarity index 99% rename from AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs rename to AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs index d400972ca..d6ee394e2 100644 --- a/AgileMapper/Queryables/Settings/LegacyEfQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.Queryables.Settings +namespace AgileObjects.AgileMapper.Queryables.Settings.EntityFramework { using System; using System.Collections.Generic; diff --git a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs index f65155166..2c38f6f83 100644 --- a/AgileMapper/Queryables/Settings/QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/QueryProviderSettings.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables.Settings { using System; + using EntityFramework; using NetStandardPolyfills; internal static class QueryProviderSettings From e8cfd566982400c1aed48ce9bc3889544f9a6e3d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sat, 10 Feb 2018 19:10:20 +0000 Subject: [PATCH 142/176] Start of support for derived types configuration --- .../Configuration/WhenIgnoringMembers.cs | 16 ---- .../Infrastructure/Ef5DbSetWrapper.cs | 18 +++- .../Infrastructure/Ef5TestDbContext.cs | 4 + .../Configuration/WhenIgnoringMembers.cs | 16 ---- .../Infrastructure/Ef6DbSetWrapper.cs | 15 ++- .../Infrastructure/Ef6TestDbContext.cs | 4 + .../Configuration/WhenIgnoringMembers.cs | 16 ---- .../Infrastructure/EfCore1DbSetWrapper.cs | 5 +- .../Infrastructure/EfCore1TestDbContext.cs | 4 + AgileMapper.UnitTests.Orms.EfCore1/app.config | 20 ---- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringDerivedTypes.cs | 13 +++ .../Configuration/WhenIgnoringMembers.cs | 16 ---- .../Infrastructure/EfCore2DbSetWrapper.cs | 5 +- .../Infrastructure/EfCore2TestDbContext.cs | 4 + .../AgileMapper.UnitTests.Orms.csproj | 6 ++ .../WhenConfiguringConstructorDataSources.cs | 4 +- .../WhenConfiguringDataSources.cs | 22 ++--- .../WhenConfiguringDerivedTypes.cs | 68 +++++++++++++ .../WhenConfiguringEnumMapping.cs | 4 +- .../Configuration/WhenIgnoringMembers.cs | 22 +++-- .../WhenProjectingToEnumerableMembers.cs | 4 +- .../Infrastructure/DbSetWrapperBase.cs | 4 +- .../Infrastructure/IDbSetWrapper.cs | 4 +- .../Infrastructure/ITestDbContext.cs | 2 + .../Infrastructure/OrmTestClassBase.cs | 1 + .../WhenProjectingCircularReferences.cs | 6 +- .../WhenConvertingToBools.cs | 18 ++-- .../WhenConvertingToDateTimes.cs | 6 +- .../WhenConvertingToDoubles.cs | 10 +- .../WhenConvertingToEnums.cs | 14 +-- .../WhenConvertingToGuids.cs | 4 +- .../WhenConvertingToInts.cs | 14 +-- .../WhenConvertingToStrings.cs | 4 +- .../TestClasses/Animal.cs | 23 +++++ .../TestClasses/AnimalDtoBase.cs | 11 +++ .../TestClasses/DogDto.cs | 7 ++ .../TestClasses/ElephantDto.cs | 7 ++ .../TestClasses/SnakeDto.cs | 7 ++ .../WhenProjectingFlatTypes.cs | 7 +- .../WhenProjectingToComplexTypeMembers.cs | 4 +- .../WhenProjectingToFlatTypes.cs | 3 +- .../DerivedPairTargetTypeSpecifier.cs | 37 +++---- .../IConditionalRootMappingConfigurator.cs | 4 +- .../Configuration/IFullMappingConfigurator.cs | 6 +- .../IMappingDerivedPairTargetTypeSpecifier.cs | 27 ++++++ .../Api/Configuration/MappingConfigurator.cs | 15 ++- .../IConditionalRootProjectionConfigurator.cs | 11 +++ ...rojectionDerivedPairTargetTypeSpecifier.cs | 27 ++++++ .../ComplexTypeConstructionFactory.cs | 2 +- .../ComplexTypeMappingExpressionFactory.cs | 5 + .../DerivedComplexTypeMappingsFactory.cs | 96 ++++++++++++++++--- .../EnumerablePopulationBuilder.cs | 30 +++--- .../MappingExpressionFactoryBase.cs | 17 ++-- .../Queryables/QueryProjectionModifier.cs | 11 --- CommonAssemblyInfo.cs | 7 +- 56 files changed, 487 insertions(+), 251 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Animal.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs create mode 100644 AgileMapper/Api/Configuration/IMappingDerivedPairTargetTypeSpecifier.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionDerivedPairTargetTypeSpecifier.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs index 00a2f57d9..810d93eec 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenIgnoringMembers.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration { - using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; - using Xunit; public class WhenIgnoringMembers : WhenIgnoringMembers { @@ -11,19 +9,5 @@ public WhenIgnoringMembers(InMemoryEf5TestContext context) : base(context) { } - - [Fact] - public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); - - [Fact] - public Task ShouldIgnoreAConfiguredMemberConditionally() => - DoShouldIgnoreAConfiguredMemberConditionally(); - - [Fact] - public Task ShouldIgnorePropertiesByPropertyInfoMatcher() - => DoShouldIgnorePropertiesByPropertyInfoMatcher(); - - [Fact] - public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs index 11a0d83b4..74eb8f10c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5DbSetWrapper.cs @@ -4,6 +4,7 @@ using System.Data.Entity; using System.Linq; using System.Linq.Expressions; + using System.Threading.Tasks; using Orms.Infrastructure; public class Ef5DbSetWrapper : DbSetWrapperBase @@ -20,7 +21,22 @@ public Ef5DbSetWrapper(DbContext context) public override void Include(Expression> navigationPropertyPath) => _dbSet.Include(navigationPropertyPath); - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + public override Task Add(TEntity itemToAdd) + { + _dbSet.Add(itemToAdd); + + return Task.CompletedTask; + } + + public override Task AddRange(TEntity[] itemsToAdd) + { + foreach (var entity in itemsToAdd) + { + _dbSet.Add(entity); + } + + return Task.CompletedTask; + } public override void Clear() { diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 3456e56e4..3b5d0b1db 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -19,6 +19,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) { } + public DbSet Animals { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -66,6 +68,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Animals => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees => new Ef5DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs index f6c1f1e8f..6efe8f2ed 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenIgnoringMembers.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration { - using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; - using Xunit; public class WhenIgnoringMembers : WhenIgnoringMembers { @@ -11,19 +9,5 @@ public WhenIgnoringMembers(InMemoryEf6TestContext context) : base(context) { } - - [Fact] - public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); - - [Fact] - public Task ShouldIgnoreAConfiguredMemberConditionally() => - DoShouldIgnoreAConfiguredMemberConditionally(); - - [Fact] - public Task ShouldIgnorePropertiesByPropertyInfoMatcher() - => DoShouldIgnorePropertiesByPropertyInfoMatcher(); - - [Fact] - public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs index 37850f10a..8e547c2c9 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6DbSetWrapper.cs @@ -3,6 +3,7 @@ using System; using System.Data.Entity; using System.Linq.Expressions; + using System.Threading.Tasks; using Orms.Infrastructure; public class Ef6DbSetWrapper : DbSetWrapperBase @@ -19,7 +20,19 @@ public Ef6DbSetWrapper(DbContext context) public override void Include(Expression> navigationPropertyPath) => _dbSet.Include(navigationPropertyPath); - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + public override Task Add(TEntity itemToAdd) + { + _dbSet.Add(itemToAdd); + + return Task.CompletedTask; + } + + public override Task AddRange(TEntity[] itemsToAdd) + { + _dbSet.AddRange(itemsToAdd); + + return Task.CompletedTask; + } public override void Clear() => _dbSet.RemoveRange(_dbSet); } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index efb2ed15c..52dfbf659 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -19,6 +19,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) { } + public DbSet Animals { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -66,6 +68,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Animals => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs index a01f9b5ae..7a9aeedb0 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenIgnoringMembers.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration { - using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; - using Xunit; public class WhenIgnoringMembers : WhenIgnoringMembers { @@ -11,19 +9,5 @@ public WhenIgnoringMembers(InMemoryEfCore1TestContext context) : base(context) { } - - [Fact] - public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); - - [Fact] - public Task ShouldIgnoreAConfiguredMemberConditionally() => - DoShouldIgnoreAConfiguredMemberConditionally(); - - [Fact] - public Task ShouldIgnorePropertiesByPropertyInfoMatcher() - => DoShouldIgnorePropertiesByPropertyInfoMatcher(); - - [Fact] - public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs index afd5fbd67..408512241 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1DbSetWrapper.cs @@ -2,6 +2,7 @@ { using System; using System.Linq.Expressions; + using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; @@ -19,7 +20,9 @@ public EfCore1DbSetWrapper(DbContext context) public override void Include(Expression> navigationPropertyPath) => _dbSet.Include(navigationPropertyPath); - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + public override Task Add(TEntity itemToAdd) => _dbSet.AddAsync(itemToAdd); + + public override Task AddRange(TEntity[] itemsToAdd) => _dbSet.AddRangeAsync(itemsToAdd); public override void Clear() => _dbSet.RemoveRange(_dbSet); } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index b9893307e..d1439733d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -17,6 +17,8 @@ public EfCore1TestDbContext() { } + public DbSet Animals { get; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -70,6 +72,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Animals => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/app.config b/AgileMapper.UnitTests.Orms.EfCore1/app.config index 1c6e20dd0..70b68dd93 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/app.config +++ b/AgileMapper.UnitTests.Orms.EfCore1/app.config @@ -2,26 +2,6 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 4b7550cbf..aaf732d31 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -152,6 +152,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs new file mode 100644 index 000000000..5cde12c41 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringDerivedTypes : WhenConfiguringDerivedTypes + { + public WhenConfiguringDerivedTypes(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs index 573f5031e..a4fd70d27 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenIgnoringMembers.cs @@ -1,9 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { - using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; - using Xunit; public class WhenIgnoringMembers : WhenIgnoringMembers { @@ -11,19 +9,5 @@ public WhenIgnoringMembers(InMemoryEfCore2TestContext context) : base(context) { } - - [Fact] - public Task ShouldIgnoreAConfiguredMember() => DoShouldIgnoreAConfiguredMember(); - - [Fact] - public Task ShouldIgnoreAConfiguredMemberConditionally() => - DoShouldIgnoreAConfiguredMemberConditionally(); - - [Fact] - public Task ShouldIgnorePropertiesByPropertyInfoMatcher() - => DoShouldIgnorePropertiesByPropertyInfoMatcher(); - - [Fact] - public Task ShouldIgnoreMembersByTypeAndTargetType() => DoShouldIgnoreMembersByTypeAndTargetType(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs index cee4e1e7a..cfc6da522 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2DbSetWrapper.cs @@ -2,6 +2,7 @@ { using System; using System.Linq.Expressions; + using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Orms.Infrastructure; @@ -19,7 +20,9 @@ public EfCore2DbSetWrapper(DbContext context) public override void Include(Expression> navigationPropertyPath) => _dbSet.Include(navigationPropertyPath); - public override void Add(TEntity itemToAdd) => _dbSet.Add(itemToAdd); + public override Task Add(TEntity itemToAdd) => _dbSet.AddAsync(itemToAdd); + + public override Task AddRange(TEntity[] itemsToAdd) => _dbSet.AddRangeAsync(itemsToAdd); public override void Clear() => _dbSet.RemoveRange(_dbSet); } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index ecf077134..d4b063485 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -25,6 +25,8 @@ protected EfCore2TestDbContext(DbContextOptions options) { } + public DbSet Animals { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -78,6 +80,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members + IDbSetWrapper ITestDbContext.Animals => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees => new EfCore2DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 0e7dc563f..4cbf7306a 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -74,6 +74,7 @@ Properties\VersionInfo.cs + @@ -99,10 +100,14 @@ + + + + @@ -126,6 +131,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs index 6fc278ab8..5fed9f813 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs @@ -24,7 +24,7 @@ private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmCo { var product = new Product { Name = "Prod.One" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -58,7 +58,7 @@ private static async Task DoShouldApplyAConfiguredExpressionByParameterName(TOrm { var product = new Product { Name = "Prod.One" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 4f4caa541..8f2af3bae 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -21,7 +21,7 @@ public Task ShouldApplyAConfiguredConstant() { var product = new Product { Name = "P1" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -51,8 +51,7 @@ public Task ShouldConditionallyApplyAConfiguredConstant() var product1 = new Product { Name = "P1" }; var product2 = new Product { Name = "P2" }; - context.Products.Add(product1); - context.Products.Add(product2); + await context.Products.AddRange(product1, product2); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -92,7 +91,7 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() Address = new Address { Line1 = "Line 1", Postcode = "Postcode" } }; - context.Persons.Add(person); + await context.Persons.Add(person); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -123,7 +122,7 @@ protected Task DoShouldApplyAConfiguredMember() { var product = new Product { Name = "P1" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -151,7 +150,7 @@ protected Task DoShouldApplyMultipleConfiguredMembers() { var product = new Product { Name = "Product1" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -186,8 +185,7 @@ public Task ShouldConditionallyApplyAConfiguredMember() var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; - context.Products.Add(product1); - context.Products.Add(product2); + await context.Products.AddRange(product1, product2); await context.SaveChanges(); var product1Id = product1.ProductId; @@ -226,8 +224,7 @@ public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; - context.Products.Add(product1); - context.Products.Add(product2); + await context.Products.AddRange(product1, product2); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -267,8 +264,7 @@ public Task ShouldHandleANullMemberInACondition() var person1 = new Person { Name = "Frank", Address = new Address { Line1 = "Philly" } }; var person2 = new Person { Name = "Dee" }; - context.Persons.Add(person1); - context.Persons.Add(person2); + await context.Persons.AddRange(person1, person2); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -312,7 +308,7 @@ public Task ShouldSupportMultipleDivergedMappers() { var address = new Address { Line1 = "Philly", Postcode = "PH1 1LY" }; - context.Addresses.Add(address); + await context.Addresses.Add(address); await context.SaveChanges(); using (var mapper1 = Mapper.CreateNew()) diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs new file mode 100644 index 000000000..6c56be38b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -0,0 +1,68 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + using static TestClasses.Animal.AnimalType; + + public abstract class WhenConfiguringDerivedTypes : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringDerivedTypes(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToConfiguredDerivedTypes() + { + return RunTest(async context => + { + var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; + var nelly = new Animal { Type = Elephant, Name = "Nelly" }; + var kaa = new Animal { Type = Snake, Name = "Kaa" }; + + await context.Animals.AddRange(fido, nelly, kaa); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type == Elephant) + .MapTo() + .And + .If(a => a.Type == Snake) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("Trumpet"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(kaa.Id); + animalDtos.Third().Name.ShouldBe("Kaa"); + animalDtos.Third().Sound.ShouldBe("Hiss"); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs index 39d96d28d..2a35fee21 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs @@ -39,9 +39,7 @@ public Task ShouldPairEnumMembers() PaymentType = PaymentTypeUk.Card }; - context.Orders.Add(order1); - context.Orders.Add(order2); - context.Orders.Add(order3); + await context.Orders.AddRange(order1, order2, order3); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index c6f6984af..2173ab1b5 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; + using Xunit; public abstract class WhenIgnoringMembers : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -13,13 +14,14 @@ protected WhenIgnoringMembers(ITestContext context) { } - protected Task DoShouldIgnoreAConfiguredMember() + [Fact] + public Task DoShouldIgnoreAConfiguredMember() { return RunTest(async context => { var product = new Product { Name = "P1" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -40,15 +42,15 @@ protected Task DoShouldIgnoreAConfiguredMember() }); } - protected Task DoShouldIgnoreAConfiguredMemberConditionally() + [Fact] + public Task DoShouldIgnoreAConfiguredMemberConditionally() { return RunTest(async context => { var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; - context.Products.Add(product1); - context.Products.Add(product2); + await context.Products.AddRange(product1, product2); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -73,7 +75,8 @@ protected Task DoShouldIgnoreAConfiguredMemberConditionally() }); } - protected Task DoShouldIgnoreMembersByTypeAndTargetType() + [Fact] + public Task DoShouldIgnoreMembersByTypeAndTargetType() { return RunTest(async context => { @@ -83,7 +86,7 @@ protected Task DoShouldIgnoreMembersByTypeAndTargetType() Address = new Address { Line1 = "1", Line2 = "2", Postcode = "3" } }; - context.Persons.Add(person); + await context.Persons.Add(person); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -110,7 +113,8 @@ protected Task DoShouldIgnoreMembersByTypeAndTargetType() }); } - protected Task DoShouldIgnorePropertiesByPropertyInfoMatcher() + [Fact] + public Task DoShouldIgnorePropertiesByPropertyInfoMatcher() { return RunTest(async context => { @@ -120,7 +124,7 @@ protected Task DoShouldIgnorePropertiesByPropertyInfoMatcher() Address = new Address { Line1 = "1", Line2 = "2", Postcode = "3" } }; - context.Persons.Add(person); + await context.Persons.Add(person); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs index 9b27c17d1..652919319 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs @@ -62,7 +62,7 @@ private static async Task ProjectToComplexTypeCollectionMember(TOrmContext conte Entries = new List { rotaEntry1, rotaEntry2, rotaEntry3 } }; - context.Rotas.Add(rota); + await context.Rotas.Add(rota); await context.SaveChanges(); var rotaDto = context.Rotas.Where(r => r.Id == 1).Project().To().First(); @@ -109,7 +109,7 @@ protected async Task ProjectToComplexTypeEnumerableMember(TOrmContext context) Items = new List { item1, item2 } }; - context.Orders.Add(order); + await context.Orders.Add(order); await context.SaveChanges(); var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs index ac24e5388..1ce20572f 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/DbSetWrapperBase.cs @@ -28,7 +28,9 @@ protected DbSetWrapperBase(IQueryable dbSet) public abstract void Include(Expression> navigationPropertyPath); - public abstract void Add(TEntity itemToAdd); + public abstract Task Add(TEntity itemToAdd); + + public abstract Task AddRange(TEntity[] itemsToAdd); public abstract void Clear(); } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs index fec23313a..23a278c5d 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/IDbSetWrapper.cs @@ -9,7 +9,9 @@ public interface IDbSetWrapper : IQueryable { void Include(Expression> navigationPropertyPath); - void Add(TEntity itemToAdd); + Task Add(TEntity itemToAdd); + + Task AddRange(params TEntity[] itemsToAdd); void Clear(); } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 4d6a84172..bf2f81023 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -6,6 +6,8 @@ public interface ITestDbContext : IDisposable { + IDbSetWrapper Animals { get; } + IDbSetWrapper Companies { get; } IDbSetWrapper Employees { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index c6619e8fb..4b2c4cdb3 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -59,6 +59,7 @@ protected async Task RunTest(Func test) private async Task EmptyDbContext() { + Context.Animals.Clear(); Context.Companies.Clear(); Context.Employees.Clear(); Context.Categories.Clear(); diff --git a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs index b1e700b71..6ebf1ce2e 100644 --- a/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs +++ b/AgileMapper.UnitTests.Orms/Recursion/WhenProjectingCircularReferences.cs @@ -26,7 +26,7 @@ public Task ShouldProjectAOneToOneRelationship() HeadOffice = new Address { Line1 = "Acme Park", Postcode = "AC3 3ME" } }; - context.Companies.Add(company); + await context.Companies.Add(company); await context.SaveChanges(); var ceo = new Employee @@ -38,7 +38,7 @@ public Task ShouldProjectAOneToOneRelationship() Company = company }; - context.Employees.Add(ceo); + await context.Employees.Add(ceo); await context.SaveChanges(); company.CeoId = ceo.Id; @@ -92,7 +92,7 @@ protected async Task ProjectAOneToManyRelationshipToRecursionDepth( new Category { Name = "Top > Two" }, new Category { Name = "Top > Three" }); - context.Categories.Add(topLevel); + await context.Categories.Add(topLevel); if (depth > 0) { diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs index 405ca1f44..cad2db1c5 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToBools.cs @@ -19,7 +19,7 @@ public Task ShouldProjectAnIntOneToTrue() { return RunTest(async context => { - context.IntItems.Add(new PublicInt { Value = 1 }); + await context.IntItems.Add(new PublicInt { Value = 1 }); await context.SaveChanges(); var boolItem = context.IntItems.Project().To().First(); @@ -33,7 +33,7 @@ public Task ShouldProjectAnIntZeroToFalse() { return RunTest(async context => { - context.IntItems.Add(new PublicInt { Value = 0 }); + await context.IntItems.Add(new PublicInt { Value = 0 }); await context.SaveChanges(); var boolItem = context.IntItems.Project().To().First(); @@ -47,7 +47,7 @@ public Task ShouldProjectAStringTrueToTrue() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "true" }); + await context.StringItems.Add(new PublicString { Value = "true" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -61,7 +61,7 @@ public Task ShouldProjectAStringTrueToTrueIgnoringCase() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "tRuE" }); + await context.StringItems.Add(new PublicString { Value = "tRuE" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -75,7 +75,7 @@ public Task ShouldProjectAStringOneToTrue() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "1" }); + await context.StringItems.Add(new PublicString { Value = "1" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -89,7 +89,7 @@ public Task ShouldProjectAStringFalseToFalse() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "false" }); + await context.StringItems.Add(new PublicString { Value = "false" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -103,7 +103,7 @@ public Task ShouldProjectAStringZeroToFalse() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "0" }); + await context.StringItems.Add(new PublicString { Value = "0" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -117,7 +117,7 @@ public Task ShouldProjectAStringNonBooleanValueToFalse() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "uokyujhygt" }); + await context.StringItems.Add(new PublicString { Value = "uokyujhygt" }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); @@ -131,7 +131,7 @@ public Task ShouldProjectAStringNullToFalse() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = null }); + await context.StringItems.Add(new PublicString { Value = null }); await context.SaveChanges(); var boolItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs index 01ee79d16..ef6c76e0c 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDateTimes.cs @@ -26,7 +26,7 @@ private static async Task ProjectParseableStringToADateTime(TOrmContext context) { var now = DateTime.Now; - context.StringItems.Add(new PublicString { Value = now.ToString("s") }); + await context.StringItems.Add(new PublicString { Value = now.ToString("s") }); await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); @@ -46,7 +46,7 @@ protected Task RunShouldErrorProjectingANullStringToADateTime() private static async Task ProjectANullStringToADateTime(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = default(string) }); + await context.StringItems.Add(new PublicString { Value = default(string) }); await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); @@ -66,7 +66,7 @@ protected Task RunShouldErrorProjectingAnUnparseableStringToADateTime() private static async Task ProjectAnUnparseableStringToADateTime(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); + await context.StringItems.Add(new PublicString { Value = "htgijfoekld" }); await context.SaveChanges(); var dateTimeItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs index 7baeb99ba..66d099507 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToDoubles.cs @@ -19,7 +19,7 @@ public Task ShouldProjectAShortToADouble() { return RunTest(async context => { - context.ShortItems.Add(new PublicShort { Value = 123 }); + await context.ShortItems.Add(new PublicShort { Value = 123 }); await context.SaveChanges(); var doubleItem = context.ShortItems.Project().To().First(); @@ -33,7 +33,7 @@ public Task ShouldProjectALongToADouble() { return RunTest(async context => { - context.LongItems.Add(new PublicLong { Value = 12345L }); + await context.LongItems.Add(new PublicLong { Value = 12345L }); await context.SaveChanges(); var doubleItem = context.LongItems.Project().To().First(); @@ -52,7 +52,7 @@ protected Task RunShouldErrorProjectingAParseableStringToADouble() private static async Task ProjectParseableStringToDouble(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = "738.01" }); + await context.StringItems.Add(new PublicString { Value = "738.01" }); await context.SaveChanges(); var doubleItem = context.StringItems.Project().To().First(); @@ -72,7 +72,7 @@ protected Task RunShouldErrorProjectingANullStringToADouble() private static async Task ProjectNullStringToDouble(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = default(string) }); + await context.StringItems.Add(new PublicString { Value = default(string) }); await context.SaveChanges(); var doubleItem = context.StringItems.Project().To().First(); @@ -92,7 +92,7 @@ protected Task RunShouldErrorProjectingAnUnparseableStringToADouble() private static async Task ProjectUnparseableStringToDouble(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = "poioiujygy" }); + await context.StringItems.Add(new PublicString { Value = "poioiujygy" }); await context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs index 7b5592c75..e31333e32 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToEnums.cs @@ -20,7 +20,7 @@ public Task ShouldProjectAByteToAnEnum() { return RunTest(async context => { - context.ByteItems.Add(new PublicByte { Value = (byte)Dr }); + await context.ByteItems.Add(new PublicByte { Value = (byte)Dr }); await context.SaveChanges(); var enumItem = context.ByteItems.Project().To().ShouldHaveSingleItem(); @@ -34,7 +34,7 @@ public Task ShouldProjectAShortToAnEnum() { return RunTest(async context => { - context.ShortItems.Add(new PublicShort { Value = (short)Count }); + await context.ShortItems.Add(new PublicShort { Value = (short)Count }); await context.SaveChanges(); var enumItem = context.ShortItems.Project().To().ShouldHaveSingleItem(); @@ -48,7 +48,7 @@ public Task ShouldProjectAnIntToAnEnum() { return RunTest(async context => { - context.IntItems.Add(new PublicInt { Value = (int)Duke }); + await context.IntItems.Add(new PublicInt { Value = (int)Duke }); await context.SaveChanges(); var enumItem = context.IntItems.Project().To().ShouldHaveSingleItem(); @@ -62,7 +62,7 @@ public Task ShouldProjectALongToAnEnum() { return RunTest(async context => { - context.LongItems.Add(new PublicLong { Value = (long)Ms }); + await context.LongItems.Add(new PublicLong { Value = (long)Ms }); await context.SaveChanges(); var enumItem = context.LongItems.Project().To().ShouldHaveSingleItem(); @@ -76,7 +76,7 @@ public Task ShouldProjectAMatchingStringToAnEnum() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = Mr.ToString() }); + await context.StringItems.Add(new PublicString { Value = Mr.ToString() }); await context.SaveChanges(); var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); @@ -90,7 +90,7 @@ public Task ShouldProjectAMatchingNumericStringToAnEnum() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = ((int)Mrs).ToString() }); + await context.StringItems.Add(new PublicString { Value = ((int)Mrs).ToString() }); await context.SaveChanges(); var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); @@ -104,7 +104,7 @@ public Task ShouldProjectANonMatchingStringToAnEnum() { return RunTest(async context => { - context.StringItems.Add(new PublicString { Value = "Horse Pills" }); + await context.StringItems.Add(new PublicString { Value = "Horse Pills" }); await context.SaveChanges(); var enumItem = context.StringItems.Project().To().ShouldHaveSingleItem(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs index c40d3ceb5..3ea93c54f 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToGuids.cs @@ -26,7 +26,7 @@ private static async Task ProjectAParseableStringToAGuid(TOrmContext context) { var guid = Guid.NewGuid(); - context.StringItems.Add(new PublicString { Value = guid.ToString() }); + await context.StringItems.Add(new PublicString { Value = guid.ToString() }); await context.SaveChanges(); var guidItem = context.StringItems.Project().To().First(); @@ -46,7 +46,7 @@ protected Task RunShouldErrorProjectingANullStringToAGuid() private static async Task ProjectANullStringToAGuid(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = default(string) }); + await context.StringItems.Add(new PublicString { Value = default(string) }); await context.SaveChanges(); var guidItem = context.StringItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index dae6ef46b..8cf128691 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -19,7 +19,7 @@ public Task ShouldProjectAShortToAnInt() { return RunTest(async context => { - context.ShortItems.Add(new PublicShort { Value = 123 }); + await context.ShortItems.Add(new PublicShort { Value = 123 }); await context.SaveChanges(); var intItem = context.ShortItems.Project().To().First(); @@ -33,7 +33,7 @@ public Task ShouldProjectAnInRangeLongToAnInt() { return RunTest(async context => { - context.LongItems.Add(new PublicLong { Value = 12345L }); + await context.LongItems.Add(new PublicLong { Value = 12345L }); await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -47,7 +47,7 @@ public Task ShouldProjectATooBigLongToAnInt() { return RunTest(async context => { - context.LongItems.Add(new PublicLong { Value = long.MaxValue }); + await context.LongItems.Add(new PublicLong { Value = long.MaxValue }); await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -61,7 +61,7 @@ public Task ShouldProjectATooSmallLongToAnInt() { return RunTest(async context => { - context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); + await context.LongItems.Add(new PublicLong { Value = int.MinValue - 1L }); await context.SaveChanges(); var intItem = context.LongItems.Project().To().First(); @@ -80,7 +80,7 @@ protected Task RunShouldErrorProjectingAParseableStringToAnInt() private static async Task ProjectParseableStringToInt(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = "738" }); + await context.StringItems.Add(new PublicString { Value = "738" }); await context.SaveChanges(); var intItem = context.StringItems.Project().To().First(); @@ -100,7 +100,7 @@ protected Task RunShouldErrorProjectingANullStringToAnInt() private static async Task ProjectNullStringToInt(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = default(string) }); + await context.StringItems.Add(new PublicString { Value = default(string) }); await context.SaveChanges(); var intItem = context.StringItems.Project().To().First(); @@ -120,7 +120,7 @@ protected Task RunShouldErrorProjectingAnUnparseableStringToAnInt() private static async Task ProjectUnparseableStringToInt(TOrmContext context) { - context.StringItems.Add(new PublicString { Value = "hsejk" }); + await context.StringItems.Add(new PublicString { Value = "hsejk" }); await context.SaveChanges(); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index edf0f1a08..ab35d027e 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -19,7 +19,7 @@ public Task ShouldProjectAnIntToAString() { return RunTest(async context => { - context.IntItems.Add(new PublicInt { Value = 763483 }); + await context.IntItems.Add(new PublicInt { Value = 763483 }); await context.SaveChanges(); var stringItem = context.IntItems.Project().To().First(); @@ -33,7 +33,7 @@ public Task ShouldProjectABoolToAString() { return RunTest(async context => { - context.BoolItems.Add(new PublicBool { Value = true }); + await context.BoolItems.Add(new PublicBool { Value = true }); await context.SaveChanges(); var stringItem = context.BoolItems.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Animal.cs b/AgileMapper.UnitTests.Orms/TestClasses/Animal.cs new file mode 100644 index 000000000..b4ef3d267 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Animal.cs @@ -0,0 +1,23 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class Animal + { + [Key] + public int Id { get; set; } + + public AnimalType Type { get; set; } + + public string Name { get; set; } + + public string Sound { get; set; } + + public enum AnimalType + { + Dog = 1, + Elephant = 2, + Snake = 3 + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs new file mode 100644 index 000000000..5bd090edb --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public abstract class AnimalDtoBase + { + public int Id { get; set; } + + public string Name { get; set; } + + public abstract string Sound { get; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs new file mode 100644 index 000000000..5aeb2423e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class DogDto : AnimalDtoBase + { + public override string Sound => "Woof"; + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs new file mode 100644 index 000000000..ce11e151c --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class ElephantDto : AnimalDtoBase + { + public override string Sound => "Trumpet"; + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs new file mode 100644 index 000000000..dafe71cf9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class SnakeDto : AnimalDtoBase + { + public override string Sound => "Hiss"; + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs index 2e47f3f6e..941b1dbd1 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingFlatTypes.cs @@ -22,8 +22,7 @@ public Task ShouldProjectToAFlatTypeArray() var product1 = new Product { Name = "Product One" }; var product2 = new Product { Name = "Product Two" }; - context.Products.Add(product1); - context.Products.Add(product2); + await context.Products.AddRange(product1, product2); await context.SaveChanges(); var productDtos = context @@ -54,7 +53,7 @@ private static async Task DoShouldProjectStructCtorParameters(TOrmContext contex { var product = new Product { Name = "Product One" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); var productDto = context @@ -75,7 +74,7 @@ public Task ShouldProjectToANonMatchingTypeList() { var product = new Product { Name = "Uno" }; - context.Products.Add(product); + await context.Products.Add(product); await context.SaveChanges(); var productDtos = context.Products.Project().To().ToList(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs index 12242e673..140207b03 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToComplexTypeMembers.cs @@ -29,7 +29,7 @@ public Task ShouldProjectToAComplexTypeMember() } }; - context.Persons.Add(person); + await context.Persons.Add(person); await context.SaveChanges(); var personDto = context.Persons.Project().To().First(); @@ -50,7 +50,7 @@ public Task ShouldHandleANullComplexTypeMember() { var person = new Person { Name = "No Address!" }; - context.Persons.Add(person); + await context.Persons.Add(person); await context.SaveChanges(); var personDto = context.Persons.Project().To().First(); diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs index dc7e2b0f3..f67acf78e 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToFlatTypes.cs @@ -32,8 +32,7 @@ public Task ShouldProjectAComplexTypeMemberToAFlatTypeList() var person2 = new Person { Name = "Person Two" }; - context.Persons.Add(person1); - context.Persons.Add(person2); + await context.Persons.AddRange(person1, person2); await context.SaveChanges(); var personViewModels = context diff --git a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs index 2137912b9..b7ff16065 100644 --- a/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs +++ b/AgileMapper/Api/Configuration/DerivedPairTargetTypeSpecifier.cs @@ -1,40 +1,29 @@ namespace AgileObjects.AgileMapper.Api.Configuration { using AgileMapper.Configuration; + using Projection; - /// - /// Enables the selection of a derived target type to which to match a configured, derived source type. - /// - /// - /// The type of source object for which the derived type pair is being configured. - /// - /// - /// The type of derived source object for which the specified derived target type is being configured. - /// - /// - /// The type of target object for which the derived type pair is being configured. - /// - public class DerivedPairTargetTypeSpecifier + internal class DerivedPairTargetTypeSpecifier : + IMappingDerivedPairTargetTypeSpecifier, + IProjectionDerivedPairTargetTypeSpecifier { private readonly MappingConfigInfo _configInfo; - internal DerivedPairTargetTypeSpecifier(MappingConfigInfo configInfo) + public DerivedPairTargetTypeSpecifier(MappingConfigInfo configInfo) { _configInfo = configInfo; } - /// - /// Map the derived source type being configured to the derived target type specified by the type argument. - /// - /// - /// The derived target type to create for the configured derived source type. - /// - /// - /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and - /// target type being configured. - /// public IMappingConfigContinuation To() where TDerivedTarget : TTarget + { + return SetDerivedTargetType(); + } + + IProjectionConfigContinuation IProjectionDerivedPairTargetTypeSpecifier.To() + => SetDerivedTargetType(); + + private MappingConfigContinuation SetDerivedTargetType() { var derivedTypePair = DerivedTypePair .For(_configInfo); diff --git a/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs index df4828858..cdcf65690 100644 --- a/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs @@ -9,8 +9,8 @@ public interface IConditionalRootMappingConfigurator : IRootMappingConfigurator { /// - /// Map the source type being configured to the derived target type specified by the type argument if - /// the preceding condition evaluates to true. + /// Map the source type being configured to the derived target type specified by + /// if the preceding condition evaluates to true. /// /// The derived target type to create. /// diff --git a/AgileMapper/Api/Configuration/IFullMappingConfigurator.cs b/AgileMapper/Api/Configuration/IFullMappingConfigurator.cs index 919de0dd2..ac333be96 100644 --- a/AgileMapper/Api/Configuration/IFullMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IFullMappingConfigurator.cs @@ -23,8 +23,10 @@ public interface IFullMappingConfigurator : IFullMappingSettin /// /// The derived source type for which to configure a matching derived target type. /// - /// A DerivedPairTargetTypeSpecifier with which to specify the matching derived target type. - DerivedPairTargetTypeSpecifier Map() + /// + /// A IMappingDerivedPairTargetTypeSpecifier with which to specify the matching derived target type. + /// + IMappingDerivedPairTargetTypeSpecifier Map() where TDerivedSource : TSource; } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IMappingDerivedPairTargetTypeSpecifier.cs b/AgileMapper/Api/Configuration/IMappingDerivedPairTargetTypeSpecifier.cs new file mode 100644 index 000000000..7dc110073 --- /dev/null +++ b/AgileMapper/Api/Configuration/IMappingDerivedPairTargetTypeSpecifier.cs @@ -0,0 +1,27 @@ +namespace AgileObjects.AgileMapper.Api.Configuration +{ + /// + /// Enables the selection of a derived target type to which to match a configured source type. + /// + /// + /// The type of source object for which the derived type pair is being configured. + /// + /// + /// The type of target object for which the derived type pair is being configured. + /// + public interface IMappingDerivedPairTargetTypeSpecifier + { + /// + /// Map the derived source type being configured to the derived target type specified by the type argument. + /// + /// + /// The derived target type to create for the configured derived source type. + /// + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and + /// target type being configured. + /// + IMappingConfigContinuation To() + where TDerivedTarget : TTarget; + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index bb18ffe7c..5b81698f1 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -324,6 +324,14 @@ public IMappingConfigContinuation MapTo() return derivedTypePair.To(); } + IProjectionConfigContinuation IConditionalRootProjectionConfigurator.MapTo() + { + IProjectionDerivedPairTargetTypeSpecifier derivedTypePair = + new DerivedPairTargetTypeSpecifier(ConfigInfo); + + return derivedTypePair.To(); + } + public IMappingConfigContinuation MapToNull() { var condition = new MapToNullCondition(ConfigInfo); @@ -333,8 +341,11 @@ public IMappingConfigContinuation MapToNull() return new MappingConfigContinuation(ConfigInfo); } - public DerivedPairTargetTypeSpecifier Map() where TDerivedSource : TSource - => new DerivedPairTargetTypeSpecifier(ConfigInfo); + public IMappingDerivedPairTargetTypeSpecifier Map() + where TDerivedSource : TSource + { + return new DerivedPairTargetTypeSpecifier(ConfigInfo); + } #endregion } diff --git a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs index 7da8e7c36..fb7515af6 100644 --- a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs @@ -8,5 +8,16 @@ public interface IConditionalRootProjectionConfigurator : IRootProjectionConfigurator { + /// + /// Map the source type being configured to the derived result type specified by + /// if the preceding condition evaluates to true. + /// + /// The derived result type to create. + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source + /// and result type being configured. + /// + IProjectionConfigContinuation MapTo() + where TDerivedResult : TResultElement; } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionDerivedPairTargetTypeSpecifier.cs b/AgileMapper/Api/Configuration/Projection/IProjectionDerivedPairTargetTypeSpecifier.cs new file mode 100644 index 000000000..0d30ab211 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionDerivedPairTargetTypeSpecifier.cs @@ -0,0 +1,27 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Enables the selection of a derived result type to which to match a configured source type. + /// + /// + /// The type of source object for which the derived type pair is being configured. + /// + /// + /// The type of result object for which the derived type pair is being configured. + /// + public interface IProjectionDerivedPairTargetTypeSpecifier + { + /// + /// Map the derived source type being configured to the derived result type specified by the type argument. + /// + /// + /// The derived result type to create for the configured derived source type. + /// + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the source and + /// result type being configured. + /// + IProjectionConfigContinuation To() + where TDerivedResult : TResultElement; + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs index 250c49da1..9d0543d70 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs @@ -31,7 +31,7 @@ public Expression GetNewObjectCreation(IObjectMappingData mappingData) key, out var newingConstructorRequired); - if (newingConstructorRequired) + if (newingConstructorRequired && !key.MappingData.MapperData.TargetType.IsAbstract()) { AddNewingConstruction(constructions, key); } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs index 522fe6532..76c6f07b1 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeMappingExpressionFactory.cs @@ -46,6 +46,11 @@ protected override bool TargetCannotBeMapped(IObjectMappingData mappingData, out var targetType = mappingData.MapperData.TargetType; + if (targetType.IsAbstract() && mappingData.MapperData.GetDerivedTargetTypes().Any()) + { + return base.TargetCannotBeMapped(mappingData, out nullMappingBlock); + } + nullMappingBlock = Expression.Block( ReadableExpression.Comment("Cannot construct an instance of " + targetType.GetFriendlyName()), targetType.ToDefaultExpression()); diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs index e055ec3ee..6b95b81dc 100644 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs @@ -61,7 +61,9 @@ public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) return typedObjectVariables.Any() ? Expression.Block(typedObjectVariables, derivedTypeMappingExpressions) - : Expression.Block(derivedTypeMappingExpressions); + : derivedTypeMappingExpressions.HasOne() + ? derivedTypeMappingExpressions.First() + : Expression.Block(derivedTypeMappingExpressions); } private static ICollection GetDerivedTargetTypesIfNecessary(IObjectMappingData mappingData) @@ -75,40 +77,79 @@ private static ICollection GetDerivedTargetTypesIfNecessary(IObjectMapping } private static void AddDeclaredSourceTypeMappings( - IEnumerable derivedTypePairs, + ICollection derivedTypePairs, IObjectMappingData declaredTypeMappingData, ICollection derivedTypeMappingExpressions, out bool declaredTypeHasUnconditionalTypePair) { - var declaredTypeMapperData = declaredTypeMappingData.MapperData; + declaredTypeHasUnconditionalTypePair = false; + + if (derivedTypePairs.None()) + { + return; + } - derivedTypePairs = derivedTypePairs + var derivedTypeMappings = new List(derivedTypePairs.Count); + + var orderedDerivedTypePairs = derivedTypePairs .OrderBy(tp => tp.DerivedSourceType, TypeComparer.MostToLeastDerived); - foreach (var derivedTypePair in derivedTypePairs) + foreach (var derivedTypePair in orderedDerivedTypePairs) { - var condition = GetTypePairCondition(derivedTypePair, declaredTypeMapperData); + var derivedTypeMapping = DerivedTypeMapping.For(derivedTypePair, declaredTypeMappingData); - var derivedTypeMapping = DerivedMappingFactory.GetDerivedTypeMapping( - declaredTypeMappingData, - declaredTypeMapperData.SourceObject, - derivedTypePair.DerivedTargetType); + derivedTypeMappings.Add(derivedTypeMapping); - var returnMappingResult = Expression.Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); - declaredTypeHasUnconditionalTypePair = (condition == null); + declaredTypeHasUnconditionalTypePair = derivedTypeMapping.IsUnconditional; if (declaredTypeHasUnconditionalTypePair) + { + break; + } + } + + AddDerivedTargetTypeMappings( + declaredTypeMappingData.MapperData, + derivedTypeMappings, + derivedTypeMappingExpressions); + } + + private static void AddDerivedTargetTypeMappings( + ObjectMapperData declaredTypeMapperData, + IEnumerable derivedTypeMappings, + ICollection derivedTypeMappingExpressions) + { + if (declaredTypeMapperData.UseMemberInitialisations()) + { + Expression fallbackValue = declaredTypeMapperData.TargetType.ToDefaultExpression(); + + var derivedTypeMappingTree = derivedTypeMappings.Reverse().Aggregate( + fallbackValue, + (mappingsSoFar, derivedTypeMapping) => Expression.Condition( + derivedTypeMapping.Condition, + derivedTypeMapping.Mapping.GetConversionTo(fallbackValue.Type), + mappingsSoFar)); + + derivedTypeMappingExpressions.Add(derivedTypeMappingTree); + return; + } + + foreach (var derivedTypeMapping in derivedTypeMappings) + { + var returnMappingResult = Expression.Return( + declaredTypeMapperData.ReturnLabelTarget, + derivedTypeMapping.Mapping); + + if (derivedTypeMapping.IsUnconditional) { derivedTypeMappingExpressions.Add(returnMappingResult); return; } - var ifConditionThenMap = Expression.IfThen(condition, returnMappingResult); + var ifConditionThenMap = Expression.IfThen(derivedTypeMapping.Condition, returnMappingResult); derivedTypeMappingExpressions.Add(ifConditionThenMap); } - - declaredTypeHasUnconditionalTypePair = false; } private static Expression GetTypePairCondition(DerivedTypePair derivedTypePair, IMemberMapperData mapperData) @@ -402,5 +443,30 @@ public TypePairGroup(IGrouping typePairGroup) public IList TypePairs { get; } } + + private class DerivedTypeMapping + { + public static DerivedTypeMapping For( + DerivedTypePair derivedTypePair, + IObjectMappingData declaredTypeMappingData) + { + var mapping = DerivedMappingFactory.GetDerivedTypeMapping( + declaredTypeMappingData, + declaredTypeMappingData.MapperData.SourceObject, + derivedTypePair.DerivedTargetType); + + return new DerivedTypeMapping + { + Condition = GetTypePairCondition(derivedTypePair, declaredTypeMappingData.MapperData), + Mapping = mapping + }; + } + + public Expression Condition { get; private set; } + + public bool IsUnconditional => Condition == null; + + public Expression Mapping { get; private set; } + } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs index b74e9ade5..a0337fe3c 100644 --- a/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs +++ b/AgileMapper/ObjectPopulation/Enumerables/EnumerablePopulationBuilder.cs @@ -558,24 +558,24 @@ private static Expression GetElementMapping( public Expression GetSourceItemsProjection( Expression sourceEnumerableValue, - Func projectionFuncFactory) + Func projectionLambdaFactory) { return CreateSourceItemsProjection( sourceEnumerableValue, - (sourceParameter, counter) => projectionFuncFactory.Invoke(sourceParameter), + (sourceParameter, counter) => projectionLambdaFactory.Invoke(sourceParameter), counterRequired: false); } public Expression GetSourceItemsProjection( Expression sourceEnumerableValue, - Func projectionFuncFactory) + Func projectionLambdaFactory) { - return CreateSourceItemsProjection(sourceEnumerableValue, projectionFuncFactory, counterRequired: true); + return CreateSourceItemsProjection(sourceEnumerableValue, projectionLambdaFactory, counterRequired: true); } private Expression CreateSourceItemsProjection( Expression sourceEnumerableValue, - Func projectionFuncFactory, + Func projectionLambdaFactory, bool counterRequired) { var isRootQueryableMapping = MapperData.SourceType.IsQueryable(); @@ -591,35 +591,29 @@ private Expression CreateSourceItemsProjection( ? _enumerableSelectWithIndexMethod : EnumerableSelectWithoutIndexMethod; - ParameterExpression[] projectionFuncParameters; + ParameterExpression[] projectionLambdaParameters; Type[] funcTypes; if (counterRequired) { - projectionFuncParameters = new[] { _sourceElementParameter, Counter }; + projectionLambdaParameters = new[] { _sourceElementParameter, Counter }; funcTypes = new[] { Context.SourceElementType, Counter.Type, Context.TargetElementType }; } else { - projectionFuncParameters = new[] { _sourceElementParameter }; + projectionLambdaParameters = new[] { _sourceElementParameter }; funcTypes = new[] { Context.SourceElementType, Context.TargetElementType }; } var projectionFuncType = Expression.GetFuncType(funcTypes); - Expression projectionFunc = Expression.Lambda( + Expression projectionLambda = Expression.Lambda( projectionFuncType, - projectionFuncFactory.Invoke(_sourceElementParameter, Counter), - projectionFuncParameters); - - if (isRootQueryableMapping) - { - projectionFuncType = typeof(Expression<>).MakeGenericType(projectionFuncType); - projectionFunc = projectionFunc.ToConstantExpression(projectionFuncType); - } + projectionLambdaFactory.Invoke(_sourceElementParameter, Counter), + projectionLambdaParameters); var typedSelectMethod = linqSelectOverload.MakeGenericMethod(Context.ElementTypes); - var typedSelectCall = Expression.Call(typedSelectMethod, sourceEnumerableValue, projectionFunc); + var typedSelectCall = Expression.Call(typedSelectMethod, sourceEnumerableValue, projectionLambda); return typedSelectCall; } diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index d9b9796cf..8760cc52f 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -74,14 +74,19 @@ private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, { derivedTypeMappings = GetDerivedTypeMappings(mappingData); - if (derivedTypeMappings.NodeType != Goto) + switch (derivedTypeMappings.NodeType) { - return false; - } + case Conditional: + return true; - var returnExpression = (GotoExpression)derivedTypeMappings; - derivedTypeMappings = returnExpression.Value; - return true; + case Goto: + var returnExpression = (GotoExpression)derivedTypeMappings; + derivedTypeMappings = returnExpression.Value; + return true; + + default: + return false; + } } protected virtual Expression GetDerivedTypeMappings(IObjectMappingData mappingData) => Constants.EmptyExpression; diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index ec372d431..5a339e341 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -2,7 +2,6 @@ { using System.Linq.Expressions; using Converters; - using Extensions.Internal; using Members; using ObjectPopulation; using Settings; @@ -55,16 +54,6 @@ protected override Expression VisitConditional(ConditionalExpression conditional return base.VisitConditional(conditional); } - protected override Expression VisitConstant(ConstantExpression constant) - { - if (constant.Value is LambdaExpression lambda) - { - return Modify(lambda).ToConstantExpression(constant.Type); - } - - return base.VisitConstant(constant); - } - protected override Expression VisitDefault(DefaultExpression defaultExpression) => Settings.GetDefaultValueFor(defaultExpression); diff --git a/CommonAssemblyInfo.cs b/CommonAssemblyInfo.cs index 5ebe21f74..fced7121b 100644 --- a/CommonAssemblyInfo.cs +++ b/CommonAssemblyInfo.cs @@ -1,10 +1,7 @@ -using System; -using System.Reflection; +using System.Reflection; using System.Resources; [assembly: AssemblyCompany("AgileObjects Ltd")] [assembly: AssemblyProduct("AgileObjects.AgileMapper")] [assembly: AssemblyCopyright("Copyright © AgileObjects Ltd 2018")] -[assembly: NeutralResourcesLanguage("en")] - -[assembly: CLSCompliant(true)] \ No newline at end of file +[assembly: NeutralResourcesLanguage("en")] \ No newline at end of file From 960fe3a2265034c7f60f3bdf6164e24f1eb8f4a9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 11 Feb 2018 10:00:39 +0000 Subject: [PATCH 143/176] Refactoring derived type mapping projection --- .../Members/MemberMapperDataExtensions.cs | 4 + .../MemberInitPopulationExpressionFactory.cs | 5 + .../TargetObjectResolutionFactory.cs | 9 +- .../DefaultValueDataSourceFactory.cs | 3 +- .../DerivedComplexTypeMappingsFactory.cs | 92 +++------------ ...ExistingOrDefaultValueDataSourceFactory.cs | 5 +- .../MappingExpressionFactoryBase.cs | 110 ++++++++++++------ .../ComplexTypeConditionalConverter.cs | 2 +- .../Converters/DerivedTypeMappingConverter.cs | 85 ++++++++++++++ .../Queryables/QueryProjectionModifier.cs | 10 ++ ...apToDepthRecursiveMemberMappingStrategy.cs | 4 +- 11 files changed, 202 insertions(+), 127 deletions(-) create mode 100644 AgileMapper/Queryables/Converters/DerivedTypeMappingConverter.cs diff --git a/AgileMapper/Members/MemberMapperDataExtensions.cs b/AgileMapper/Members/MemberMapperDataExtensions.cs index efb594820..d3e2234c9 100644 --- a/AgileMapper/Members/MemberMapperDataExtensions.cs +++ b/AgileMapper/Members/MemberMapperDataExtensions.cs @@ -118,6 +118,10 @@ public static Expression GetTargetMemberAccess(this IMemberMapperData mapperData return mapperData.TargetMember.GetAccess(subjectMapperData.TargetInstance, mapperData); } + [DebuggerStepThrough] + public static Expression GetTargetMemberDefault(this IBasicMapperData mapperData) + => mapperData.TargetMember.Type.ToDefaultExpression(); + public static ExpressionInfoFinder.ExpressionInfo GetExpressionInfoFor( this IMemberMapperData mapperData, Expression value, diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs index 461dfef8f..3656ed29c 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/MemberInitPopulationExpressionFactory.cs @@ -18,6 +18,11 @@ protected override Expression GetNewObjectCreation(IObjectMappingData mappingDat { var objectCreation = base.GetNewObjectCreation(mappingData, memberPopulations); + if (objectCreation == null) + { + memberPopulations.Clear(); + } + if (memberPopulations.None()) { return objectCreation; diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs index 820aac858..60ee1191b 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/TargetObjectResolutionFactory.cs @@ -42,13 +42,16 @@ public static Expression GetObjectResolution( { mapperData.TargetMember.IsReadOnly = true; - // Use the existing target object if the mapper can't create an instance: - return mapperData.TargetObject; + // Use the existing target object if it might have a value and + // the mapper can't create an instance: + return mapperData.TargetCouldBePopulated() + ? mapperData.TargetObject + : mapperData.GetTargetMemberDefault(); } if (UseNullFallbackValue(mapperData, objectValue, memberPopulations)) { - objectValue = mapperData.TargetMember.Type.ToDefaultExpression(); + objectValue = mapperData.GetTargetMemberDefault(); mapperData.Context.UsesMappingDataObjectAsParameter = false; } else diff --git a/AgileMapper/ObjectPopulation/DefaultValueDataSourceFactory.cs b/AgileMapper/ObjectPopulation/DefaultValueDataSourceFactory.cs index 6cc1f2bbb..a22a21fc5 100644 --- a/AgileMapper/ObjectPopulation/DefaultValueDataSourceFactory.cs +++ b/AgileMapper/ObjectPopulation/DefaultValueDataSourceFactory.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using DataSources; - using Extensions.Internal; using Members; internal class DefaultValueDataSourceFactory : IDataSourceFactory @@ -14,7 +13,7 @@ public IDataSource Create(IMemberMapperData mapperData) private class DefaultValueDataSource : DataSourceBase { public DefaultValueDataSource(IMemberMapperData mapperData) - : base(mapperData.SourceMember, mapperData.TargetMember.Type.ToDefaultExpression()) + : base(mapperData.SourceMember, mapperData.GetTargetMemberDefault()) { } } diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs index 6b95b81dc..ce95e05b8 100644 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs @@ -77,79 +77,40 @@ private static ICollection GetDerivedTargetTypesIfNecessary(IObjectMapping } private static void AddDeclaredSourceTypeMappings( - ICollection derivedTypePairs, + IEnumerable derivedTypePairs, IObjectMappingData declaredTypeMappingData, ICollection derivedTypeMappingExpressions, out bool declaredTypeHasUnconditionalTypePair) { - declaredTypeHasUnconditionalTypePair = false; - - if (derivedTypePairs.None()) - { - return; - } - - var derivedTypeMappings = new List(derivedTypePairs.Count); + var declaredTypeMapperData = declaredTypeMappingData.MapperData; - var orderedDerivedTypePairs = derivedTypePairs + derivedTypePairs = derivedTypePairs .OrderBy(tp => tp.DerivedSourceType, TypeComparer.MostToLeastDerived); - foreach (var derivedTypePair in orderedDerivedTypePairs) + foreach (var derivedTypePair in derivedTypePairs) { - var derivedTypeMapping = DerivedTypeMapping.For(derivedTypePair, declaredTypeMappingData); + var condition = GetTypePairCondition(derivedTypePair, declaredTypeMapperData); - derivedTypeMappings.Add(derivedTypeMapping); + var derivedTypeMapping = DerivedMappingFactory.GetDerivedTypeMapping( + declaredTypeMappingData, + declaredTypeMapperData.SourceObject, + derivedTypePair.DerivedTargetType); - declaredTypeHasUnconditionalTypePair = derivedTypeMapping.IsUnconditional; + var returnMappingResult = Expression.Return(declaredTypeMapperData.ReturnLabelTarget, derivedTypeMapping); + declaredTypeHasUnconditionalTypePair = (condition == null); if (declaredTypeHasUnconditionalTypePair) - { - break; - } - } - - AddDerivedTargetTypeMappings( - declaredTypeMappingData.MapperData, - derivedTypeMappings, - derivedTypeMappingExpressions); - } - - private static void AddDerivedTargetTypeMappings( - ObjectMapperData declaredTypeMapperData, - IEnumerable derivedTypeMappings, - ICollection derivedTypeMappingExpressions) - { - if (declaredTypeMapperData.UseMemberInitialisations()) - { - Expression fallbackValue = declaredTypeMapperData.TargetType.ToDefaultExpression(); - - var derivedTypeMappingTree = derivedTypeMappings.Reverse().Aggregate( - fallbackValue, - (mappingsSoFar, derivedTypeMapping) => Expression.Condition( - derivedTypeMapping.Condition, - derivedTypeMapping.Mapping.GetConversionTo(fallbackValue.Type), - mappingsSoFar)); - - derivedTypeMappingExpressions.Add(derivedTypeMappingTree); - return; - } - - foreach (var derivedTypeMapping in derivedTypeMappings) - { - var returnMappingResult = Expression.Return( - declaredTypeMapperData.ReturnLabelTarget, - derivedTypeMapping.Mapping); - - if (derivedTypeMapping.IsUnconditional) { derivedTypeMappingExpressions.Add(returnMappingResult); return; } - var ifConditionThenMap = Expression.IfThen(derivedTypeMapping.Condition, returnMappingResult); + var ifConditionThenMap = Expression.IfThen(condition, returnMappingResult); derivedTypeMappingExpressions.Add(ifConditionThenMap); } + + declaredTypeHasUnconditionalTypePair = false; } private static Expression GetTypePairCondition(DerivedTypePair derivedTypePair, IMemberMapperData mapperData) @@ -443,30 +404,5 @@ public TypePairGroup(IGrouping typePairGroup) public IList TypePairs { get; } } - - private class DerivedTypeMapping - { - public static DerivedTypeMapping For( - DerivedTypePair derivedTypePair, - IObjectMappingData declaredTypeMappingData) - { - var mapping = DerivedMappingFactory.GetDerivedTypeMapping( - declaredTypeMappingData, - declaredTypeMappingData.MapperData.SourceObject, - derivedTypePair.DerivedTargetType); - - return new DerivedTypeMapping - { - Condition = GetTypePairCondition(derivedTypePair, declaredTypeMappingData.MapperData), - Mapping = mapping - }; - } - - public Expression Condition { get; private set; } - - public bool IsUnconditional => Condition == null; - - public Expression Mapping { get; private set; } - } } } \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs index f9e3b90c3..068f44938 100644 --- a/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs +++ b/AgileMapper/ObjectPopulation/ExistingOrDefaultValueDataSourceFactory.cs @@ -2,7 +2,6 @@ namespace AgileObjects.AgileMapper.ObjectPopulation { using System.Linq.Expressions; using DataSources; - using Extensions.Internal; using Members; using Members.Dictionaries; @@ -25,7 +24,7 @@ private static Expression GetValue(IMemberMapperData mapperData) if (mapperData.TargetMember.IsEnumerable) { return FallbackToNull(mapperData) - ? mapperData.TargetMember.Type.ToDefaultExpression() + ? mapperData.GetTargetMemberDefault() : mapperData.GetFallbackCollectionValue(); } @@ -34,7 +33,7 @@ private static Expression GetValue(IMemberMapperData mapperData) return mapperData.GetTargetMemberAccess(); } - return mapperData.TargetMember.Type.ToDefaultExpression(); + return mapperData.GetTargetMemberDefault(); } private static bool FallbackToNull(IBasicMapperData mapperData) diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index 8760cc52f..cf327d254 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -74,19 +74,14 @@ private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, { derivedTypeMappings = GetDerivedTypeMappings(mappingData); - switch (derivedTypeMappings.NodeType) + if (derivedTypeMappings.NodeType != Goto) { - case Conditional: - return true; - - case Goto: - var returnExpression = (GotoExpression)derivedTypeMappings; - derivedTypeMappings = returnExpression.Value; - return true; - - default: - return false; + return false; } + + var returnExpression = (GotoExpression)derivedTypeMappings; + derivedTypeMappings = returnExpression.Value; + return true; } protected virtual Expression GetDerivedTypeMappings(IObjectMappingData mappingData) => Constants.EmptyExpression; @@ -197,8 +192,6 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping { var mapperData = mappingExtras.MapperData; - Expression returnExpression; - AdjustForSingleExpressionBlockIfApplicable(ref mappingExpressions); if (mapperData.UseSingleMappingExpression()) @@ -211,6 +204,8 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping goto CreateFullMappingBlock; } + Expression returnExpression; + if (mappingExpressions[0].NodeType != Block) { if (mappingExpressions[0].NodeType == MemberAccess) @@ -218,34 +213,23 @@ private Expression GetMappingBlock(IList mappingExpressions, Mapping return GetReturnExpression(mappingExpressions[0], mappingExtras); } - if (!mapperData.Context.UseLocalVariable) - { - goto CreateFullMappingBlock; - } - - var localVariableAssignment = (BinaryExpression)mappingExpressions.First(exp => exp.NodeType == Assign); - - if ((localVariableAssignment.Left.NodeType == Parameter) && - (mappingExpressions.Last() == localVariableAssignment)) + if (TryAdjustForUnusedLocalVariableIfApplicable( + mappingExpressions, + mappingExtras, + mapperData, + out returnExpression)) { - var assignedValue = localVariableAssignment.Right; - - returnExpression = (assignedValue.NodeType == Invoke) - ? Expression.Block( - new[] { (ParameterExpression)localVariableAssignment.Left }, - GetReturnExpression(localVariableAssignment, mappingExtras)) - : GetReturnExpression(assignedValue, mappingExtras); - - if (mappingExpressions.HasOne()) - { - return returnExpression; - } - - mappingExpressions[mappingExpressions.Count - 1] = mapperData.GetReturnLabel(returnExpression); - - return Expression.Block(mappingExpressions); + return returnExpression; } } + else if (TryAdjustForUnusedLocalVariableIfApplicable( + mappingExpressions, + mappingExtras, + mapperData, + out returnExpression)) + { + return returnExpression; + } CreateFullMappingBlock: @@ -275,6 +259,56 @@ private static void AdjustForSingleExpressionBlockIfApplicable(ref IList mappingExpressions, + MappingExtras mappingExtras, + ObjectMapperData mapperData, + out Expression returnExpression) + { + if (!mapperData.Context.UseLocalVariable) + { + returnExpression = null; + return false; + } + + if (!TryGetVariableAssignment(mappingExpressions, out var localVariableAssignment)) + { + returnExpression = null; + return false; + } + + if ((localVariableAssignment.Left.NodeType != Parameter) || + (localVariableAssignment != mappingExpressions.Last())) + { + returnExpression = null; + return false; + } + + var assignedValue = localVariableAssignment.Right; + + returnExpression = (assignedValue.NodeType == Invoke) + ? Expression.Block( + new[] { (ParameterExpression)localVariableAssignment.Left }, + GetReturnExpression(localVariableAssignment, mappingExtras)) + : GetReturnExpression(assignedValue, mappingExtras); + + if (mappingExpressions.HasOne()) + { + return true; + } + + mappingExpressions[mappingExpressions.Count - 1] = mapperData.GetReturnLabel(returnExpression); + returnExpression = Expression.Block(mappingExpressions); + return true; + } + + private static bool TryGetVariableAssignment(IEnumerable mappingExpressions, out BinaryExpression binaryExpression) + { + binaryExpression = mappingExpressions.FirstOrDefault(exp => exp.NodeType == Assign) as BinaryExpression; + + return binaryExpression != null; + } + private static Expression GetReturnExpression(Expression returnValue, MappingExtras mappingExtras) { return (mappingExtras.MapToNullCondition != null) diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs index c610ecbcf..be0777109 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -22,7 +22,7 @@ public static bool TryConvert( return true; } - public class NonNullableMemberBinder : ExpressionVisitor + private class NonNullableMemberBinder : ExpressionVisitor { private readonly IQueryProviderSettings _settings; private readonly ConditionalExpression _conditional; diff --git a/AgileMapper/Queryables/Converters/DerivedTypeMappingConverter.cs b/AgileMapper/Queryables/Converters/DerivedTypeMappingConverter.cs new file mode 100644 index 000000000..11cb6c259 --- /dev/null +++ b/AgileMapper/Queryables/Converters/DerivedTypeMappingConverter.cs @@ -0,0 +1,85 @@ +namespace AgileObjects.AgileMapper.Queryables.Converters +{ + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using Extensions.Internal; + using static System.Linq.Expressions.ExpressionType; + + internal static class DerivedTypeMappingConverter + { + public static bool TryConvert( + BlockExpression mappingBlock, + IQueryProjectionModifier modifier, + out Expression converted) + { + if (IsNotDerivedTypeMappingsBlock(mappingBlock, out var derivedTypeMappings)) + { + converted = null; + return false; + } + + var fallbackValue = GetReturnedValue(mappingBlock.Expressions.Last(), modifier); + + converted = derivedTypeMappings.Reverse().Aggregate( + fallbackValue, + (mappingSoFar, derivedTypeMapping) => Expression.Condition( + derivedTypeMapping.Test, + GetReturnedValue(derivedTypeMapping.IfTrue, modifier).GetConversionTo(fallbackValue.Type), + mappingSoFar)); + + return true; + } + + private static bool IsNotDerivedTypeMappingsBlock( + BlockExpression mappingBlock, + out ICollection derivedTypeMappings) + { + derivedTypeMappings = new List(); + + var firstExpression = mappingBlock.Expressions.First(); + + switch (firstExpression.NodeType) + { + case Conditional: + derivedTypeMappings.Add((ConditionalExpression)firstExpression); + return false; + + case Block: + var nestedMappingBlock = (BlockExpression)firstExpression; + + if (nestedMappingBlock.Expressions.All(exp => exp.NodeType == Conditional)) + { + foreach (var derivedTypeMapping in nestedMappingBlock.Expressions) + { + derivedTypeMappings.Add((ConditionalExpression)derivedTypeMapping); + } + + return false; + } + + break; + } + + return true; + } + + private static Expression GetReturnedValue( + Expression returnExpression, + IQueryProjectionModifier modifier) + { + switch (returnExpression.NodeType) + { + case Label: + returnExpression = ((LabelExpression)returnExpression).DefaultValue; + break; + + case Goto: + returnExpression = ((GotoExpression)returnExpression).Value; + break; + } + + return modifier.Modify(returnExpression); + } + } +} \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 5a339e341..71aa1e386 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -39,6 +39,16 @@ protected override Expression VisitBinary(BinaryExpression binary) return base.VisitBinary(binary); } + protected override Expression VisitBlock(BlockExpression block) + { + if (DerivedTypeMappingConverter.TryConvert(block, this, out var converted)) + { + return converted; + } + + return base.VisitBlock(block); + } + protected override Expression VisitConditional(ConditionalExpression conditional) { if (ComplexTypeConditionalConverter.TryConvert(conditional, this, out var converted)) diff --git a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs index 2bd8a2692..71f36524c 100644 --- a/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs +++ b/AgileMapper/Queryables/Recursion/MapToDepthRecursiveMemberMappingStrategy.cs @@ -1,7 +1,7 @@ namespace AgileObjects.AgileMapper.Queryables.Recursion { using System.Linq.Expressions; - using Extensions.Internal; + using Members; using ObjectPopulation; using ObjectPopulation.Recursion; @@ -20,7 +20,7 @@ public Expression GetMapRecursionCallFor( var mappingValues = new MappingValues( sourceValue, - childMappingData.MapperData.TargetMember.Type.ToDefaultExpression(), + childMappingData.MapperData.GetTargetMemberDefault(), declaredTypeMapperData.EnumerableIndex); var inlineMappingBlock = MappingFactory.GetInlineMappingBlock( From 4a19bb63c7758dd49775dc565da5709dfbb532dd Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 11 Feb 2018 10:11:55 +0000 Subject: [PATCH 144/176] Expanding derived type test coverage --- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../WhenConfiguringDerivedTypes.cs | 54 ++++++++++++++++++- .../TestClasses/AnimalDto.cs | 7 +++ .../TestClasses/AnimalDtoBase.cs | 2 +- .../TestClasses/DogDto.cs | 6 ++- .../TestClasses/ElephantDto.cs | 6 ++- .../TestClasses/SnakeDto.cs | 6 ++- AgileMapper.UnitTests.Orms/TestExtensions.cs | 2 + 8 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/AnimalDto.cs diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 4cbf7306a..105ccf980 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -101,6 +101,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs index 6c56be38b..8056956f5 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -23,8 +23,9 @@ public Task ShouldProjectToConfiguredDerivedTypes() var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; var nelly = new Animal { Type = Elephant, Name = "Nelly" }; var kaa = new Animal { Type = Snake, Name = "Kaa" }; + var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; - await context.Animals.AddRange(fido, nelly, kaa); + await context.Animals.AddRange(fido, nelly, kaa, sparkles); await context.SaveChanges(); using (var mapper = Mapper.CreateNew()) @@ -44,7 +45,7 @@ public Task ShouldProjectToConfiguredDerivedTypes() var animalDtos = context .Animals .ProjectUsing(mapper).To() - .OrderBy(a => a.Id) + .OrderBy(a => a != null ? a.Id : 1000) .ToArray(); animalDtos.First().ShouldBeOfType(); @@ -61,6 +62,55 @@ public Task ShouldProjectToConfiguredDerivedTypes() animalDtos.Third().Id.ShouldBe(kaa.Id); animalDtos.Third().Name.ShouldBe("Kaa"); animalDtos.Third().Sound.ShouldBe("Hiss"); + + animalDtos.Fourth().ShouldBeNull(); + } + }); + } + + [Fact] + public Task ShouldProjectToAFallbackDerivedType() + { + return RunTest(async context => + { + var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; + var nelly = new Animal { Type = Elephant, Name = "Nelly", Sound = "HrrrRRRRRRR" }; + var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; + + await context.Animals.AddRange(fido, nelly, sparkles); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type != Dog) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("HrrrRRRRRRR"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(sparkles.Id); + animalDtos.Third().Name.ShouldBe("Sparkles"); + animalDtos.Third().Sound.ShouldBe("Wheeeee!"); } }); } diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AnimalDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDto.cs new file mode 100644 index 000000000..e8fdbff61 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDto.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class AnimalDto : AnimalDtoBase + { + public override string Sound { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs index 5bd090edb..c6008ef98 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/AnimalDtoBase.cs @@ -6,6 +6,6 @@ public abstract class AnimalDtoBase public string Name { get; set; } - public abstract string Sound { get; } + public abstract string Sound { get; set; } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs index 5aeb2423e..1bb2c8722 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/DogDto.cs @@ -2,6 +2,10 @@ { public class DogDto : AnimalDtoBase { - public override string Sound => "Woof"; + public override string Sound + { + get => "Woof"; + set { } + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs index ce11e151c..aa6700c04 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/ElephantDto.cs @@ -2,6 +2,10 @@ { public class ElephantDto : AnimalDtoBase { - public override string Sound => "Trumpet"; + public override string Sound + { + get => "Trumpet"; + set { } + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs index dafe71cf9..db8bb6067 100644 --- a/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs +++ b/AgileMapper.UnitTests.Orms/TestClasses/SnakeDto.cs @@ -2,6 +2,10 @@ { public class SnakeDto : AnimalDtoBase { - public override string Sound => "Hiss"; + public override string Sound + { + get => "Hiss"; + set { } + } } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestExtensions.cs b/AgileMapper.UnitTests.Orms/TestExtensions.cs index ab7d8984f..ee7b280bf 100644 --- a/AgileMapper.UnitTests.Orms/TestExtensions.cs +++ b/AgileMapper.UnitTests.Orms/TestExtensions.cs @@ -12,6 +12,8 @@ public static class TestExtensions public static T Third(this IEnumerable items) => items.ElementAt(2); + public static T Fourth(this IEnumerable items) => items.ElementAt(3); + public static string GetTableName(this string traceString) => _tableNameMatcher.Match(traceString).Groups["table"].Value; } From f4a5415813a7d81d68377795329096a6eb43c7da Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 11 Feb 2018 17:34:14 +0000 Subject: [PATCH 145/176] Support for projecting source types which have derived types / Support for creation of mappers and projectors for the same types --- .../Infrastructure/Ef5TestDbContext.cs | 4 + .../Infrastructure/Ef6TestDbContext.cs | 4 + .../Infrastructure/EfCore1TestDbContext.cs | 6 +- .../Infrastructure/EfCore2TestDbContext.cs | 9 +- .../AgileMapper.UnitTests.Orms.csproj | 6 + .../WhenConfiguringDerivedTypes.cs | 59 ++++ .../Infrastructure/ITestDbContext.cs | 2 + .../Infrastructure/OrmTestClassBase.cs | 1 + .../TestClasses/Circle.cs | 6 + .../TestClasses/CircleViewModel.cs | 7 + .../TestClasses/Shape.cs | 18 ++ .../TestClasses/ShapeViewModel.cs | 11 + .../TestClasses/Square.cs | 6 + .../TestClasses/SquareViewModel.cs | 7 + .../WhenConfiguringDataSourcesIncorrectly.cs | 2 +- .../Extensions/Internal/ExpressionEquator.cs | 292 ------------------ .../Internal/ExpressionEvaluation.cs | 5 +- AgileMapper/MappingRuleSetCollection.cs | 3 + AgileMapper/MappingRuleSetSettings.cs | 2 + .../ComplexTypeConstructionFactory.cs | 2 +- .../DerivedComplexTypeMappingsFactory.cs | 16 +- .../ObjectPopulation/IMappingDataOwner.cs | 7 + .../ObjectPopulation/IRootMapperKey.cs | 7 + .../ObjectPopulation/ITypedMapperKey.cs | 9 + .../ObjectPopulation/ObjectMapperKeyBase.cs | 4 +- .../ObjectPopulation/RootObjectMapperKey.cs | 15 +- .../SourceMemberTypeDependentKeyBase.cs | 2 +- AgileMapper/Queryables/QueryProjectorKey.cs | 4 +- 28 files changed, 201 insertions(+), 315 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Circle.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/CircleViewModel.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Shape.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/ShapeViewModel.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Square.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/SquareViewModel.cs delete mode 100644 AgileMapper/Extensions/Internal/ExpressionEquator.cs create mode 100644 AgileMapper/ObjectPopulation/IMappingDataOwner.cs create mode 100644 AgileMapper/ObjectPopulation/IRootMapperKey.cs create mode 100644 AgileMapper/ObjectPopulation/ITypedMapperKey.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 3b5d0b1db..890b476a3 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -21,6 +21,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet Animals { get; set; } + public DbSet Shapes { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -69,6 +71,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new Ef5DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.Shapes => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new Ef5DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 52dfbf659..25a172df7 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -21,6 +21,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet Animals { get; set; } + public DbSet Shapes { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -69,6 +71,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new Ef6DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.Shapes => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index d1439733d..45ab3bcc9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -17,7 +17,9 @@ public EfCore1TestDbContext() { } - public DbSet Animals { get; } + public DbSet Animals { get; set; } + + public DbSet Shapes { get; set; } public DbSet Companies { get; set; } @@ -73,6 +75,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new EfCore1DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.Shapes => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index d4b063485..34ab8ecc4 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -26,7 +26,9 @@ protected EfCore2TestDbContext(DbContextOptions options) } public DbSet Animals { get; set; } - + + public DbSet Shapes { get; set; } + public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -75,6 +77,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(c => c.SubCategories) .HasForeignKey(c => c.ParentCategoryId); + modelBuilder.Entity(); + modelBuilder.Entity(); + base.OnModelCreating(modelBuilder); } @@ -82,6 +87,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.Animals => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Shapes => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Companies => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.Employees => new EfCore2DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 105ccf980..38b39a1fb 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -105,6 +105,8 @@ + + @@ -132,7 +134,11 @@ + + + + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs index 8056956f5..7969729bb 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -114,5 +114,64 @@ public Task ShouldProjectToAFallbackDerivedType() } }); } + + [Fact] + public Task ShouldNotAttemptToApplyDerivedSourceTypePairing() + { + return RunTest(async context => + { + var square = new Square { Name = "Square", NumberOfSides = 4, SideLength = 10 }; + var circle = new Circle { Name = "Circle", Diameter = 5 }; + + var shapes = new Shape[] { square, circle }; + + using (var mapper = Mapper.CreateNew()) + { + var mappedShapeVms = mapper.Map(shapes).ToANew(); + + mappedShapeVms.Length.ShouldBe(2); + + mappedShapeVms.First().ShouldBeOfType(); + mappedShapeVms.Second().ShouldBeOfType(); + + await context.Shapes.AddRange(square, circle); + await context.SaveChanges(); + + mapper.WhenMapping + .From() + .ProjectedTo() + .If(s => s.Name == "Square") + .MapTo() + .But + .If(s => s.Name == "Circle") + .MapTo(); + + var shapeVms = context + .Shapes + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + shapeVms.Length.ShouldBe(2); + + var squareVm = shapeVms.First() as SquareViewModel; + squareVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + squareVm.Id.ShouldBe(square.Id); + squareVm.Name.ShouldBe(square.Name); + squareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + squareVm.SideLength.ShouldBe(square.SideLength); + + var circleVm = shapeVms.Second() as CircleViewModel; + circleVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + circleVm.Id.ShouldBe(circle.Id); + circleVm.Name.ShouldBe(circle.Name); + circleVm.Diameter.ShouldBe(circle.Diameter); + } + }); + } } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index bf2f81023..6d676d11f 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -8,6 +8,8 @@ public interface ITestDbContext : IDisposable { IDbSetWrapper Animals { get; } + IDbSetWrapper Shapes { get; } + IDbSetWrapper Companies { get; } IDbSetWrapper Employees { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 4b2c4cdb3..f4953c476 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -60,6 +60,7 @@ protected async Task RunTest(Func test) private async Task EmptyDbContext() { Context.Animals.Clear(); + Context.Shapes.Clear(); Context.Companies.Clear(); Context.Employees.Clear(); Context.Categories.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Circle.cs b/AgileMapper.UnitTests.Orms/TestClasses/Circle.cs new file mode 100644 index 000000000..48a218dbe --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Circle.cs @@ -0,0 +1,6 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class Circle : Shape + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/CircleViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/CircleViewModel.cs new file mode 100644 index 000000000..a148e29bf --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/CircleViewModel.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class CircleViewModel : ShapeViewModel + { + public int Diameter { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Shape.cs b/AgileMapper.UnitTests.Orms/TestClasses/Shape.cs new file mode 100644 index 000000000..55d31a53e --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Shape.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class Shape + { + [Key] + public int Id { get; set; } + + public string Name { get; set; } + + public int? NumberOfSides { get; set; } + + public int? SideLength { get; set; } + + public int? Diameter { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/ShapeViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/ShapeViewModel.cs new file mode 100644 index 000000000..c14bf0a9b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/ShapeViewModel.cs @@ -0,0 +1,11 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class ShapeViewModel + { + public int Id { get; set; } + + public string Name { get; set; } + + public int? NumberOfSides { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Square.cs b/AgileMapper.UnitTests.Orms/TestClasses/Square.cs new file mode 100644 index 000000000..9efdb97b4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Square.cs @@ -0,0 +1,6 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class Square : Shape + { + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/SquareViewModel.cs b/AgileMapper.UnitTests.Orms/TestClasses/SquareViewModel.cs new file mode 100644 index 000000000..6b50c5397 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/SquareViewModel.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class SquareViewModel : ShapeViewModel + { + public int SideLength { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs index 126810621..4e43840d0 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs @@ -93,7 +93,7 @@ public void ShouldErrorIfDuplicateDataSourceIsConfigured() .To(x => x.Value); mapper.WhenMapping - .From() + .From() .To>() .Map((p, x) => p.Id) .To(x => x.Value); diff --git a/AgileMapper/Extensions/Internal/ExpressionEquator.cs b/AgileMapper/Extensions/Internal/ExpressionEquator.cs deleted file mode 100644 index 16d9308b9..000000000 --- a/AgileMapper/Extensions/Internal/ExpressionEquator.cs +++ /dev/null @@ -1,292 +0,0 @@ -namespace AgileObjects.AgileMapper.Extensions.Internal -{ - using System; - using System.Collections.Generic; - using System.Linq.Expressions; - using NetStandardPolyfills; - - internal class ExpressionEquator : IEqualityComparer - { - public static IEqualityComparer Instance = new ExpressionEquator(); - - public bool Equals(Expression x, Expression y) - { - while (true) - { - if (x.NodeType != y.NodeType) - { - return false; - } - - switch (x.NodeType) - { - case ExpressionType.Call: - return AreEqual((MethodCallExpression)x, (MethodCallExpression)y); - - case ExpressionType.Conditional: - return AreEqual((ConditionalExpression)x, (ConditionalExpression)y); - - case ExpressionType.Constant: - return AreEqual((ConstantExpression)x, (ConstantExpression)y); - - case ExpressionType.ArrayLength: - case ExpressionType.Convert: - case ExpressionType.Negate: - case ExpressionType.NegateChecked: - case ExpressionType.Not: - case ExpressionType.TypeAs: - return AreEqual((UnaryExpression)x, (UnaryExpression)y); - - case ExpressionType.Index: - return AreEqual((IndexExpression)x, (IndexExpression)y); - - case ExpressionType.Lambda: - x = ((LambdaExpression)x).Body; - y = ((LambdaExpression)y).Body; - continue; - - case ExpressionType.ListInit: - return AreEqual((ListInitExpression)x, (ListInitExpression)y); - - case ExpressionType.MemberAccess: - return AreEqual((MemberExpression)x, (MemberExpression)y); - - case ExpressionType.Add: - case ExpressionType.AddChecked: - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.ArrayIndex: - case ExpressionType.Coalesce: - case ExpressionType.Divide: - case ExpressionType.Equal: - case ExpressionType.ExclusiveOr: - case ExpressionType.GreaterThan: - case ExpressionType.GreaterThanOrEqual: - case ExpressionType.LeftShift: - case ExpressionType.LessThan: - case ExpressionType.LessThanOrEqual: - case ExpressionType.Modulo: - case ExpressionType.Multiply: - case ExpressionType.MultiplyChecked: - case ExpressionType.NotEqual: - case ExpressionType.Or: - case ExpressionType.OrElse: - case ExpressionType.RightShift: - case ExpressionType.Subtract: - case ExpressionType.SubtractChecked: - return AreEqual((BinaryExpression)x, (BinaryExpression)y); - - case ExpressionType.MemberInit: - return AreEqual((MemberInitExpression)x, (MemberInitExpression)y); - - case ExpressionType.New: - return AreEqual((NewExpression)x, (NewExpression)y); - - case ExpressionType.NewArrayBounds: - case ExpressionType.NewArrayInit: - return AreEqual((NewArrayExpression)x, (NewArrayExpression)y); - - case ExpressionType.Parameter: - return AreEqual((ParameterExpression)x, (ParameterExpression)y); - - case ExpressionType.Quote: - x = ((UnaryExpression)x).Operand; - y = ((UnaryExpression)y).Operand; - continue; - - case ExpressionType.TypeIs: - return AreEqual((TypeBinaryExpression)x, (TypeBinaryExpression)y); - } - - throw new NotImplementedException("Unable to equate Expressions of type " + x.NodeType); - } - } - - private bool AllEqual(IList xItems, IList yItems) - { - if (xItems.Count != yItems.Count) - { - return false; - } - - for (var i = 0; i < xItems.Count; i++) - { - if (!Equals(xItems[i], yItems[i])) - { - return false; - } - } - - return true; - } - - private bool AreEqual(MethodCallExpression x, MethodCallExpression y) - { - return ReferenceEquals(x.Method, y.Method) && - (((x.Object == null) && (y.Object == null)) || ((x.Object != null) && Equals(x.Object, y.Object))) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(ConditionalExpression x, ConditionalExpression y) - { - return (x.Type == y.Type) && Equals(x.Test, y.Test) && - Equals(x.IfTrue, y.IfTrue) && Equals(x.IfFalse, y.IfFalse); - } - - private static bool AreEqual(ConstantExpression x, ConstantExpression y) - { - return ReferenceEquals(x.Value, y.Value) || x.Value.Equals(y.Value); - } - - private bool AreEqual(UnaryExpression x, UnaryExpression y) - { - return (x.Type == y.Type) && Equals(x.Operand, y.Operand); - } - - private bool AreEqual(IndexExpression x, IndexExpression y) - { - return ReferenceEquals(x.Indexer, y.Indexer) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(ListInitExpression x, ListInitExpression y) - { - return (x.Type == y.Type) && Equals(x.NewExpression, y.NewExpression) && - AllEqual(x.Initializers, y.Initializers); - } - - private bool AllEqual(IList xInitializers, IList yInitializers) - { - if (xInitializers.Count != yInitializers.Count) - { - return false; - } - - for (var i = 0; i < xInitializers.Count; i++) - { - var x = xInitializers[i]; - var y = yInitializers[i]; - - if (!ReferenceEquals(x.AddMethod, y.AddMethod) || - !AllEqual(x.Arguments, y.Arguments)) - { - return false; - } - } - - return true; - } - - private static bool AreEqual(MemberExpression x, MemberExpression y) - { - if (ReferenceEquals(x.Member, y.Member)) - { - return true; - } - - // ReSharper disable once PossibleNullReferenceException - return (x.Member.Name == y.Member.Name) && - x.Member.DeclaringType.IsAssignableTo(y.Member.DeclaringType); - } - - private bool AreEqual(BinaryExpression x, BinaryExpression y) - { - return ReferenceEquals(x.Method, y.Method) && - Equals(x.Left, y.Left) && Equals(x.Right, y.Right); - } - - private bool AreEqual(MemberInitExpression x, MemberInitExpression y) - { - return Equals(x.NewExpression, y.NewExpression) && - AllEqual(x.Bindings, y.Bindings); - } - - private bool AllEqual(IList xBindings, IList yBindings) - { - if (xBindings.Count != yBindings.Count) - { - return false; - } - - for (var i = 0; i < xBindings.Count; i++) - { - var x = xBindings[i]; - var y = yBindings[i]; - - if ((x.BindingType != y.BindingType) || !ReferenceEquals(x.Member, y.Member)) - { - return false; - } - - switch (x.BindingType) - { - case MemberBindingType.Assignment: - if (Equals(((MemberAssignment)x).Expression, ((MemberAssignment)y).Expression)) - { - break; - } - return false; - - case MemberBindingType.MemberBinding: - if (AllEqual(((MemberMemberBinding)x).Bindings, ((MemberMemberBinding)y).Bindings)) - { - break; - } - return false; - - case MemberBindingType.ListBinding: - if (AreEqual((MemberListBinding)x, (MemberListBinding)y)) - { - break; - } - return false; - } - } - - return true; - } - - private bool AreEqual(MemberListBinding x, MemberListBinding y) - { - if (x.Initializers.Count != y.Initializers.Count) - { - return false; - } - - for (var i = 0; i < x.Initializers.Count; i++) - { - var xInitialiser = x.Initializers[i]; - var yInitialiser = y.Initializers[i]; - - if (!ReferenceEquals(xInitialiser.AddMethod, yInitialiser.AddMethod) || - !AllEqual(xInitialiser.Arguments, yInitialiser.Arguments)) - { - return false; - } - } - - return true; - } - - private bool AreEqual(NewExpression x, NewExpression y) - { - return (x.Type == y.Type) && ReferenceEquals(x.Constructor, y.Constructor) && - AllEqual(x.Arguments, y.Arguments); - } - - private bool AreEqual(NewArrayExpression x, NewArrayExpression y) - { - return (x.Type == y.Type) && AllEqual(x.Expressions, y.Expressions); - } - - private static bool AreEqual(ParameterExpression x, ParameterExpression y) - { - return (x.Type == y.Type) && (x.Name == y.Name); - } - - private bool AreEqual(TypeBinaryExpression x, TypeBinaryExpression y) - => (x.TypeOperand == y.TypeOperand) && Equals(x.Expression, y.Expression); - - public int GetHashCode(Expression obj) => 0; - } -} \ No newline at end of file diff --git a/AgileMapper/Extensions/Internal/ExpressionEvaluation.cs b/AgileMapper/Extensions/Internal/ExpressionEvaluation.cs index fc3ddeec9..00590268f 100644 --- a/AgileMapper/Extensions/Internal/ExpressionEvaluation.cs +++ b/AgileMapper/Extensions/Internal/ExpressionEvaluation.cs @@ -18,9 +18,7 @@ internal static class ExpressionEvaluation #region Member Access Evaluation private static bool MemberAccessesAreEqual(ExpressionEquator equator, MemberExpression x, MemberExpression y) - { - return MemberAccessesAreEquivalent(equator, x, y) && equator.Equals(x.Expression, y.Expression); - } + => MemberAccessesAreEquivalent(equator, x, y) && equator.Equals(x.Expression, y.Expression); private static bool MemberAccessesAreEquivalent(ExpressionEquator equator, MemberExpression x, MemberExpression y) { @@ -60,7 +58,6 @@ public bool Equals(Expression x, Expression y) case ExpressionType.Call: return AreEqual((MethodCallExpression)x, (MethodCallExpression)y); - case ExpressionType.Conditional: return AreEqual((ConditionalExpression)x, (ConditionalExpression)y); diff --git a/AgileMapper/MappingRuleSetCollection.cs b/AgileMapper/MappingRuleSetCollection.cs index 06dd8cfc6..21012af63 100644 --- a/AgileMapper/MappingRuleSetCollection.cs +++ b/AgileMapper/MappingRuleSetCollection.cs @@ -18,6 +18,7 @@ internal class MappingRuleSetCollection { SourceElementsCouldBeNull = true, UseTryCatch = true, + CheckDerivedSourceTypes = true, GuardMemberAccesses = value => true, AllowObjectTracking = true, AllowGetMethods = true, @@ -35,6 +36,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, + CheckDerivedSourceTypes = true, GuardMemberAccesses = value => true, AllowObjectTracking = true, AllowGetMethods = true, @@ -52,6 +54,7 @@ internal class MappingRuleSetCollection RootHasPopulatedTarget = true, SourceElementsCouldBeNull = true, UseTryCatch = true, + CheckDerivedSourceTypes = true, GuardMemberAccesses = value => true, AllowObjectTracking = true, AllowGetMethods = true, diff --git a/AgileMapper/MappingRuleSetSettings.cs b/AgileMapper/MappingRuleSetSettings.cs index f579fbcad..6f858924e 100644 --- a/AgileMapper/MappingRuleSetSettings.cs +++ b/AgileMapper/MappingRuleSetSettings.cs @@ -16,6 +16,8 @@ internal class MappingRuleSetSettings public bool UseTryCatch { get; set; } + public bool CheckDerivedSourceTypes { get; set; } + public Func GuardMemberAccesses { get; set; } public bool AllowEnumerableAssignment { get; set; } diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs index 9d0543d70..3c24e12e6 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/ComplexTypeConstructionFactory.cs @@ -167,7 +167,7 @@ private static ConstructorData CreateConstructorData(ConstructorInfo ctor, Const #region Helper Classes - private class ConstructionKey : SourceMemberTypeDependentKeyBase + private class ConstructionKey : SourceMemberTypeDependentKeyBase, IMappingDataOwner { private readonly MappingRuleSet _ruleSet; private readonly IQualifiedMember _sourceMember; diff --git a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs index ce95e05b8..7cc8eb4ba 100644 --- a/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs +++ b/AgileMapper/ObjectPopulation/DerivedComplexTypeMappingsFactory.cs @@ -19,7 +19,10 @@ public static Expression CreateFor(IObjectMappingData declaredTypeMappingData) return Constants.EmptyExpression; } - var derivedSourceTypes = declaredTypeMapperData.GetDerivedSourceTypes(); + var derivedSourceTypes = declaredTypeMapperData.RuleSet.Settings.CheckDerivedSourceTypes + ? declaredTypeMapperData.GetDerivedSourceTypes() + : Enumerable.EmptyArray; + var derivedTargetTypes = GetDerivedTargetTypesIfNecessary(declaredTypeMappingData); var derivedTypePairs = GetTypePairsFor(declaredTypeMapperData, declaredTypeMapperData); @@ -128,18 +131,23 @@ private static Expression GetTypePairCondition(DerivedTypePair derivedTypePair, } private static void AddDerivedSourceTypeMappings( - IEnumerable derivedSourceTypes, + ICollection derivedSourceTypes, IObjectMappingData declaredTypeMappingData, ICollection typedObjectVariables, IList derivedTypeMappingExpressions) { + if (derivedSourceTypes.None()) + { + return; + } + var declaredTypeMapperData = declaredTypeMappingData.MapperData; var insertionOffset = derivedTypeMappingExpressions.Count; - derivedSourceTypes = derivedSourceTypes + var orderedDerivedSourceTypes = derivedSourceTypes .OrderBy(t => t, TypeComparer.MostToLeastDerived); - foreach (var derivedSourceType in derivedSourceTypes) + foreach (var derivedSourceType in orderedDerivedSourceTypes) { var derivedSourceCheck = new DerivedSourceTypeCheck(derivedSourceType); var typedVariableAssignment = derivedSourceCheck.GetTypedVariableAssignment(declaredTypeMapperData); diff --git a/AgileMapper/ObjectPopulation/IMappingDataOwner.cs b/AgileMapper/ObjectPopulation/IMappingDataOwner.cs new file mode 100644 index 000000000..e282dadce --- /dev/null +++ b/AgileMapper/ObjectPopulation/IMappingDataOwner.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation +{ + internal interface IMappingDataOwner + { + IObjectMappingData MappingData { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/IRootMapperKey.cs b/AgileMapper/ObjectPopulation/IRootMapperKey.cs new file mode 100644 index 000000000..87ceb2921 --- /dev/null +++ b/AgileMapper/ObjectPopulation/IRootMapperKey.cs @@ -0,0 +1,7 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation +{ + internal interface IRootMapperKey : ITypedMapperKey + { + MappingRuleSet RuleSet { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ITypedMapperKey.cs b/AgileMapper/ObjectPopulation/ITypedMapperKey.cs new file mode 100644 index 000000000..aba8d6233 --- /dev/null +++ b/AgileMapper/ObjectPopulation/ITypedMapperKey.cs @@ -0,0 +1,9 @@ +namespace AgileObjects.AgileMapper.ObjectPopulation +{ + using Members; + + internal interface ITypedMapperKey : IMappingDataOwner + { + MappingTypes MappingTypes { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/ObjectPopulation/ObjectMapperKeyBase.cs b/AgileMapper/ObjectPopulation/ObjectMapperKeyBase.cs index 2a50f0f2e..1cf85776d 100644 --- a/AgileMapper/ObjectPopulation/ObjectMapperKeyBase.cs +++ b/AgileMapper/ObjectPopulation/ObjectMapperKeyBase.cs @@ -3,7 +3,7 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Members; using Members.Sources; - internal abstract class ObjectMapperKeyBase : SourceMemberTypeDependentKeyBase + internal abstract class ObjectMapperKeyBase : SourceMemberTypeDependentKeyBase, ITypedMapperKey { protected ObjectMapperKeyBase(MappingTypes mappingTypes) { @@ -12,7 +12,7 @@ protected ObjectMapperKeyBase(MappingTypes mappingTypes) public MappingTypes MappingTypes { get; } - protected bool TypesMatch(ObjectMapperKeyBase otherKey) => otherKey.MappingTypes.Equals(MappingTypes); + protected bool TypesMatch(ITypedMapperKey otherKey) => otherKey.MappingTypes.Equals(MappingTypes); public abstract IMembersSource GetMembersSource(IObjectMappingData parentMappingData); diff --git a/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs b/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs index 43d1ff5d2..a7d44360c 100644 --- a/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs +++ b/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs @@ -6,10 +6,9 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using ReadableExpressions.Extensions; #endif - internal class RootObjectMapperKey : ObjectMapperKeyBase + internal class RootObjectMapperKey : ObjectMapperKeyBase, IRootMapperKey { private readonly MapperContext _mapperContext; - private readonly MappingRuleSet _ruleSet; public RootObjectMapperKey(MappingTypes mappingTypes, IMappingContext mappingContext) : this(mappingContext.RuleSet, mappingTypes, mappingContext.MapperContext) @@ -20,22 +19,24 @@ private RootObjectMapperKey(MappingRuleSet ruleSet, MappingTypes mappingTypes, M : base(mappingTypes) { _mapperContext = mapperContext; - _ruleSet = ruleSet; + RuleSet = ruleSet; } + public MappingRuleSet RuleSet { get; } + public override IMembersSource GetMembersSource(IObjectMappingData parentMappingData) => _mapperContext.RootMembersSource; protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTypes) - => new RootObjectMapperKey(_ruleSet, newMappingTypes, _mapperContext); + => new RootObjectMapperKey(RuleSet, newMappingTypes, _mapperContext); public override bool Equals(object obj) { - var otherKey = (RootObjectMapperKey)obj; + var otherKey = (IRootMapperKey)obj; // ReSharper disable once PossibleNullReferenceException return TypesMatch(otherKey) && - (otherKey._ruleSet == _ruleSet) && + (otherKey.RuleSet == RuleSet) && SourceHasRequiredTypes(otherKey); } @@ -54,7 +55,7 @@ public override string ToString() var sourceTypeName = MappingTypes.SourceType.GetFriendlyName(); var targetTypeName = MappingTypes.TargetType.GetFriendlyName(); - return $"{_ruleSet.Name}: {sourceTypeName} -> {targetTypeName}"; + return $"{RuleSet.Name}: {sourceTypeName} -> {targetTypeName}"; } #endif #endregion diff --git a/AgileMapper/ObjectPopulation/SourceMemberTypeDependentKeyBase.cs b/AgileMapper/ObjectPopulation/SourceMemberTypeDependentKeyBase.cs index f6aa4588f..b7b7eaa21 100644 --- a/AgileMapper/ObjectPopulation/SourceMemberTypeDependentKeyBase.cs +++ b/AgileMapper/ObjectPopulation/SourceMemberTypeDependentKeyBase.cs @@ -33,7 +33,7 @@ public void AddSourceMemberTypeTesterIfRequired(IObjectMappingData mappingData = _sourceMemberTypeTester = typeTestLambda.Compile(); } - protected bool SourceHasRequiredTypes(SourceMemberTypeDependentKeyBase otherKey) + protected bool SourceHasRequiredTypes(IMappingDataOwner otherKey) => (_sourceMemberTypeTester == null) || _sourceMemberTypeTester.Invoke(otherKey.MappingData); } } \ No newline at end of file diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs index 4dce11a5c..8a80bc48d 100644 --- a/AgileMapper/Queryables/QueryProjectorKey.cs +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -5,7 +5,7 @@ using Members.Sources; using ObjectPopulation; - internal class QueryProjectorKey : ObjectMapperKeyBase + internal class QueryProjectorKey : ObjectMapperKeyBase, IRootMapperKey { private readonly MapperContext _mapperContext; @@ -19,6 +19,8 @@ public QueryProjectorKey( _mapperContext = mapperContext; } + public MappingRuleSet RuleSet => _mapperContext.RuleSets.Project; + public Type QueryProviderType { get; } public override IMembersSource GetMembersSource(IObjectMappingData parentMappingData) From dd56d16c36078d783267be76c47bd6fef83df2b7 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Sun, 11 Feb 2018 19:47:07 +0000 Subject: [PATCH 146/176] Support for cached query projectors and mappers in the same mapper --- .../Configuration/WhenConfiguringDerivedTypes.cs | 7 +++++++ AgileMapper/ObjectPopulation/RootObjectMapperKey.cs | 6 +++--- AgileMapper/Queryables/QueryProjectorKey.cs | 6 ++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs index 7969729bb..e5a901675 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -170,6 +170,13 @@ public Task ShouldNotAttemptToApplyDerivedSourceTypePairing() circleVm.Id.ShouldBe(circle.Id); circleVm.Name.ShouldBe(circle.Name); circleVm.Diameter.ShouldBe(circle.Diameter); + + var mappedSquareVm = mapper.Map(square).ToANew(); + + mappedSquareVm.Id.ShouldBe(square.Id); + mappedSquareVm.Name.ShouldBe(square.Name); + mappedSquareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + mappedSquareVm.SideLength.ShouldBe(square.SideLength); } }); } diff --git a/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs b/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs index a7d44360c..d16f8eadd 100644 --- a/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs +++ b/AgileMapper/ObjectPopulation/RootObjectMapperKey.cs @@ -35,9 +35,9 @@ public override bool Equals(object obj) var otherKey = (IRootMapperKey)obj; // ReSharper disable once PossibleNullReferenceException - return TypesMatch(otherKey) && - (otherKey.RuleSet == RuleSet) && - SourceHasRequiredTypes(otherKey); + return (otherKey.RuleSet == RuleSet) && + TypesMatch(otherKey) && + SourceHasRequiredTypes(otherKey); } #region ExcludeFromCodeCoverage diff --git a/AgileMapper/Queryables/QueryProjectorKey.cs b/AgileMapper/Queryables/QueryProjectorKey.cs index 8a80bc48d..061825687 100644 --- a/AgileMapper/Queryables/QueryProjectorKey.cs +++ b/AgileMapper/Queryables/QueryProjectorKey.cs @@ -31,10 +31,12 @@ protected override ObjectMapperKeyBase CreateInstance(MappingTypes newMappingTyp public override bool Equals(object obj) { - var otherKey = (QueryProjectorKey)obj; + var otherKey = (IRootMapperKey)obj; // ReSharper disable once PossibleNullReferenceException - return TypesMatch(otherKey) && (otherKey.QueryProviderType == QueryProviderType); + return (otherKey.RuleSet == RuleSet) && + TypesMatch(otherKey) && + (((QueryProjectorKey)otherKey).QueryProviderType == QueryProviderType); } #region ExcludeFromCodeCoverage From 7da177dcf67946e46c41dc54d7cb94584056b382 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 12 Feb 2018 09:23:35 +0000 Subject: [PATCH 147/176] Extending derived type testing --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringDerivedTypes.cs | 25 ++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringDerivedTypes.cs | 25 ++ .../Infrastructure/Ef6TestDbContext.cs | 4 +- .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringDerivedTypes.cs | 24 ++ .../Infrastructure/EfCore1TestDbContext.cs | 11 +- .../WhenConfiguringDerivedTypes.cs | 11 + .../WhenConfiguringDerivedTypes.cs | 302 ++++++++++-------- 10 files changed, 257 insertions(+), 148 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDerivedTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDerivedTypes.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDerivedTypes.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 5bdbce79c..53f732c3e 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -87,6 +87,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDerivedTypes.cs new file mode 100644 index 000000000..ab0a2ecc6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringDerivedTypes.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDerivedTypes : WhenConfiguringDerivedTypes + { + public WhenConfiguringDerivedTypes(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorProjectingToConfiguredDerivedTypes() => RunShouldErrorProjectingToConfiguredDerivedTypes(); + + [Fact] + public Task ShouldErrorProjectingToAFallbackDerivedType() => RunShouldErrorProjectingToAFallbackDerivedType(); + + [Fact] + public Task ShouldErrorAttemptingToNotApplyDerivedSourceTypePairing() + => RunShouldErrorAttemptingToNotApplyDerivedSourceTypePairing(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index a1ddbc8e2..3d0b42356 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDerivedTypes.cs new file mode 100644 index 000000000..68ab1a7dc --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringDerivedTypes.cs @@ -0,0 +1,25 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDerivedTypes : WhenConfiguringDerivedTypes + { + public WhenConfiguringDerivedTypes(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorProjectingToConfiguredDerivedTypes() => RunShouldErrorProjectingToConfiguredDerivedTypes(); + + [Fact] + public Task ShouldErrorProjectingToAFallbackDerivedType() => RunShouldErrorProjectingToAFallbackDerivedType(); + + [Fact] + public Task ShouldErrorAttemptingToNotApplyDerivedSourceTypePairing() + => RunShouldErrorAttemptingToNotApplyDerivedSourceTypePairing(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 25a172df7..2795f9d01 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -21,7 +21,7 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet Animals { get; set; } - public DbSet Shapes { get; set; } + public DbSet Shapes { get; set; } public DbSet Companies { get; set; } @@ -71,7 +71,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new Ef6DbSetWrapper(this); - + IDbSetWrapper ITestDbContext.Shapes => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index a7f08d82e..64700230e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -215,6 +215,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDerivedTypes.cs new file mode 100644 index 000000000..dc4377ca9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringDerivedTypes.cs @@ -0,0 +1,24 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringDerivedTypes : WhenConfiguringDerivedTypes + { + public WhenConfiguringDerivedTypes(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToConfiguredDerivedTypes() => RunShouldProjectToConfiguredDerivedTypes(); + + [Fact] + public Task ShouldProjectToAFallbackDerivedType() => RunShouldProjectToAFallbackDerivedType(); + + [Fact] + public Task ShouldNotAttemptToApplyDerivedSourceTypePairing() => RunShouldNotAttemptToApplyDerivedSourceTypePairing(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 45ab3bcc9..ea7bcae4e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -17,10 +17,10 @@ public EfCore1TestDbContext() { } - public DbSet Animals { get; set; } + public DbSet Animals { get; set; } + + public DbSet Shapes { get; set; } - public DbSet Shapes { get; set; } - public DbSet Companies { get; set; } public DbSet Employees { get; set; } @@ -69,13 +69,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(c => c.SubCategories) .HasForeignKey(c => c.ParentCategoryId); + modelBuilder.Entity(); + modelBuilder.Entity(); + base.OnModelCreating(modelBuilder); } #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new EfCore1DbSetWrapper(this); - + IDbSetWrapper ITestDbContext.Shapes => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs index 5cde12c41..a6c32681a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringDerivedTypes.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; + using Xunit; public class WhenConfiguringDerivedTypes : WhenConfiguringDerivedTypes { @@ -9,5 +11,14 @@ public WhenConfiguringDerivedTypes(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectToConfiguredDerivedTypes() => RunShouldProjectToConfiguredDerivedTypes(); + + [Fact] + public Task ShouldProjectToAFallbackDerivedType() => RunShouldProjectToAFallbackDerivedType(); + + [Fact] + public Task ShouldNotAttemptToApplyDerivedSourceTypePairing() => RunShouldNotAttemptToApplyDerivedSourceTypePairing(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs index e5a901675..a6ae7fd8d 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -15,170 +15,188 @@ protected WhenConfiguringDerivedTypes(ITestContext context) { } - [Fact] - public Task ShouldProjectToConfiguredDerivedTypes() - { - return RunTest(async context => - { - var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; - var nelly = new Animal { Type = Elephant, Name = "Nelly" }; - var kaa = new Animal { Type = Snake, Name = "Kaa" }; - var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; + #region Project -> Derived Types - await context.Animals.AddRange(fido, nelly, kaa, sparkles); - await context.SaveChanges(); + protected Task RunShouldProjectToConfiguredDerivedTypes() + => RunTest(DoShouldProjectToConfiguredDerivedTypes); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(a => a.Type == Dog) - .MapTo() - .And - .If(a => a.Type == Elephant) - .MapTo() - .And - .If(a => a.Type == Snake) - .MapTo(); - - var animalDtos = context - .Animals - .ProjectUsing(mapper).To() - .OrderBy(a => a != null ? a.Id : 1000) - .ToArray(); - - animalDtos.First().ShouldBeOfType(); - animalDtos.First().Id.ShouldBe(fido.Id); - animalDtos.First().Name.ShouldBe("Fido"); - animalDtos.First().Sound.ShouldBe("Woof"); - - animalDtos.Second().ShouldBeOfType(); - animalDtos.Second().Id.ShouldBe(nelly.Id); - animalDtos.Second().Name.ShouldBe("Nelly"); - animalDtos.Second().Sound.ShouldBe("Trumpet"); - - animalDtos.Third().ShouldBeOfType(); - animalDtos.Third().Id.ShouldBe(kaa.Id); - animalDtos.Third().Name.ShouldBe("Kaa"); - animalDtos.Third().Sound.ShouldBe("Hiss"); - - animalDtos.Fourth().ShouldBeNull(); - } - }); - } + protected Task RunShouldErrorProjectingToConfiguredDerivedTypes() + => RunTestAndExpectThrow(DoShouldProjectToConfiguredDerivedTypes); - [Fact] - public Task ShouldProjectToAFallbackDerivedType() + private static async Task DoShouldProjectToConfiguredDerivedTypes(TOrmContext context) { - return RunTest(async context => - { - var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; - var nelly = new Animal { Type = Elephant, Name = "Nelly", Sound = "HrrrRRRRRRR" }; - var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; + var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; + var nelly = new Animal { Type = Elephant, Name = "Nelly" }; + var kaa = new Animal { Type = Snake, Name = "Kaa" }; + var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; - await context.Animals.AddRange(fido, nelly, sparkles); - await context.SaveChanges(); + await context.Animals.AddRange(fido, nelly, kaa, sparkles); + await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(a => a.Type == Dog) - .MapTo() - .And - .If(a => a.Type != Dog) - .MapTo(); - - var animalDtos = context - .Animals - .ProjectUsing(mapper).To() - .OrderBy(a => a.Id) - .ToArray(); - - animalDtos.First().ShouldBeOfType(); - animalDtos.First().Id.ShouldBe(fido.Id); - animalDtos.First().Name.ShouldBe("Fido"); - animalDtos.First().Sound.ShouldBe("Woof"); - - animalDtos.Second().ShouldBeOfType(); - animalDtos.Second().Id.ShouldBe(nelly.Id); - animalDtos.Second().Name.ShouldBe("Nelly"); - animalDtos.Second().Sound.ShouldBe("HrrrRRRRRRR"); - - animalDtos.Third().ShouldBeOfType(); - animalDtos.Third().Id.ShouldBe(sparkles.Id); - animalDtos.Third().Name.ShouldBe("Sparkles"); - animalDtos.Third().Sound.ShouldBe("Wheeeee!"); - } - }); + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type == Elephant) + .MapTo() + .And + .If(a => a.Type == Snake) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a != null ? a.Id : 1000) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("Trumpet"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(kaa.Id); + animalDtos.Third().Name.ShouldBe("Kaa"); + animalDtos.Third().Sound.ShouldBe("Hiss"); + + animalDtos.Fourth().ShouldBeNull(); + } } - [Fact] - public Task ShouldNotAttemptToApplyDerivedSourceTypePairing() - { - return RunTest(async context => - { - var square = new Square { Name = "Square", NumberOfSides = 4, SideLength = 10 }; - var circle = new Circle { Name = "Circle", Diameter = 5 }; + #endregion - var shapes = new Shape[] { square, circle }; + #region Project -> Fallback Derived Type - using (var mapper = Mapper.CreateNew()) - { - var mappedShapeVms = mapper.Map(shapes).ToANew(); + protected Task RunShouldProjectToAFallbackDerivedType() + => RunTest(DoShouldProjectToAFallbackDerivedType); - mappedShapeVms.Length.ShouldBe(2); + protected Task RunShouldErrorProjectingToAFallbackDerivedType() + => RunTestAndExpectThrow(DoShouldProjectToAFallbackDerivedType); - mappedShapeVms.First().ShouldBeOfType(); - mappedShapeVms.Second().ShouldBeOfType(); + private static async Task DoShouldProjectToAFallbackDerivedType(TOrmContext context) + { + var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; + var nelly = new Animal { Type = Elephant, Name = "Nelly", Sound = "HrrrRRRRRRR" }; + var sparkles = new Animal { Name = "Sparkles", Sound = "Wheeeee!" }; - await context.Shapes.AddRange(square, circle); - await context.SaveChanges(); + await context.Animals.AddRange(fido, nelly, sparkles); + await context.SaveChanges(); - mapper.WhenMapping - .From() - .ProjectedTo() - .If(s => s.Name == "Square") - .MapTo() - .But - .If(s => s.Name == "Circle") - .MapTo(); + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type != Dog) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("HrrrRRRRRRR"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(sparkles.Id); + animalDtos.Third().Name.ShouldBe("Sparkles"); + animalDtos.Third().Sound.ShouldBe("Wheeeee!"); + } + } - var shapeVms = context - .Shapes - .ProjectUsing(mapper).To() - .OrderBy(a => a.Id) - .ToArray(); + #endregion - shapeVms.Length.ShouldBe(2); + #region Project -> No Derived Source Types - var squareVm = shapeVms.First() as SquareViewModel; - squareVm.ShouldNotBeNull(); + protected Task RunShouldNotAttemptToApplyDerivedSourceTypePairing() + => RunTest(DoShouldNotAttemptToApplyDerivedSourceTypePairing); - // ReSharper disable once PossibleNullReferenceException - squareVm.Id.ShouldBe(square.Id); - squareVm.Name.ShouldBe(square.Name); - squareVm.NumberOfSides.ShouldBe(square.NumberOfSides); - squareVm.SideLength.ShouldBe(square.SideLength); + protected Task RunShouldErrorAttemptingToNotApplyDerivedSourceTypePairing() + => RunTestAndExpectThrow(DoShouldNotAttemptToApplyDerivedSourceTypePairing); - var circleVm = shapeVms.Second() as CircleViewModel; - circleVm.ShouldNotBeNull(); + private static async Task DoShouldNotAttemptToApplyDerivedSourceTypePairing(TOrmContext context) + { + var square = new Square { Name = "Square", NumberOfSides = 4, SideLength = 10 }; + var circle = new Circle { Name = "Circle", Diameter = 5 }; - // ReSharper disable once PossibleNullReferenceException - circleVm.Id.ShouldBe(circle.Id); - circleVm.Name.ShouldBe(circle.Name); - circleVm.Diameter.ShouldBe(circle.Diameter); + var shapes = new Shape[] { square, circle }; + + using (var mapper = Mapper.CreateNew()) + { + var mappedShapeVms = mapper.Map(shapes).ToANew(); - var mappedSquareVm = mapper.Map(square).ToANew(); + mappedShapeVms.Length.ShouldBe(2); - mappedSquareVm.Id.ShouldBe(square.Id); - mappedSquareVm.Name.ShouldBe(square.Name); - mappedSquareVm.NumberOfSides.ShouldBe(square.NumberOfSides); - mappedSquareVm.SideLength.ShouldBe(square.SideLength); - } - }); + mappedShapeVms.First().ShouldBeOfType(); + mappedShapeVms.Second().ShouldBeOfType(); + + await context.Shapes.AddRange(square, circle); + await context.SaveChanges(); + + mapper.WhenMapping + .From() + .ProjectedTo() + .If(s => s.Name == "Square") + .MapTo() + .But + .If(s => s.Name == "Circle") + .MapTo(); + + var shapeVms = context + .Shapes + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + shapeVms.Length.ShouldBe(2); + + var squareVm = shapeVms.First() as SquareViewModel; + squareVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + squareVm.Id.ShouldBe(square.Id); + squareVm.Name.ShouldBe(square.Name); + squareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + squareVm.SideLength.ShouldBe(square.SideLength); + + var circleVm = shapeVms.Second() as CircleViewModel; + circleVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + circleVm.Id.ShouldBe(circle.Id); + circleVm.Name.ShouldBe(circle.Name); + circleVm.Diameter.ShouldBe(circle.Diameter); + + var mappedSquareVm = mapper.Map(square).ToANew(); + + mappedSquareVm.Id.ShouldBe(square.Id); + mappedSquareVm.Name.ShouldBe(square.Name); + mappedSquareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + mappedSquareVm.SideLength.ShouldBe(square.SideLength); + } } + + #endregion } } From 67c5c8495a397d0b9b16302e0f2799530bc8ba65 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Mon, 12 Feb 2018 10:22:28 +0000 Subject: [PATCH 148/176] Ignoring configured callbacks when projecting / Reinstating converting default expressions to null constants as EF Core doesn't implement comparing default expressions --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 13 + .../AgileMapper.UnitTests.Orms.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 237 ++++++++++++++++++ .../PopulationExpressionFactoryBase.cs | 20 +- .../MappingExpressionFactoryBase.cs | 40 +-- .../ComplexTypeConditionalConverter.cs | 9 +- .../Converters/GetValueOrDefaultConverter.cs | 2 +- .../Queryables/QueryProjectionModifier.cs | 2 +- .../Settings/DefaultQueryProviderSettings.cs | 9 +- .../LegacyEfQueryProviderSettings.cs | 4 - .../Settings/IQueryProviderSettings.cs | 2 - 12 files changed, 304 insertions(+), 36 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index aaf732d31..ffd3d3b90 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -153,6 +153,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs new file mode 100644 index 000000000..6d7cd82da --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringCallbacks : WhenConfiguringCallbacks + { + public WhenConfiguringCallbacks(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 38b39a1fb..bb90927a1 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,6 +73,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs new file mode 100644 index 000000000..d5b9c92ee --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs @@ -0,0 +1,237 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenConfiguringCallbacks : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringCallbacks(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldNotAttemptToCallAPreMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .MappingBegins + .Call(ctx => callbackCalled = true); + + var square = new Square { SideLength = 12 }; + + await context.Shapes.Add(square); + await context.SaveChanges(); + + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(12); + + mapper.Map(square).OnTo(squareVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .From() + .To() + .After + .MappingEnds + .Call((s, c) => callbackCalled = true); + + var circle = new Circle { Diameter = 1 }; + + await context.Shapes.Add(circle); + await context.SaveChanges(); + + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(1); + + mapper.Map(circle).Over(circleVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPreObjectCreationCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .CreatingInstancesOf() + .Call(ctx => callbackCalled = true); + + var person = new Person { Name = "Bjorn", Address = new Address { Line1 = "Sweden" } }; + + await context.Persons.Add(person); + await context.SaveChanges(); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Bjorn"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); + + mapper.Map(person).Over(personDto); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostObjectCreationCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .After + .CreatingInstances + .Call(ctx => callbackCalled = true); + + var person = new Person { Name = "Benny", Address = new Address { Line1 = "Sweden" } }; + + await context.Persons.Add(person); + await context.SaveChanges(); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Benny"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); + + mapper.Map(person).ToANew(); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPreMemberMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .Mapping(s => s.SideLength) + .Call(ctx => callbackCalled = true); + + var square = new Square { SideLength = 1 }; + + await context.Shapes.Add(square); + await context.SaveChanges(); + + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(1); + + mapper.Map(square).Over(squareVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostMemberMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .After + .Mapping(c => c.Diameter) + .Call(ctx => callbackCalled = true); + + var circle = new Circle { Diameter = 11 }; + + await context.Shapes.Add(circle); + await context.SaveChanges(); + + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(11); + + mapper.Map(circle).Over(circleVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + } +} diff --git a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs index 657dd897f..1d05eb4c2 100644 --- a/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/ComplexTypes/PopulationExpressionFactoryBase.cs @@ -14,8 +14,9 @@ internal abstract class PopulationExpressionFactoryBase public IEnumerable GetPopulation(IObjectMappingData mappingData) { var mapperData = mappingData.MapperData; - var preCreationCallback = GetCreationCallbackOrNull(Before, mapperData); - var postCreationCallback = GetCreationCallbackOrNull(After, mapperData); + + GetCreationCallbacks(mapperData, out var preCreationCallback, out var postCreationCallback); + var populationsAndCallbacks = GetPopulationsAndCallbacks(mappingData).ToList(); yield return preCreationCallback; @@ -39,6 +40,21 @@ public IEnumerable GetPopulation(IObjectMappingData mappingData) mappingData.MapperKey.AddSourceMemberTypeTesterIfRequired(mappingData); } + private static void GetCreationCallbacks( + IMemberMapperData mapperData, + out Expression preCreationCallback, + out Expression postCreationCallback) + { + if (mapperData.RuleSet.Settings.UseSingleRootMappingExpression) + { + preCreationCallback = postCreationCallback = null; + return; + } + + preCreationCallback = GetCreationCallbackOrNull(Before, mapperData); + postCreationCallback = GetCreationCallbackOrNull(After, mapperData); + } + private static Expression GetCreationCallbackOrNull(CallbackPosition callbackPosition, IMemberMapperData mapperData) => mapperData.MapperContext.UserConfigurations.GetCreationCallbackOrNull(callbackPosition, mapperData); diff --git a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs index cf327d254..5dd6f1f44 100644 --- a/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs +++ b/AgileMapper/ObjectPopulation/MappingExpressionFactoryBase.cs @@ -7,8 +7,8 @@ namespace AgileObjects.AgileMapper.ObjectPopulation using Extensions.Internal; using Members; using NetStandardPolyfills; - using static CallbackPosition; using static System.Linq.Expressions.ExpressionType; + using static CallbackPosition; internal abstract class MappingExpressionFactoryBase { @@ -51,7 +51,7 @@ public Expression Create(IObjectMappingData mappingData) mappingExpressions.InsertRange(0, GetShortCircuitReturns(returnNull, mappingData)); - var mappingBlock = GetMappingBlock(mappingExpressions, mappingExtras); + var mappingBlock = GetMappingBlock(mappingExpressions, mappingExtras, mapperData); if (mapperData.Context.UseMappingTryCatch) { @@ -88,16 +88,20 @@ private bool MappingAlwaysBranchesToDerivedType(IObjectMappingData mappingData, private static MappingExtras GetMappingExtras(ObjectMapperData mapperData) { + var mapToNullCondition = GetMapToNullConditionOrNull(mapperData); + + if (mapperData.RuleSet.Settings.UseSingleRootMappingExpression) + { + return (mapToNullCondition != null) + ? new MappingExtras(mapToNullCondition) + : MappingExtras.Empty; + } + var basicMapperData = mapperData.WithNoTargetMember(); var preMappingCallback = basicMapperData.GetMappingCallbackOrNull(Before, mapperData); var postMappingCallback = basicMapperData.GetMappingCallbackOrNull(After, mapperData); - var mapToNullCondition = GetMapToNullConditionOrNull(mapperData); - return new MappingExtras( - mapperData, - preMappingCallback, - postMappingCallback, - mapToNullCondition); + return new MappingExtras(preMappingCallback, postMappingCallback, mapToNullCondition); } private static Expression GetMapToNullConditionOrNull(IMemberMapperData mapperData) @@ -188,10 +192,11 @@ private static bool IsMemberMapping(Expression expression) private static bool IsCallTo(string methodName, Expression methodCall) => ((MethodCallExpression)methodCall).Method.Name == methodName; - private Expression GetMappingBlock(IList mappingExpressions, MappingExtras mappingExtras) + private Expression GetMappingBlock( + IList mappingExpressions, + MappingExtras mappingExtras, + ObjectMapperData mapperData) { - var mapperData = mappingExtras.MapperData; - AdjustForSingleExpressionBlockIfApplicable(ref mappingExpressions); if (mapperData.UseSingleMappingExpression()) @@ -305,7 +310,7 @@ private static bool TryAdjustForUnusedLocalVariableIfApplicable( private static bool TryGetVariableAssignment(IEnumerable mappingExpressions, out BinaryExpression binaryExpression) { binaryExpression = mappingExpressions.FirstOrDefault(exp => exp.NodeType == Assign) as BinaryExpression; - + return binaryExpression != null; } @@ -385,20 +390,23 @@ public virtual void Reset() internal class MappingExtras { + public static readonly MappingExtras Empty = new MappingExtras(null, null, null); + + public MappingExtras(Expression mapToNullCondition) + : this(null, null, mapToNullCondition) + { + } + public MappingExtras( - ObjectMapperData mapperData, Expression preMappingCallback, Expression postMappingCallback, Expression mapToNullCondition) { - MapperData = mapperData; PreMappingCallback = preMappingCallback; PostMappingCallback = postMappingCallback; MapToNullCondition = mapToNullCondition; } - public ObjectMapperData MapperData { get; } - public Expression PreMappingCallback { get; } public Expression PostMappingCallback { get; } diff --git a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs index be0777109..92ab026fc 100644 --- a/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs +++ b/AgileMapper/Queryables/Converters/ComplexTypeConditionalConverter.cs @@ -3,7 +3,6 @@ using System.Linq.Expressions; using Extensions.Internal; using ReadableExpressions.Extensions; - using Settings; internal static class ComplexTypeConditionalConverter { @@ -18,18 +17,16 @@ public static bool TryConvert( return false; } - converted = new NonNullableMemberBinder(modifier, conditional).GuardMemberAccesses(); + converted = new NonNullableMemberBinder(conditional).GuardMemberAccesses(); return true; } private class NonNullableMemberBinder : ExpressionVisitor { - private readonly IQueryProviderSettings _settings; private readonly ConditionalExpression _conditional; - public NonNullableMemberBinder(IQueryProjectionModifier modifier, ConditionalExpression conditional) + public NonNullableMemberBinder(ConditionalExpression conditional) { - _settings = modifier.Settings; _conditional = conditional; } @@ -53,7 +50,7 @@ protected override MemberBinding VisitMemberBinding(MemberBinding binding) var bindingValueOrNull = Expression.Condition( _conditional.Test, memberBinding.Expression, - _settings.GetDefaultValueFor(memberBinding.Expression)); + DefaultValueConstantExpressionFactory.CreateFor(memberBinding.Expression)); return memberBinding.Update(bindingValueOrNull); } diff --git a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs index 8a136f94a..977db8d82 100644 --- a/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs +++ b/AgileMapper/Queryables/Converters/GetValueOrDefaultConverter.cs @@ -22,7 +22,7 @@ public static bool TryConvert( converted = Expression.Condition( methodCall.Object.GetIsNotDefaultComparison(), Expression.Convert(methodCall.Object, methodCall.Type), - context.Settings.GetDefaultValueFor(methodCall)); + DefaultValueConstantExpressionFactory.CreateFor(methodCall)); return true; } diff --git a/AgileMapper/Queryables/QueryProjectionModifier.cs b/AgileMapper/Queryables/QueryProjectionModifier.cs index 71aa1e386..72c5cb099 100644 --- a/AgileMapper/Queryables/QueryProjectionModifier.cs +++ b/AgileMapper/Queryables/QueryProjectionModifier.cs @@ -65,7 +65,7 @@ protected override Expression VisitConditional(ConditionalExpression conditional } protected override Expression VisitDefault(DefaultExpression defaultExpression) - => Settings.GetDefaultValueFor(defaultExpression); + => DefaultValueConstantExpressionFactory.CreateFor(defaultExpression); protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 7a17f4d5e..48e223a83 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; + using Converters; using Extensions.Internal; using NetStandardPolyfills; @@ -40,9 +41,6 @@ public DefaultQueryProviderSettings() public virtual bool SupportsEnumerableMaterialisation => true; - public virtual Expression GetDefaultValueFor(Expression value) - => value.NodeType != ExpressionType.Default ? value.Type.ToDefaultExpression() : value; - public virtual Expression ConvertToStringCall(MethodCallExpression call) => call.Object.GetConversionTo(); @@ -79,7 +77,7 @@ public Expression ConvertTryParseCall(MethodCallExpression call, Expression fall #region ConvertTryParseCall Helpers - private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) + private static Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, Expression fallbackValue) { var parseMethod = typeof(Guid) .GetPublicStaticMethod("Parse", parameterCount: 1); @@ -95,6 +93,9 @@ private Expression GetConvertStringToGuid(MethodCallExpression guidTryParseCall, return convertedOrFallback; } + protected static Expression GetDefaultValueFor(Expression value) + => DefaultValueConstantExpressionFactory.CreateFor(value); + protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) => null; diff --git a/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs index d6ee394e2..8afd5f9b0 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; - using Converters; using Extensions.Internal; using NetStandardPolyfills; @@ -17,9 +16,6 @@ internal abstract class LegacyEfQueryProviderSettings : DefaultQueryProviderSett public override bool SupportsEnumerableMaterialisation => false; - public override Expression GetDefaultValueFor(Expression value) - => DefaultValueConstantExpressionFactory.CreateFor(value); - protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpression call, Expression fallbackValue) { if ((CanonicalFunctionsType == null) || (SqlFunctionsType == null)) diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index e3d4d2f10..d93beb378 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -23,8 +23,6 @@ internal interface IQueryProviderSettings bool SupportsEnumerableMaterialisation { get; } - Expression GetDefaultValueFor(Expression value); - Expression ConvertToStringCall(MethodCallExpression call); Expression ConvertTryParseCall(MethodCallExpression call, Expression fallbackValue); From 589d7ec324eb743a02e4210f1ba55313f284875d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 08:27:11 +0000 Subject: [PATCH 149/176] Expanding projections + callbacks tests --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 13 +++++++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 13 +++++++++++++ .../IPreInstanceCreationCallbackSpecifier.cs | 6 +++--- .../InstanceCreationCallbackSpecifier.cs | 6 +++--- 8 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 53f732c3e..5187f02ab 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -85,6 +85,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs new file mode 100644 index 000000000..4f9454e43 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringCallbacks : WhenConfiguringCallbacks + { + public WhenConfiguringCallbacks(InMemoryEf5TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 3d0b42356..d2811b449 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,6 +87,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs new file mode 100644 index 000000000..b6bcea5b6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringCallbacks : WhenConfiguringCallbacks + { + public WhenConfiguringCallbacks(InMemoryEf6TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 64700230e..9ebc076b2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -213,6 +213,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs new file mode 100644 index 000000000..fecc75246 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringCallbacks : WhenConfiguringCallbacks + { + public WhenConfiguringCallbacks(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper/Api/Configuration/IPreInstanceCreationCallbackSpecifier.cs b/AgileMapper/Api/Configuration/IPreInstanceCreationCallbackSpecifier.cs index 6cffbaa77..a62033a35 100644 --- a/AgileMapper/Api/Configuration/IPreInstanceCreationCallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/IPreInstanceCreationCallbackSpecifier.cs @@ -21,7 +21,7 @@ public interface IPreInstanceCreationCallbackSpecifier /// target type being configured. /// /// - MappingConfigContinuation Call(Action> callback); + IMappingConfigContinuation Call(Action> callback); /// /// Configure a callback to call in the configured conditions. The callback is passed the current @@ -32,7 +32,7 @@ public interface IPreInstanceCreationCallbackSpecifier /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action callback); + IMappingConfigContinuation Call(Action callback); /// /// Configure a callback to call in the configured conditions. The callback is passed the current @@ -43,6 +43,6 @@ public interface IPreInstanceCreationCallbackSpecifier /// A MappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - MappingConfigContinuation Call(Action callback); + IMappingConfigContinuation Call(Action callback); } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs b/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs index c08c1dbd9..c2d614582 100644 --- a/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs +++ b/AgileMapper/Api/Configuration/InstanceCreationCallbackSpecifier.cs @@ -45,19 +45,19 @@ private InstanceCreationCallbackSpecifier SetConditio return this; } - MappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( Action> callback) { return CreateCallbackFactory(callback); } - MappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( Action callback) { return CreateCallbackFactory(callback); } - MappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( + IMappingConfigContinuation IPreInstanceCreationCallbackSpecifier.Call( Action callback) { return CreateCallbackFactory(callback); From 20fb65da4529966574bc79fed009d826251306d6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 08:44:23 +0000 Subject: [PATCH 150/176] Moving callback tests to EFCore-only / Test coverage for object tracking + projection --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 - .../Configuration/WhenConfiguringCallbacks.cs | 13 - .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 - .../Configuration/WhenConfiguringCallbacks.cs | 13 - .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 - .../Configuration/WhenConfiguringCallbacks.cs | 13 - .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Configuration/WhenConfiguringCallbacks.cs | 228 ++++++++++++++++- .../WhenConfiguringObjectTracking.cs | 51 ++++ .../AgileMapper.UnitTests.Orms.csproj | 1 - .../Configuration/WhenConfiguringCallbacks.cs | 237 ------------------ 11 files changed, 278 insertions(+), 282 deletions(-) delete mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs delete mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs delete mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 5187f02ab..53f732c3e 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -85,7 +85,6 @@ Properties\VersionInfo.cs - diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs deleted file mode 100644 index 4f9454e43..000000000 --- a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringCallbacks.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration -{ - using Infrastructure; - using Orms.Configuration; - - public class WhenConfiguringCallbacks : WhenConfiguringCallbacks - { - public WhenConfiguringCallbacks(InMemoryEf5TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index d2811b449..3d0b42356 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,7 +87,6 @@ Properties\VersionInfo.cs - diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs deleted file mode 100644 index b6bcea5b6..000000000 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringCallbacks.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration -{ - using Infrastructure; - using Orms.Configuration; - - public class WhenConfiguringCallbacks : WhenConfiguringCallbacks - { - public WhenConfiguringCallbacks(InMemoryEf6TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 9ebc076b2..64700230e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -213,7 +213,6 @@ Properties\VersionInfo.cs - diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs deleted file mode 100644 index fecc75246..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringCallbacks.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration -{ - using Infrastructure; - using Orms.Configuration; - - public class WhenConfiguringCallbacks : WhenConfiguringCallbacks - { - public WhenConfiguringCallbacks(InMemoryEfCore1TestContext context) - : base(context) - { - } - } -} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index ffd3d3b90..3341f6165 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -154,6 +154,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs index 6d7cd82da..ca09ed3be 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs @@ -1,13 +1,237 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; - using Orms.Configuration; + using Orms.Infrastructure; + using TestClasses; + using Xunit; - public class WhenConfiguringCallbacks : WhenConfiguringCallbacks + public class WhenConfiguringCallbacks : OrmTestClassBase { public WhenConfiguringCallbacks(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldNotAttemptToCallAPreMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .MappingBegins + .Call(ctx => callbackCalled = true); + + var square = new Square { SideLength = 12 }; + + await context.Shapes.AddAsync(square); + await context.SaveChangesAsync(); + + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(12); + + mapper.Map(square).OnTo(squareVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .From() + .To() + .After + .MappingEnds + .Call((s, c) => callbackCalled = true); + + var circle = new Circle { Diameter = 1 }; + + await context.Shapes.AddAsync(circle); + await context.SaveChangesAsync(); + + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(1); + + mapper.Map(circle).Over(circleVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPreObjectCreationCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .CreatingInstancesOf() + .Call(ctx => callbackCalled = true); + + var person = new Person { Name = "Bjorn", Address = new Address { Line1 = "Sweden" } }; + + await context.Persons.AddAsync(person); + await context.SaveChangesAsync(); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Bjorn"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); + + mapper.Map(person).Over(personDto); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostObjectCreationCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .After + .CreatingInstances + .Call(ctx => callbackCalled = true); + + var person = new Person { Name = "Benny", Address = new Address { Line1 = "Sweden" } }; + + await context.Persons.AddAsync(person); + await context.SaveChangesAsync(); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Benny"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); + + mapper.Map(person).ToANew(); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPreMemberMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .Before + .Mapping(s => s.SideLength) + .Call(ctx => callbackCalled = true); + + var square = new Square { SideLength = 1 }; + + await context.Shapes.AddAsync(square); + await context.SaveChangesAsync(); + + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(1); + + mapper.Map(square).Over(squareVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } + + [Fact] + public Task ShouldNotAttemptToCallAPostMemberMappingCallback() + { + return RunTest(async context => + { + using (var mapper = Mapper.CreateNew()) + { + var callbackCalled = false; + + mapper.WhenMapping + .To() + .After + .Mapping(c => c.Diameter) + .Call(ctx => callbackCalled = true); + + var circle = new Circle { Diameter = 11 }; + + await context.Shapes.AddAsync(circle); + await context.SaveChangesAsync(); + + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(11); + + mapper.Map(circle).Over(circleVm); + + callbackCalled.ShouldBeTrue(); + } + }); + } } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs new file mode 100644 index 000000000..8c93d735d --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs @@ -0,0 +1,51 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenConfiguringObjectTracking : OrmTestClassBase + { + public WhenConfiguringObjectTracking(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreObjectTracking() + { + return RunTest(async context => + { + var circle1 = new Circle { Diameter = 1 }; + var circle2 = new Circle { Diameter = 2 }; + var circle3 = new Circle { Diameter = 3 }; + + await context.Shapes.AddRangeAsync(circle1, circle2, circle3); + await context.SaveChangesAsync(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .To() + .MaintainIdentityIntegrity(); + + var circleVms = await context + .Shapes + .ProjectUsing(mapper) + .To() + .ToArrayAsync(); + + circleVms.Length.ShouldBe(3); + + circleVms.First().Diameter.ShouldBe(1); + circleVms.Second().Diameter.ShouldBe(2); + circleVms.Third().Diameter.ShouldBe(3); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index bb90927a1..38b39a1fb 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,7 +73,6 @@ Properties\VersionInfo.cs - diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs deleted file mode 100644 index d5b9c92ee..000000000 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringCallbacks.cs +++ /dev/null @@ -1,237 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration -{ - using System.Threading.Tasks; - using Infrastructure; - using TestClasses; - using Xunit; - - public abstract class WhenConfiguringCallbacks : OrmTestClassBase - where TOrmContext : ITestDbContext, new() - { - protected WhenConfiguringCallbacks(ITestContext context) - : base(context) - { - } - - [Fact] - public Task ShouldNotAttemptToCallAPreMappingCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .To() - .Before - .MappingBegins - .Call(ctx => callbackCalled = true); - - var square = new Square { SideLength = 12 }; - - await context.Shapes.Add(square); - await context.SaveChanges(); - - var squareVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - squareVm.SideLength.ShouldBe(12); - - mapper.Map(square).OnTo(squareVm); - - callbackCalled.ShouldBeTrue(); - } - }); - } - - [Fact] - public Task ShouldNotAttemptToCallAPostMappingCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .From() - .To() - .After - .MappingEnds - .Call((s, c) => callbackCalled = true); - - var circle = new Circle { Diameter = 1 }; - - await context.Shapes.Add(circle); - await context.SaveChanges(); - - var circleVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - circleVm.Diameter.ShouldBe(1); - - mapper.Map(circle).Over(circleVm); - - callbackCalled.ShouldBeTrue(); - } - }); - } - - [Fact] - public Task ShouldNotAttemptToCallAPreObjectCreationCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .To() - .Before - .CreatingInstancesOf() - .Call(ctx => callbackCalled = true); - - var person = new Person { Name = "Bjorn", Address = new Address { Line1 = "Sweden" } }; - - await context.Persons.Add(person); - await context.SaveChanges(); - - var personDto = context - .Persons - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - personDto.Name.ShouldBe("Bjorn"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("Sweden"); - - mapper.Map(person).Over(personDto); - - callbackCalled.ShouldBeTrue(); - } - }); - } - - [Fact] - public Task ShouldNotAttemptToCallAPostObjectCreationCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .To() - .After - .CreatingInstances - .Call(ctx => callbackCalled = true); - - var person = new Person { Name = "Benny", Address = new Address { Line1 = "Sweden" } }; - - await context.Persons.Add(person); - await context.SaveChanges(); - - var personDto = context - .Persons - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - personDto.Name.ShouldBe("Benny"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("Sweden"); - - mapper.Map(person).ToANew(); - - callbackCalled.ShouldBeTrue(); - } - }); - } - - [Fact] - public Task ShouldNotAttemptToCallAPreMemberMappingCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .To() - .Before - .Mapping(s => s.SideLength) - .Call(ctx => callbackCalled = true); - - var square = new Square { SideLength = 1 }; - - await context.Shapes.Add(square); - await context.SaveChanges(); - - var squareVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - squareVm.SideLength.ShouldBe(1); - - mapper.Map(square).Over(squareVm); - - callbackCalled.ShouldBeTrue(); - } - }); - } - - [Fact] - public Task ShouldNotAttemptToCallAPostMemberMappingCallback() - { - return RunTest(async context => - { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; - - mapper.WhenMapping - .To() - .After - .Mapping(c => c.Diameter) - .Call(ctx => callbackCalled = true); - - var circle = new Circle { Diameter = 11 }; - - await context.Shapes.Add(circle); - await context.SaveChanges(); - - var circleVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - callbackCalled.ShouldBeFalse(); - circleVm.Diameter.ShouldBe(11); - - mapper.Map(circle).Over(circleVm); - - callbackCalled.ShouldBeTrue(); - } - }); - } - } -} From 520a2334786d58a48af637e44450dce32768d978 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 09:17:32 +0000 Subject: [PATCH 151/176] Start of support for custom object factories in projections --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringObjectCreation.cs | 13 ++++++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../WhenConfiguringObjectCreation.cs | 41 +++++++++++++++++++ .../TestClasses/PublicStringCtorDto.cs | 18 ++++++++ .../Api/Configuration/FactorySpecifier.cs | 6 ++- .../Api/Configuration/IFactorySpecifier.cs | 2 +- .../Configuration/IRootMappingConfigurator.cs | 6 ++- .../MappingConfigStartingPoint.cs | 36 ++++++++++------ .../Api/Configuration/MappingConfigurator.cs | 30 ++++++++++++-- .../Projection/IRootProjectionConfigurator.cs | 15 +++++++ .../Configuration/ConfiguredLambdaInfo.cs | 2 +- .../Configuration/ParametersSwapper.cs | 13 +++--- 13 files changed, 158 insertions(+), 27 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicStringCtorDto.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 3341f6165..dfd6f4539 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -154,6 +154,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs new file mode 100644 index 000000000..a3e67c468 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringObjectCreation : WhenConfiguringObjectCreation + { + public WhenConfiguringObjectCreation(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 38b39a1fb..6db437fdc 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -76,6 +76,7 @@ + @@ -127,6 +128,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs new file mode 100644 index 000000000..a57d6a542 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -0,0 +1,41 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenConfiguringObjectCreation : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringObjectCreation(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldUseACustomObjectFactory() + { + return RunTest(async context => + { + await context.StringItems.Add(new PublicString { Value = "Ctor!" }); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .ProjectionsTo() + .CreateInstancesUsing(o => new PublicStringCtorDto("PANTS")); + + var ctorDto = context + .StringItems + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + ctorDto.Value.ShouldBe("PANTS"); + } + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringCtorDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringCtorDto.cs new file mode 100644 index 000000000..ecbae435b --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringCtorDto.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicStringCtorDto + { + public PublicStringCtorDto(string value) + { + Value = value; + } + + [Key] + public int Id { get; set; } + + + public string Value { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/FactorySpecifier.cs b/AgileMapper/Api/Configuration/FactorySpecifier.cs index 9a40b4e67..e3ce32699 100644 --- a/AgileMapper/Api/Configuration/FactorySpecifier.cs +++ b/AgileMapper/Api/Configuration/FactorySpecifier.cs @@ -18,13 +18,17 @@ public FactorySpecifier(MappingConfigInfo configInfo) } public void Using(Expression, TObject>> factory) + => Using((LambdaExpression)factory); + + public void Using(LambdaExpression factory) { var objectFactory = ConfiguredObjectFactory.For(_configInfo, typeof(TObject), factory); _configInfo.MapperContext.UserConfigurations.Add(objectFactory); } - public void Using(TFactory factory) where TFactory : class + public void Using(TFactory factory) + where TFactory : class { var factoryInfo = ConfiguredLambdaInfo.ForFunc(factory, typeof(TSource), typeof(TTarget)); diff --git a/AgileMapper/Api/Configuration/IFactorySpecifier.cs b/AgileMapper/Api/Configuration/IFactorySpecifier.cs index 30e022bfb..e30aa5791 100644 --- a/AgileMapper/Api/Configuration/IFactorySpecifier.cs +++ b/AgileMapper/Api/Configuration/IFactorySpecifier.cs @@ -6,7 +6,7 @@ namespace AgileObjects.AgileMapper.Api.Configuration /// /// Provides options for configuring custom factory objects with which to create instances of the type - /// specified by the type argument when mapping from and to the given + /// specified by the type argument, when mapping from and to the given /// source and target types. /// /// The source type to which the configuration should apply. diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index fa321fbd1..3a2c6c20a 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -52,7 +52,8 @@ IMappingConfigContinuation CreateInstancesUsing( /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - IMappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class; + IMappingConfigContinuation CreateInstancesUsing(TFactory factory) + where TFactory : class; /// /// Configure a factory to use to create instances of the type specified by the type argument. @@ -61,7 +62,8 @@ IMappingConfigContinuation CreateInstancesUsing( /// /// An IFactorySpecifier with which to configure the factory for the type specified by the type argument. /// - IFactorySpecifier CreateInstancesOf() where TObject : class; + IFactorySpecifier CreateInstancesOf() + where TObject : class; /// /// Ignore the given when mappingfrom and to the source and target types diff --git a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs index f11ff72ae..0c3c97ae9 100644 --- a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs +++ b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs @@ -11,6 +11,7 @@ using Dynamics; using Extensions.Internal; using Members; + using Projection; using static Constants; using static AgileMapper.Configuration.Dictionaries.DictionaryType; @@ -434,26 +435,26 @@ public TargetSpecifier From() => GetTargetTypeSpecifier(ci => ci.ForSourceType()); /// - /// Configure how this mapper performs mappings from all source types and MappingRuleSets (create new, overwrite, - /// etc), to the target type specified by the type argument. + /// Configure how this mapper performs mappings from all source types and MappingRuleSets (create new, + /// overwrite, etc), to the Type. /// - /// The target type to which the configuration will apply. + /// The target Type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. public IFullMappingConfigurator To() => GetAllSourcesTargetTypeSpecifier(ci => ci.ForAllRuleSets()).To(); /// - /// Configure how this mapper performs object creation mappings from any source type to the target type - /// specified by the type argument. + /// Configure how this mapper performs object creation mappings from any source type to the + /// Type. /// - /// The target type to which the configuration will apply. + /// The result Type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. - public IFullMappingConfigurator ToANew() - => GetAllSourcesTargetTypeSpecifier(ci => ci.ForRuleSet(CreateNew)).ToANew(); + public IFullMappingConfigurator ToANew() + => GetAllSourcesTargetTypeSpecifier(ci => ci.ForRuleSet(CreateNew)).ToANew(); /// - /// Configure how this mapper performs OnTo (merge) mappings from any source type to the target type - /// specified by the type argument. + /// Configure how this mapper performs OnTo (merge) mappings from any source type to the + /// Type. /// /// The target type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. @@ -461,14 +462,23 @@ public IFullMappingConfigurator OnTo() => GetAllSourcesTargetTypeSpecifier(ci => ci.ForRuleSet(Merge)).OnTo(); /// - /// Configure how this mapper performs Over (overwrite) mappings from any source type to the target type - /// specified by the type argument. + /// Configure how this mapper performs Over (overwrite) mappings from any source type to the + /// Type. /// - /// The target type to which the configuration will apply. + /// The target Type to which the configuration will apply. /// An IFullMappingConfigurator with which to complete the configuration. public IFullMappingConfigurator Over() => GetAllSourcesTargetTypeSpecifier(ci => ci.ForRuleSet(Overwrite)).Over(); + /// + /// Configure how this mapper performs query projection mappings from any source type to the + /// Type. + /// + /// The result Type to which the configuration will apply. + /// An IFullProjectionConfigurator with which to complete the configuration. + public IFullProjectionConfigurator ProjectionsTo() + => GetAllSourcesTargetTypeSpecifier(ci => ci.ForRuleSet(Project)).ProjectedTo(); + private TargetSpecifier GetAllSourcesTargetTypeSpecifier( Func configInfoConfigurator) { diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 5b81698f1..daecf9fcd 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -119,24 +119,46 @@ private MappingConfigurator SetCondition(LambdaExpression cond #endregion + #region Instance Creation + public IMappingConfigContinuation CreateInstancesUsing( Expression, TTarget>> factory) { - new FactorySpecifier(ConfigInfo).Using(factory); + return RegisterFactory(factory); + } + + public IProjectionConfigContinuation CreateInstancesUsing( + Expression> factory) + { + return RegisterFactory(factory); + } + + private MappingConfigContinuation RegisterFactory(LambdaExpression factory) + { + CreateFactorySpecifier().Using(factory); return new MappingConfigContinuation(ConfigInfo); } - public IMappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class + public IMappingConfigContinuation CreateInstancesUsing(TFactory factory) + where TFactory : class { - new FactorySpecifier(ConfigInfo).Using(factory); + CreateFactorySpecifier().Using(factory); return new MappingConfigContinuation(ConfigInfo); } - public IFactorySpecifier CreateInstancesOf() where TObject : class + public IFactorySpecifier CreateInstancesOf() + where TObject : class + { + return CreateFactorySpecifier(); + } + + private FactorySpecifier CreateFactorySpecifier() => new FactorySpecifier(ConfigInfo); + #endregion + public IFullMappingSettings SwallowAllExceptions() => PassExceptionsTo(ctx => { }); public IFullMappingSettings PassExceptionsTo(Action> callback) diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index cc47b6549..f11911c65 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -11,6 +11,21 @@ /// The result type to which the configuration should apply. public interface IRootProjectionConfigurator { + /// + /// Use the given expression to create instances of the result type being + /// configured. The factory expression is passed the source element being projected, and must be + /// translatable by the QueryProvider being used. + /// + /// + /// The factory expression to use to create instances of the Type being configured. + /// + /// + /// An IProjectionConfigContinuation to enable further configuration of projections from and to the + /// source and result Type being configured. + /// + IProjectionConfigContinuation CreateInstancesUsing( + Expression> factory); + /// /// Ignore the specified when projecting from and to the source and /// result types being configured. diff --git a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs index 34bc5d601..a174fcfdb 100644 --- a/AgileMapper/Configuration/ConfiguredLambdaInfo.cs +++ b/AgileMapper/Configuration/ConfiguredLambdaInfo.cs @@ -122,7 +122,7 @@ private static ConfiguredLambdaInfo For( #endregion - public bool UsesMappingDataObjectParameter => _parametersSwapper.NumberOfParameters == 1; + public bool UsesMappingDataObjectParameter => _parametersSwapper.HasMappingContextParameter; public Type ReturnType { get; } diff --git a/AgileMapper/Configuration/ParametersSwapper.cs b/AgileMapper/Configuration/ParametersSwapper.cs index 14f8a6342..c6a734cab 100644 --- a/AgileMapper/Configuration/ParametersSwapper.cs +++ b/AgileMapper/Configuration/ParametersSwapper.cs @@ -17,7 +17,7 @@ internal class ParametersSwapper private static readonly ParametersSwapper[] _implementations = { new ParametersSwapper(0, (ct, ft) => true, SwapNothing), - new ParametersSwapper(1, IsContext, SwapForContextParameter), + new ParametersSwapper(1, IsContext, SwapForContextParameter, true), new ParametersSwapper(1, IsSourceOnly, SwapForSource), new ParametersSwapper(2, IsSourceAndTarget, SwapForSourceAndTarget), new ParametersSwapper(3, IsSourceTargetAndIndex, SwapForSourceTargetAndIndex), @@ -179,26 +179,29 @@ private static MappingContextInfo GetAppropriateMappingContext(SwapArgs swapArgs #endregion + private readonly int _numberOfParameters; private readonly Func _applicabilityPredicate; private readonly Func _parametersSwapper; private ParametersSwapper( int numberOfParameters, Func applicabilityPredicate, - Func parametersSwapper) + Func parametersSwapper, + bool hasMappingContextParameter = false) { - NumberOfParameters = numberOfParameters; + _numberOfParameters = numberOfParameters; _applicabilityPredicate = applicabilityPredicate; _parametersSwapper = parametersSwapper; + HasMappingContextParameter = hasMappingContextParameter; } public static ParametersSwapper For(Type[] contextTypes, Type[] funcArguments) => _implementations.FirstOrDefault(pso => pso.AppliesTo(contextTypes, funcArguments)); - public int NumberOfParameters { get; } + public bool HasMappingContextParameter { get; } public bool AppliesTo(Type[] contextTypes, Type[] funcArguments) - => (funcArguments.Length == NumberOfParameters) && _applicabilityPredicate.Invoke(contextTypes, funcArguments); + => (funcArguments.Length == _numberOfParameters) && _applicabilityPredicate.Invoke(contextTypes, funcArguments); public static Expression UseTargetMember(IMemberMapperData mapperData, Expression contextAccess, Type targetType) => mapperData.GetTargetAccess(contextAccess, targetType); From 47d40ba15ce55cc5cdc2d0a435be67d44b3a263a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 10:29:43 +0000 Subject: [PATCH 152/176] Support for custom factories for projections of objects of a specified type --- .../WhenConfiguringObjectCreation.cs | 40 +++++++ .../Api/Configuration/FactorySpecifier.cs | 72 +++++++----- ...ecifier.cs => IMappingFactorySpecifier.cs} | 106 ++++++++++-------- .../Configuration/IRootMappingConfigurator.cs | 12 +- .../Api/Configuration/MappingConfigurator.cs | 10 +- .../Projection/IProjectionFactorySpecifier.cs | 30 +++++ .../Projection/IRootProjectionConfigurator.cs | 13 ++- 7 files changed, 192 insertions(+), 91 deletions(-) rename AgileMapper/Api/Configuration/{IFactorySpecifier.cs => IMappingFactorySpecifier.cs} (69%) create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionFactorySpecifier.cs diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs index a57d6a542..7da9631fb 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -37,5 +37,45 @@ public Task ShouldUseACustomObjectFactory() } }); } + + [Fact] + public Task ShouldUseACustomObjectFactoryForASpecifiedType() + { + return RunTest(async context => + { + var person = new Person + { + Name = "Fatima", + Address = new Address { Line1 = "1", Line2 = "2" } + }; + + await context.Persons.Add(person); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .CreateInstancesOf().Using(p => new AddressDto + { + Line1 = p.Address.Line1 + "!", + Line2 = p.Address.Line2 + "?", + Postcode = "BA7 8RD" + }); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("1!"); + personDto.Address.Line2.ShouldBe("2?"); + personDto.Address.Postcode.ShouldBe("BA7 8RD"); + } + }); + } } } diff --git a/AgileMapper/Api/Configuration/FactorySpecifier.cs b/AgileMapper/Api/Configuration/FactorySpecifier.cs index e3ce32699..5a2c3c2b2 100644 --- a/AgileMapper/Api/Configuration/FactorySpecifier.cs +++ b/AgileMapper/Api/Configuration/FactorySpecifier.cs @@ -6,9 +6,12 @@ namespace AgileObjects.AgileMapper.Api.Configuration using AgileMapper.Configuration; using Members; using ObjectPopulation; + using Projection; using ReadableExpressions.Extensions; - internal class FactorySpecifier : IFactorySpecifier + internal class FactorySpecifier : + IMappingFactorySpecifier, + IProjectionFactorySpecifier { private readonly MappingConfigInfo _configInfo; @@ -17,47 +20,56 @@ public FactorySpecifier(MappingConfigInfo configInfo) _configInfo = configInfo; } - public void Using(Expression, TObject>> factory) - => Using((LambdaExpression)factory); + public IMappingConfigContinuation Using( + Expression, TObject>> factory) + => RegisterObjectFactory(factory, ConfiguredObjectFactory.For); - public void Using(LambdaExpression factory) - { - var objectFactory = ConfiguredObjectFactory.For(_configInfo, typeof(TObject), factory); + public IProjectionConfigContinuation Using(Expression> factory) + => RegisterObjectFactory(factory, ConfiguredObjectFactory.For); - _configInfo.MapperContext.UserConfigurations.Add(objectFactory); - } + public IMappingConfigContinuation Using(LambdaExpression factory) + => RegisterObjectFactory(factory, ConfiguredObjectFactory.For); - public void Using(TFactory factory) + public IMappingConfigContinuation Using(TFactory factory) where TFactory : class { var factoryInfo = ConfiguredLambdaInfo.ForFunc(factory, typeof(TSource), typeof(TTarget)); - if (factoryInfo == null) + if (factoryInfo != null) { - var contextTypeName = typeof(IMappingData).GetFriendlyName(); - var sourceTypeName = typeof(TSource).GetFriendlyName(); - var targetTypeName = typeof(TTarget).GetFriendlyName(); - var objectTypeName = typeof(TObject).GetFriendlyName(); - - string[] validSignatures = - { - $"Func<{objectTypeName}>", - $"Func<{contextTypeName}, {objectTypeName}>", - $"Func<{sourceTypeName}, {targetTypeName}, {objectTypeName}>", - $"Func<{sourceTypeName}, {targetTypeName}, int?, {objectTypeName}>" - }; - - throw new MappingConfigurationException(string.Format( - CultureInfo.InvariantCulture, - "Unable to create objects of type {0} using factory {1}: valid function signatures are {2}", - objectTypeName, - typeof(TFactory).GetFriendlyName(), - string.Join(", ", validSignatures))); + return RegisterObjectFactory(factoryInfo, ConfiguredObjectFactory.For); } - var objectFactory = ConfiguredObjectFactory.For(_configInfo, typeof(TObject), factoryInfo); + var contextTypeName = typeof(IMappingData).GetFriendlyName(); + var sourceTypeName = typeof(TSource).GetFriendlyName(); + var targetTypeName = typeof(TTarget).GetFriendlyName(); + var objectTypeName = typeof(TObject).GetFriendlyName(); + + string[] validSignatures = + { + $"Func<{objectTypeName}>", + $"Func<{contextTypeName}, {objectTypeName}>", + $"Func<{sourceTypeName}, {targetTypeName}, {objectTypeName}>", + $"Func<{sourceTypeName}, {targetTypeName}, int?, {objectTypeName}>" + }; + + throw new MappingConfigurationException(string.Format( + CultureInfo.InvariantCulture, + "Unable to create objects of type {0} using factory {1}: valid function signatures are {2}", + objectTypeName, + typeof(TFactory).GetFriendlyName(), + string.Join(", ", validSignatures))); + } + + private MappingConfigContinuation RegisterObjectFactory( + TFactory factory, + Func objectFactoryFactory) + { + var objectFactory = objectFactoryFactory.Invoke(_configInfo, typeof(TObject), factory); _configInfo.MapperContext.UserConfigurations.Add(objectFactory); + + return new MappingConfigContinuation(_configInfo); } } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IFactorySpecifier.cs b/AgileMapper/Api/Configuration/IMappingFactorySpecifier.cs similarity index 69% rename from AgileMapper/Api/Configuration/IFactorySpecifier.cs rename to AgileMapper/Api/Configuration/IMappingFactorySpecifier.cs index e30aa5791..7aec38b7f 100644 --- a/AgileMapper/Api/Configuration/IFactorySpecifier.cs +++ b/AgileMapper/Api/Configuration/IMappingFactorySpecifier.cs @@ -1,50 +1,58 @@ -namespace AgileObjects.AgileMapper.Api.Configuration -{ - using System; - using System.Linq.Expressions; - using Members; - - /// - /// Provides options for configuring custom factory objects with which to create instances of the type - /// specified by the type argument, when mapping from and to the given - /// source and target types. - /// - /// The source type to which the configuration should apply. - /// The target type to which the configuration should apply. - /// The type of object which will be created by the configured factories. - public interface IFactorySpecifier - { - /// - /// Use the given expression to create instances of the object type being - /// configured. The factory expression is passed a context object containing the current mapping's source - /// and target objects. - /// - /// - /// The factory expression to use to create instances of the type being configured. - /// - void Using(Expression, TObject>> factory); - - /// - /// Use the given function to create instances of the object type being - /// configured. The following factory function signatures are supported: - /// - /// Func<TObject> - parameterless. - /// - /// - /// Func<IMappingData<TSource, TTarget>, TObject> - taking a context object containing the - /// current mapping's source and target objects. - /// - /// - /// Func<TSource, TTarget, TObject> - taking the source and target objects. - /// - /// - /// Func<TSource, TTarget, int?, TObject> - taking the source and target objects and the current - /// enumerable index, if applicable. - /// - /// - /// - /// The factory function to use to create instances of the type being configured. - /// - void Using(TFactory factory) where TFactory : class; - } +namespace AgileObjects.AgileMapper.Api.Configuration +{ + using System; + using System.Linq.Expressions; + using Members; + + /// + /// Provides options for configuring custom factory objects with which to create instances of the + /// Type, when mapping from and to the given source and target types. + /// + /// The source type to which the configuration should apply. + /// The target type to which the configuration should apply. + /// The type of object which will be created by the configured factories. + public interface IMappingFactorySpecifier + { + /// + /// Use the given expression to create instances of the object type being + /// configured. The factory expression is passed a context object containing the current mapping's source + /// and target objects. + /// + /// + /// The factory expression to use to create instances of the type being configured. + /// + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source + /// and target type being configured. + /// + IMappingConfigContinuation Using(Expression, TObject>> factory); + + /// + /// Use the given function to create instances of the object type being + /// configured. The following factory function signatures are supported: + /// + /// Func<TObject> - parameterless. + /// + /// + /// Func<IMappingData<TSource, TTarget>, TObject> - taking a context object containing the + /// current mapping's source and target objects. + /// + /// + /// Func<TSource, TTarget, TObject> - taking the source and target objects. + /// + /// + /// Func<TSource, TTarget, int?, TObject> - taking the source and target objects and the current + /// enumerable index, if applicable. + /// + /// + /// + /// The factory function to use to create instances of the type being configured. + /// + /// + /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source + /// and target type being configured. + /// + IMappingConfigContinuation Using(TFactory factory) + where TFactory : class; + } } \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs index 3a2c6c20a..fbed57229 100644 --- a/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/IRootMappingConfigurator.cs @@ -52,18 +52,18 @@ IMappingConfigContinuation CreateInstancesUsing( /// An IMappingConfigContinuation to enable further configuration of mappings from and to the source and /// target type being configured. /// - IMappingConfigContinuation CreateInstancesUsing(TFactory factory) + IMappingConfigContinuation CreateInstancesUsing(TFactory factory) where TFactory : class; /// - /// Configure a factory to use to create instances of the type specified by the type argument. + /// Configure a factory to use to create instances of the Type. /// - /// The type of object the creation of which is to be configured. + /// The Type of object the creation of which is to be configured. /// - /// An IFactorySpecifier with which to configure the factory for the type specified by the type argument. + /// An IMappingFactorySpecifier with which to configure the factory for the + /// Type. /// - IFactorySpecifier CreateInstancesOf() - where TObject : class; + IMappingFactorySpecifier CreateInstancesOf(); /// /// Ignore the given when mappingfrom and to the source and target types diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index daecf9fcd..ee29fe2c1 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -148,11 +148,11 @@ public IMappingConfigContinuation CreateInstancesUsing(ConfigInfo); } - public IFactorySpecifier CreateInstancesOf() - where TObject : class - { - return CreateFactorySpecifier(); - } + public IMappingFactorySpecifier CreateInstancesOf() + => CreateFactorySpecifier(); + + IProjectionFactorySpecifier IRootProjectionConfigurator.CreateInstancesOf() + => CreateFactorySpecifier(); private FactorySpecifier CreateFactorySpecifier() => new FactorySpecifier(ConfigInfo); diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionFactorySpecifier.cs b/AgileMapper/Api/Configuration/Projection/IProjectionFactorySpecifier.cs new file mode 100644 index 000000000..ae62a0335 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionFactorySpecifier.cs @@ -0,0 +1,30 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + using System; + using System.Linq.Expressions; + + /// + /// Provides an option for configuring a custom factory Expression with which to create instances of the + /// Type, when projecting from and to the source and result types being + /// configured. + /// + /// The source Type to which the configuration should apply. + /// The result Type to which the configuration should apply. + /// The Type of object which will be created by the configured factory. + public interface IProjectionFactorySpecifier + { + /// + /// Use the given expression to create instances of the object type being + /// configured. The factory expression is passed the source element being projected, and must be + /// translatable by the QueryProvider being used. + /// + /// + /// The factory expression to use to create instances of the type being configured. + /// + /// + /// An IProjectionConfigContinuation to enable further configuration of projections from and to the + /// source and result Type being configured. + /// + IProjectionConfigContinuation Using(Expression> factory); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs index f11911c65..b143f6033 100644 --- a/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IRootProjectionConfigurator.cs @@ -26,6 +26,16 @@ public interface IRootProjectionConfigurator IProjectionConfigContinuation CreateInstancesUsing( Expression> factory); + /// + /// Configure a factory to use to create instances of the Type. + /// + /// The Type of object the creation of which is to be configured. + /// + /// An IProjectionFactorySpecifier with which to configure the factory for the + /// Type. + /// + IProjectionFactorySpecifier CreateInstancesOf(); + /// /// Ignore the specified when projecting from and to the source and /// result types being configured. @@ -84,6 +94,7 @@ ICustomProjectionDataSourceTargetMemberSpecifier /// A CustomDataSourceTargetMemberSpecifier with which to specify the result member to which the custom /// constant value should be applied. /// - ICustomProjectionDataSourceTargetMemberSpecifier Map(TSourceValue value); + ICustomProjectionDataSourceTargetMemberSpecifier Map( + TSourceValue value); } } \ No newline at end of file From da417c15a106046b31c8aa47f2e8b5bd7e38c91f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 10:41:41 +0000 Subject: [PATCH 153/176] Test coverage for conditional object factories in projections --- .../WhenConfiguringObjectCreation.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs index 7da9631fb..efbc1105a 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration { + using System.Linq; using System.Threading.Tasks; using Infrastructure; using TestClasses; @@ -77,5 +78,36 @@ public Task ShouldUseACustomObjectFactoryForASpecifiedType() } }); } + + [Fact] + public Task ShouldUseAConditionalObjectFactory() + { + return RunTest(async context => + { + await context.IntItems.Add(new PublicInt { Value = 1 }); + await context.IntItems.Add(new PublicInt { Value = 2 }); + await context.IntItems.Add(new PublicInt { Value = 3 }); + await context.SaveChanges(); + + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Value % 2 == 0) + .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString())); + + var stringDtos = context + .IntItems + .ProjectUsing(mapper) + .To() + .ToArray(); + + stringDtos.First().Value.ShouldBe("1"); + stringDtos.Second().Value.ShouldBe("4"); + stringDtos.Third().Value.ShouldBe("3"); + } + }); + } } } From f5c76a6d0dee6c4176f2ffe7d65873a6b8ee10d2 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 10:56:34 +0000 Subject: [PATCH 154/176] Expanding test coverage for custom projection object factories --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringObjectCreation.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringObjectCreation.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringObjectCreation.cs | 18 ++++++ .../WhenConfiguringObjectCreation.cs | 5 ++ .../WhenConfiguringObjectTracking.cs | 1 + .../WhenConfiguringObjectCreation.cs | 63 ++++++++++--------- 9 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringObjectCreation.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringObjectCreation.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringObjectCreation.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 53f732c3e..0f6964365 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -89,6 +89,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringObjectCreation.cs new file mode 100644 index 000000000..19992aaa6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenConfiguringObjectCreation.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringObjectCreation : WhenConfiguringObjectCreation + { + public WhenConfiguringObjectCreation(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorUsingAConditionalObjectFactory() => RunShouldErrorUsingAConditionalObjectFactory(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 3d0b42356..f3d09d91d 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -91,6 +91,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringObjectCreation.cs new file mode 100644 index 000000000..2584c5b0a --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringObjectCreation.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringObjectCreation : WhenConfiguringObjectCreation + { + public WhenConfiguringObjectCreation(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorUsingAConditionalObjectFactory() => RunShouldErrorUsingAConditionalObjectFactory(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 64700230e..c5df830ab 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -217,6 +217,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringObjectCreation.cs new file mode 100644 index 000000000..dac02809c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringObjectCreation.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringObjectCreation : WhenConfiguringObjectCreation + { + public WhenConfiguringObjectCreation(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldUseAConditionalObjectFactory() => RunShouldUseAConditionalObjectFactory(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs index a3e67c468..f37d166c1 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectCreation.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; + using Xunit; public class WhenConfiguringObjectCreation : WhenConfiguringObjectCreation { @@ -9,5 +11,8 @@ public WhenConfiguringObjectCreation(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldUseAConditionalObjectFactory() => RunShouldUseAConditionalObjectFactory(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs index 8c93d735d..50408452f 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs @@ -37,6 +37,7 @@ public Task ShouldIgnoreObjectTracking() .Shapes .ProjectUsing(mapper) .To() + .OrderBy(c => c.Diameter) .ToArrayAsync(); circleVms.Length.ShouldBe(3); diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs index efbc1105a..b851b8acc 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -25,13 +25,13 @@ public Task ShouldUseACustomObjectFactory() using (var mapper = Mapper.CreateNew()) { mapper.WhenMapping - .ProjectionsTo() - .CreateInstancesUsing(o => new PublicStringCtorDto("PANTS")); + .ProjectionsTo() + .CreateInstancesUsing(o => new PublicStringDto { Value = "PANTS" }); var ctorDto = context .StringItems .ProjectUsing(mapper) - .To() + .To() .ShouldHaveSingleItem(); ctorDto.Value.ShouldBe("PANTS"); @@ -79,35 +79,42 @@ public Task ShouldUseACustomObjectFactoryForASpecifiedType() }); } - [Fact] - public Task ShouldUseAConditionalObjectFactory() + #region Project -> Conditional Factory + + protected Task RunShouldUseAConditionalObjectFactory() + => RunTest(DoShouldUseAConditionalObjectFactory); + + protected Task RunShouldErrorUsingAConditionalObjectFactory() + => RunTestAndExpectThrow(DoShouldUseAConditionalObjectFactory); + + private static async Task DoShouldUseAConditionalObjectFactory(TOrmContext context) { - return RunTest(async context => - { - await context.IntItems.Add(new PublicInt { Value = 1 }); - await context.IntItems.Add(new PublicInt { Value = 2 }); - await context.IntItems.Add(new PublicInt { Value = 3 }); - await context.SaveChanges(); + await context.IntItems.Add(new PublicInt { Value = 1 }); + await context.IntItems.Add(new PublicInt { Value = 2 }); + await context.IntItems.Add(new PublicInt { Value = 3 }); + await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.Value % 2 == 0) - .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString())); + using (var mapper = Mapper.CreateNew()) + { + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Value % 2 == 0) + .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString())); - var stringDtos = context - .IntItems - .ProjectUsing(mapper) - .To() - .ToArray(); + var stringDtos = context + .IntItems + .OrderBy(p => p.Id) + .ProjectUsing(mapper) + .To() + .ToArray(); - stringDtos.First().Value.ShouldBe("1"); - stringDtos.Second().Value.ShouldBe("4"); - stringDtos.Third().Value.ShouldBe("3"); - } - }); + stringDtos.First().Value.ShouldBe("1"); + stringDtos.Second().Value.ShouldBe("4"); + stringDtos.Third().Value.ShouldBe("3"); + } } + + #endregion } } From e48e1d8c3d85db261d42114347278d9da8dab485 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 13:47:14 +0000 Subject: [PATCH 155/176] Support for configurable project to null conditions --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Configuration/WhenMappingToNull.cs | 13 ++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + .../Configuration/WhenMappingToNull.cs | 62 +++++++++++++++++++ .../Infrastructure/OrmTestClassBase.cs | 8 +-- .../Api/Configuration/MappingConfigurator.cs | 6 ++ .../IConditionalRootProjectionConfigurator.cs | 11 +++- 7 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index dfd6f4539..458e8c28d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -157,6 +157,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs new file mode 100644 index 000000000..09c465eb8 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenMappingToNull : WhenMappingToNull + { + public WhenMappingToNull(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 6db437fdc..824fe18bc 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -78,6 +78,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs new file mode 100644 index 000000000..959cf9ec3 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs @@ -0,0 +1,62 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System.Linq; + using System.Threading.Tasks; + using AgileMapper.Extensions.Internal; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenMappingToNull : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenMappingToNull(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAUserConfiguration() + { + return RunTest(async (context, mapper) => + { + var person1 = new Person + { + Name = "Frank", + Address = new Address { Line1 = "1" } + }; + + var person2 = new Person + { + Name = "Dee", + Address = new Address { Line1 = "Paddie's Pub" } + }; + + await context.Persons.AddRange(person1, person2); + await context.SaveChanges(); + + mapper.WhenMapping + .From
() + .ProjectedTo() + .If(a => a.Line1.Length == 1) + .MapToNull(); + + var personDtos = context + .Persons + .ProjectUsing(mapper) + .To() + .OrderBy(p => p.Id) + .ToArray(); + + personDtos.Length.ShouldBe(2); + + personDtos.First().Name.ShouldBe("Frank"); + personDtos.First().Address.ShouldBeNull(); + + personDtos.Second().Name.ShouldBe("Dee"); + personDtos.Second().Address.ShouldNotBeNull(); + personDtos.Second().Address.Line1.ShouldBe("Paddie's Pub"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index f4953c476..74d557635 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -25,17 +25,15 @@ protected Task RunTestAndExpectThrow(Func(() => RunTest(test)); } + protected Task RunTest(Func test) + => RunTest(mapper => test.Invoke(Context, mapper)); + protected async Task RunTest(Func test) { try { await test.Invoke(Context); } - catch (Exception ex) - { - Debug.WriteLine(ex); - throw; - } finally { await EmptyDbContext(); diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index ee29fe2c1..fa1d49568 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -355,6 +355,12 @@ IProjectionConfigContinuation IConditionalRootProjectionConfig } public IMappingConfigContinuation MapToNull() + => RegisterMapToNullCondition(); + + IProjectionConfigContinuation IConditionalRootProjectionConfigurator.MapToNull() + => RegisterMapToNullCondition(); + + private MappingConfigContinuation RegisterMapToNullCondition() { var condition = new MapToNullCondition(ConfigInfo); diff --git a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs index fb7515af6..7f3db44d4 100644 --- a/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IConditionalRootProjectionConfigurator.cs @@ -9,7 +9,7 @@ public interface IConditionalRootProjectionConfigurator { /// - /// Map the source type being configured to the derived result type specified by + /// Project the source Type being configured to the derived result type specified by /// if the preceding condition evaluates to true. /// /// The derived result type to create. @@ -19,5 +19,14 @@ public interface IConditionalRootProjectionConfigurator IProjectionConfigContinuation MapTo() where TDerivedResult : TResultElement; + + /// + /// Project the result Type being configured to null if the preceding condition evaluates to true. + /// + /// + /// An IProjectionConfigContinuation to enable further configuration of mappings from and to the + /// source and result Type being configured. + /// + IProjectionConfigContinuation MapToNull(); } } \ No newline at end of file From bd6a9bed0134e127d57b56bd4b4e198d09b903a0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 14:06:26 +0000 Subject: [PATCH 156/176] Increasing use of RunTest(context, mapper ) overload --- .../Configuration/WhenConfiguringCallbacks.cs | 256 ++++++------- .../WhenConfiguringObjectTracking.cs | 31 +- .../WhenConfiguringConstructorDataSources.cs | 62 ++-- .../WhenConfiguringDataSources.cs | 341 ++++++++---------- .../WhenConfiguringDerivedTypes.cs | 234 ++++++------ .../WhenConfiguringEnumMapping.cs | 41 +-- .../WhenConfiguringObjectCreation.cs | 109 +++--- .../Configuration/WhenIgnoringMembers.cs | 156 ++++---- .../Infrastructure/OrmTestClassBase.cs | 12 +- .../WhenValidatingProjections.cs | 49 +-- AgileMapper.UnitTests/Should.cs | 18 +- 11 files changed, 599 insertions(+), 710 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs index ca09ed3be..0a5a9bb01 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs @@ -16,221 +16,203 @@ public WhenConfiguringCallbacks(InMemoryEfCore2TestContext context) [Fact] public Task ShouldNotAttemptToCallAPreMappingCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .To() - .Before - .MappingBegins - .Call(ctx => callbackCalled = true); + mapper.WhenMapping + .To() + .Before + .MappingBegins + .Call(ctx => callbackCalled = true); - var square = new Square { SideLength = 12 }; + var square = new Square { SideLength = 12 }; - await context.Shapes.AddAsync(square); - await context.SaveChangesAsync(); + await context.Shapes.AddAsync(square); + await context.SaveChangesAsync(); - var squareVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - squareVm.SideLength.ShouldBe(12); + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(12); - mapper.Map(square).OnTo(squareVm); + mapper.Map(square).OnTo(squareVm); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } [Fact] public Task ShouldNotAttemptToCallAPostMappingCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .From() - .To() - .After - .MappingEnds - .Call((s, c) => callbackCalled = true); + mapper.WhenMapping + .From() + .To() + .After + .MappingEnds + .Call((s, c) => callbackCalled = true); - var circle = new Circle { Diameter = 1 }; + var circle = new Circle { Diameter = 1 }; - await context.Shapes.AddAsync(circle); - await context.SaveChangesAsync(); + await context.Shapes.AddAsync(circle); + await context.SaveChangesAsync(); - var circleVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - circleVm.Diameter.ShouldBe(1); + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(1); - mapper.Map(circle).Over(circleVm); + mapper.Map(circle).Over(circleVm); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } [Fact] public Task ShouldNotAttemptToCallAPreObjectCreationCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .To() - .Before - .CreatingInstancesOf() - .Call(ctx => callbackCalled = true); + mapper.WhenMapping + .To() + .Before + .CreatingInstancesOf() + .Call(ctx => callbackCalled = true); - var person = new Person { Name = "Bjorn", Address = new Address { Line1 = "Sweden" } }; + var person = new Person { Name = "Bjorn", Address = new Address { Line1 = "Sweden" } }; - await context.Persons.AddAsync(person); - await context.SaveChangesAsync(); + await context.Persons.AddAsync(person); + await context.SaveChangesAsync(); - var personDto = context - .Persons - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - personDto.Name.ShouldBe("Bjorn"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("Sweden"); + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Bjorn"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); - mapper.Map(person).Over(personDto); + mapper.Map(person).Over(personDto); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } [Fact] public Task ShouldNotAttemptToCallAPostObjectCreationCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .To() - .After - .CreatingInstances - .Call(ctx => callbackCalled = true); + mapper.WhenMapping + .To() + .After + .CreatingInstances + .Call(ctx => callbackCalled = true); - var person = new Person { Name = "Benny", Address = new Address { Line1 = "Sweden" } }; + var person = new Person { Name = "Benny", Address = new Address { Line1 = "Sweden" } }; - await context.Persons.AddAsync(person); - await context.SaveChangesAsync(); + await context.Persons.AddAsync(person); + await context.SaveChangesAsync(); - var personDto = context - .Persons - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - personDto.Name.ShouldBe("Benny"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("Sweden"); + callbackCalled.ShouldBeFalse(); + personDto.Name.ShouldBe("Benny"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Sweden"); - mapper.Map(person).ToANew(); + mapper.Map(person).ToANew(); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } [Fact] public Task ShouldNotAttemptToCallAPreMemberMappingCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .To() - .Before - .Mapping(s => s.SideLength) - .Call(ctx => callbackCalled = true); + mapper.WhenMapping + .To() + .Before + .Mapping(s => s.SideLength) + .Call(ctx => callbackCalled = true); - var square = new Square { SideLength = 1 }; + var square = new Square { SideLength = 1 }; - await context.Shapes.AddAsync(square); - await context.SaveChangesAsync(); + await context.Shapes.AddAsync(square); + await context.SaveChangesAsync(); - var squareVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var squareVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - squareVm.SideLength.ShouldBe(1); + callbackCalled.ShouldBeFalse(); + squareVm.SideLength.ShouldBe(1); - mapper.Map(square).Over(squareVm); + mapper.Map(square).Over(squareVm); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } [Fact] public Task ShouldNotAttemptToCallAPostMemberMappingCallback() { - return RunTest(async context => + return RunTest(async (context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - var callbackCalled = false; + var callbackCalled = false; - mapper.WhenMapping - .To() - .After - .Mapping(c => c.Diameter) - .Call(ctx => callbackCalled = true); + mapper.WhenMapping + .To() + .After + .Mapping(c => c.Diameter) + .Call(ctx => callbackCalled = true); - var circle = new Circle { Diameter = 11 }; + var circle = new Circle { Diameter = 11 }; - await context.Shapes.AddAsync(circle); - await context.SaveChangesAsync(); + await context.Shapes.AddAsync(circle); + await context.SaveChangesAsync(); - var circleVm = context - .Shapes - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); - callbackCalled.ShouldBeFalse(); - circleVm.Diameter.ShouldBe(11); + callbackCalled.ShouldBeFalse(); + circleVm.Diameter.ShouldBe(11); - mapper.Map(circle).Over(circleVm); + mapper.Map(circle).Over(circleVm); - callbackCalled.ShouldBeTrue(); - } + callbackCalled.ShouldBeTrue(); }); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs index 50408452f..26744e5b9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringObjectTracking.cs @@ -18,7 +18,7 @@ public WhenConfiguringObjectTracking(InMemoryEfCore2TestContext context) [Fact] public Task ShouldIgnoreObjectTracking() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var circle1 = new Circle { Diameter = 1 }; var circle2 = new Circle { Diameter = 2 }; @@ -27,25 +27,22 @@ public Task ShouldIgnoreObjectTracking() await context.Shapes.AddRangeAsync(circle1, circle2, circle3); await context.SaveChangesAsync(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .To() - .MaintainIdentityIntegrity(); + mapper.WhenMapping + .To() + .MaintainIdentityIntegrity(); - var circleVms = await context - .Shapes - .ProjectUsing(mapper) - .To() - .OrderBy(c => c.Diameter) - .ToArrayAsync(); + var circleVms = await context + .Shapes + .ProjectUsing(mapper) + .To() + .OrderBy(c => c.Diameter) + .ToArrayAsync(); - circleVms.Length.ShouldBe(3); + circleVms.Length.ShouldBe(3); - circleVms.First().Diameter.ShouldBe(1); - circleVms.Second().Diameter.ShouldBe(2); - circleVms.Third().Diameter.ShouldBe(3); - } + circleVms.First().Diameter.ShouldBe(1); + circleVms.Second().Diameter.ShouldBe(2); + circleVms.Third().Diameter.ShouldBe(3); }); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs index 5fed9f813..4c56e1eb1 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringConstructorDataSources.cs @@ -20,29 +20,26 @@ protected Task RunShouldApplyAConfiguredConstantByParameterType() protected Task RunShouldErrorApplyingAConfiguredConstantByParameterType() => RunTestAndExpectThrow(DoShouldApplyAConfiguredConstantByParameterType); - private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context) + private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context, IMapper mapper) { var product = new Product { Name = "Prod.One" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map("Bananas!") - .ToCtor(); - - var productDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - productDto.ProductId.ShouldBe(product.ProductId); - productDto.Name.ShouldBe("Bananas!"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("Bananas!") + .ToCtor(); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("Bananas!"); } #endregion @@ -54,29 +51,26 @@ protected Task RunShouldApplyAConfiguredExpressionByParameterName() protected Task RunShouldErrorApplyingAConfiguredExpressionByParameterName() => RunTestAndExpectThrow(DoShouldApplyAConfiguredExpressionByParameterName); - private static async Task DoShouldApplyAConfiguredExpressionByParameterName(TOrmContext context) + private static async Task DoShouldApplyAConfiguredExpressionByParameterName(TOrmContext context, IMapper mapper) { var product = new Product { Name = "Prod.One" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map(p => "2 * 3 = " + (2 * 3)) - .ToCtor("name"); - - var productDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - productDto.ProductId.ShouldBe(product.ProductId); - productDto.Name.ShouldBe("2 * 3 = 6"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => "2 * 3 = " + (2 * 3)) + .ToCtor("name"); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("2 * 3 = 6"); } #endregion diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs index 8f2af3bae..adefa11d8 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDataSources.cs @@ -17,36 +17,33 @@ protected WhenConfiguringDataSources(ITestContext context) [Fact] public Task ShouldApplyAConfiguredConstant() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product = new Product { Name = "P1" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map("PRODUCT") - .To(dto => dto.Name); - - var productDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("PRODUCT") + .To(dto => dto.Name); - productDto.ProductId.ShouldBe(product.ProductId); - productDto.Name.ShouldBe("PRODUCT"); - } + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("PRODUCT"); }); } [Fact] public Task ShouldConditionallyApplyAConfiguredConstant() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product1 = new Product { Name = "P1" }; var product2 = new Product { Name = "P2" }; @@ -54,36 +51,33 @@ public Task ShouldConditionallyApplyAConfiguredConstant() await context.Products.AddRange(product1, product2); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.Name == "P2") - .Map("PRODUCT!?") - .To(dto => dto.Name); - - var productDtos = context - .Products - .ProjectUsing(mapper).To() - .OrderBy(p => p.ProductId) - .ToArray(); - - productDtos.Length.ShouldBe(2); - - productDtos.First().ProductId.ShouldBe(product1.ProductId); - productDtos.First().Name.ShouldBe("P1"); - - productDtos.Second().ProductId.ShouldBe(product2.ProductId); - productDtos.Second().Name.ShouldBe("PRODUCT!?"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Name == "P2") + .Map("PRODUCT!?") + .To(dto => dto.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P1"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("PRODUCT!?"); }); } [Fact] public Task ShouldApplyAConfiguredConstantToANestedMember() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var person = new Person { @@ -94,93 +88,84 @@ public Task ShouldApplyAConfiguredConstantToANestedMember() await context.Persons.Add(person); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map("LINE ONE!?") - .To(dto => dto.Address.Line1); - - var personDto = context - .Persons - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - personDto.Id.ShouldBe(person.PersonId); - personDto.Name.ShouldBe("Person 1"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("LINE ONE!?"); - personDto.Address.Postcode.ShouldBe("Postcode"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Map("LINE ONE!?") + .To(dto => dto.Address.Line1); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Person 1"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("LINE ONE!?"); + personDto.Address.Postcode.ShouldBe("Postcode"); }); } protected Task DoShouldApplyAConfiguredMember() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product = new Product { Name = "P1" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map(p => p.ProductId) - .To(dto => dto.Name); - - var personDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.ProductId) + .To(dto => dto.Name); - personDto.Id.ShouldBe(product.ProductId); - personDto.Name.ShouldBe(product.ProductId); - } + var personDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(product.ProductId); + personDto.Name.ShouldBe(product.ProductId); }); } protected Task DoShouldApplyMultipleConfiguredMembers() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product = new Product { Name = "Product1" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map(p => p.ProductId) - .To(dto => dto.Name) - .And - .Map(p => p.Name) - .To(p => p.Address.Line1); - - var personDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - personDto.Id.ShouldBe(product.ProductId); - personDto.Name.ShouldBe(product.ProductId); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("Product1"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.ProductId) + .To(dto => dto.Name) + .And + .Map(p => p.Name) + .To(p => p.Address.Line1); + + var personDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(product.ProductId); + personDto.Name.ShouldBe(product.ProductId); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Product1"); }); } [Fact] public Task ShouldConditionallyApplyAConfiguredMember() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; @@ -190,36 +175,33 @@ public Task ShouldConditionallyApplyAConfiguredMember() var product1Id = product1.ProductId; - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.ProductId > product1Id) - .Map(p => p.Name + "?!") - .To(dto => dto.Name); - - var productDtos = context - .Products - .ProjectUsing(mapper).To() - .OrderBy(p => p.ProductId) - .ToArray(); - - productDtos.Length.ShouldBe(2); - - productDtos.First().ProductId.ShouldBe(product1.ProductId); - productDtos.First().Name.ShouldBe("P.1"); - - productDtos.Second().ProductId.ShouldBe(product2.ProductId); - productDtos.Second().Name.ShouldBe("P.2?!"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.ProductId > product1Id) + .Map(p => p.Name + "?!") + .To(dto => dto.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("P.2?!"); }); } [Fact] public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; @@ -227,39 +209,36 @@ public Task ShouldApplyConditionalAndUnconditionalDataSourcesInOrder() await context.Products.AddRange(product1, product2); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Map(p => p.Name + '!') - .To(dto => dto.Name) - .But - .If(p => p.Name.Contains("1")) - .Map(p => p.Name + '?') - .To(dto => dto.Name); - - var productDtos = context - .Products - .ProjectUsing(mapper).To() - .OrderBy(p => p.ProductId) - .ToArray(); - - productDtos.Length.ShouldBe(2); - - productDtos.First().ProductId.ShouldBe(product1.ProductId); - productDtos.First().Name.ShouldBe("P.1?"); - - productDtos.Second().ProductId.ShouldBe(product2.ProductId); - productDtos.Second().Name.ShouldBe("P.2!"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Map(p => p.Name + '!') + .To(dto => dto.Name) + .But + .If(p => p.Name.Contains("1")) + .Map(p => p.Name + '?') + .To(dto => dto.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.Length.ShouldBe(2); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1?"); + + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("P.2!"); }); } [Fact] public Task ShouldHandleANullMemberInACondition() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var person1 = new Person { Name = "Frank", Address = new Address { Line1 = "Philly" } }; var person2 = new Person { Name = "Dee" }; @@ -267,51 +246,47 @@ public Task ShouldHandleANullMemberInACondition() await context.Persons.AddRange(person1, person2); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.Address.Line2 == null) - .Map("None") - .To(dto => dto.AddressLine2); - - var personDtos = context - .Persons - .ProjectUsing(mapper).To() - .OrderBy(p => p.Id) - .ToArray(); - - personDtos.Length.ShouldBe(2); - - personDtos.First().Id.ShouldBe(person1.PersonId); - personDtos.First().Name.ShouldBe("Frank"); - personDtos.First().AddressId.ShouldBe(person1.Address.AddressId); - personDtos.First().AddressLine1.ShouldBe("Philly"); - personDtos.First().AddressLine2.ShouldBe("None"); - personDtos.First().AddressPostcode.ShouldBeNull(); - - personDtos.Second().Id.ShouldBe(person2.PersonId); - personDtos.Second().Name.ShouldBe("Dee"); - personDtos.Second().AddressId.ShouldBeDefault(); - personDtos.Second().AddressLine1.ShouldBeNull(); - personDtos.Second().AddressLine2.ShouldBe("None"); - personDtos.Second().AddressPostcode.ShouldBeNull(); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Address.Line2 == null) + .Map("None") + .To(dto => dto.AddressLine2); + + var personDtos = context + .Persons + .ProjectUsing(mapper).To() + .OrderBy(p => p.Id) + .ToArray(); + + personDtos.Length.ShouldBe(2); + + personDtos.First().Id.ShouldBe(person1.PersonId); + personDtos.First().Name.ShouldBe("Frank"); + personDtos.First().AddressId.ShouldBe(person1.Address.AddressId); + personDtos.First().AddressLine1.ShouldBe("Philly"); + personDtos.First().AddressLine2.ShouldBe("None"); + personDtos.First().AddressPostcode.ShouldBeNull(); + + personDtos.Second().Id.ShouldBe(person2.PersonId); + personDtos.Second().Name.ShouldBe("Dee"); + personDtos.Second().AddressId.ShouldBeDefault(); + personDtos.Second().AddressLine1.ShouldBeNull(); + personDtos.Second().AddressLine2.ShouldBe("None"); + personDtos.Second().AddressPostcode.ShouldBeNull(); }); } [Fact] public Task ShouldSupportMultipleDivergedMappers() { - return RunTest(async context => + return RunTest(async (context, mapper1) => { var address = new Address { Line1 = "Philly", Postcode = "PH1 1LY" }; await context.Addresses.Add(address); await context.SaveChanges(); - using (var mapper1 = Mapper.CreateNew()) using (var mapper2 = Mapper.CreateNew()) { mapper2.WhenMapping diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs index a6ae7fd8d..8cf8f2e60 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringDerivedTypes.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; - using Xunit; using static TestClasses.Animal.AnimalType; public abstract class WhenConfiguringDerivedTypes : OrmTestClassBase @@ -23,7 +22,7 @@ protected Task RunShouldProjectToConfiguredDerivedTypes() protected Task RunShouldErrorProjectingToConfiguredDerivedTypes() => RunTestAndExpectThrow(DoShouldProjectToConfiguredDerivedTypes); - private static async Task DoShouldProjectToConfiguredDerivedTypes(TOrmContext context) + private static async Task DoShouldProjectToConfiguredDerivedTypes(TOrmContext context, IMapper mapper) { var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; var nelly = new Animal { Type = Elephant, Name = "Nelly" }; @@ -33,43 +32,40 @@ private static async Task DoShouldProjectToConfiguredDerivedTypes(TOrmContext co await context.Animals.AddRange(fido, nelly, kaa, sparkles); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(a => a.Type == Dog) - .MapTo() - .And - .If(a => a.Type == Elephant) - .MapTo() - .And - .If(a => a.Type == Snake) - .MapTo(); - - var animalDtos = context - .Animals - .ProjectUsing(mapper).To() - .OrderBy(a => a != null ? a.Id : 1000) - .ToArray(); - - animalDtos.First().ShouldBeOfType(); - animalDtos.First().Id.ShouldBe(fido.Id); - animalDtos.First().Name.ShouldBe("Fido"); - animalDtos.First().Sound.ShouldBe("Woof"); - - animalDtos.Second().ShouldBeOfType(); - animalDtos.Second().Id.ShouldBe(nelly.Id); - animalDtos.Second().Name.ShouldBe("Nelly"); - animalDtos.Second().Sound.ShouldBe("Trumpet"); - - animalDtos.Third().ShouldBeOfType(); - animalDtos.Third().Id.ShouldBe(kaa.Id); - animalDtos.Third().Name.ShouldBe("Kaa"); - animalDtos.Third().Sound.ShouldBe("Hiss"); - - animalDtos.Fourth().ShouldBeNull(); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type == Elephant) + .MapTo() + .And + .If(a => a.Type == Snake) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a != null ? a.Id : 1000) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("Trumpet"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(kaa.Id); + animalDtos.Third().Name.ShouldBe("Kaa"); + animalDtos.Third().Sound.ShouldBe("Hiss"); + + animalDtos.Fourth().ShouldBeNull(); } #endregion @@ -82,7 +78,7 @@ protected Task RunShouldProjectToAFallbackDerivedType() protected Task RunShouldErrorProjectingToAFallbackDerivedType() => RunTestAndExpectThrow(DoShouldProjectToAFallbackDerivedType); - private static async Task DoShouldProjectToAFallbackDerivedType(TOrmContext context) + private static async Task DoShouldProjectToAFallbackDerivedType(TOrmContext context, IMapper mapper) { var fido = new Animal { Type = Dog, Name = "Fido", Sound = "Bark" }; var nelly = new Animal { Type = Elephant, Name = "Nelly", Sound = "HrrrRRRRRRR" }; @@ -91,38 +87,35 @@ private static async Task DoShouldProjectToAFallbackDerivedType(TOrmContext cont await context.Animals.AddRange(fido, nelly, sparkles); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(a => a.Type == Dog) - .MapTo() - .And - .If(a => a.Type != Dog) - .MapTo(); - - var animalDtos = context - .Animals - .ProjectUsing(mapper).To() - .OrderBy(a => a.Id) - .ToArray(); - - animalDtos.First().ShouldBeOfType(); - animalDtos.First().Id.ShouldBe(fido.Id); - animalDtos.First().Name.ShouldBe("Fido"); - animalDtos.First().Sound.ShouldBe("Woof"); - - animalDtos.Second().ShouldBeOfType(); - animalDtos.Second().Id.ShouldBe(nelly.Id); - animalDtos.Second().Name.ShouldBe("Nelly"); - animalDtos.Second().Sound.ShouldBe("HrrrRRRRRRR"); - - animalDtos.Third().ShouldBeOfType(); - animalDtos.Third().Id.ShouldBe(sparkles.Id); - animalDtos.Third().Name.ShouldBe("Sparkles"); - animalDtos.Third().Sound.ShouldBe("Wheeeee!"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type != Dog) + .MapTo(); + + var animalDtos = context + .Animals + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("HrrrRRRRRRR"); + + animalDtos.Third().ShouldBeOfType(); + animalDtos.Third().Id.ShouldBe(sparkles.Id); + animalDtos.Third().Name.ShouldBe("Sparkles"); + animalDtos.Third().Sound.ShouldBe("Wheeeee!"); } #endregion @@ -135,66 +128,63 @@ protected Task RunShouldNotAttemptToApplyDerivedSourceTypePairing() protected Task RunShouldErrorAttemptingToNotApplyDerivedSourceTypePairing() => RunTestAndExpectThrow(DoShouldNotAttemptToApplyDerivedSourceTypePairing); - private static async Task DoShouldNotAttemptToApplyDerivedSourceTypePairing(TOrmContext context) + private static async Task DoShouldNotAttemptToApplyDerivedSourceTypePairing(TOrmContext context, IMapper mapper) { var square = new Square { Name = "Square", NumberOfSides = 4, SideLength = 10 }; var circle = new Circle { Name = "Circle", Diameter = 5 }; var shapes = new Shape[] { square, circle }; - using (var mapper = Mapper.CreateNew()) - { - var mappedShapeVms = mapper.Map(shapes).ToANew(); + var mappedShapeVms = mapper.Map(shapes).ToANew(); - mappedShapeVms.Length.ShouldBe(2); + mappedShapeVms.Length.ShouldBe(2); - mappedShapeVms.First().ShouldBeOfType(); - mappedShapeVms.Second().ShouldBeOfType(); + mappedShapeVms.First().ShouldBeOfType(); + mappedShapeVms.Second().ShouldBeOfType(); - await context.Shapes.AddRange(square, circle); - await context.SaveChanges(); - - mapper.WhenMapping - .From() - .ProjectedTo() - .If(s => s.Name == "Square") - .MapTo() - .But - .If(s => s.Name == "Circle") - .MapTo(); - - var shapeVms = context - .Shapes - .ProjectUsing(mapper).To() - .OrderBy(a => a.Id) - .ToArray(); - - shapeVms.Length.ShouldBe(2); - - var squareVm = shapeVms.First() as SquareViewModel; - squareVm.ShouldNotBeNull(); - - // ReSharper disable once PossibleNullReferenceException - squareVm.Id.ShouldBe(square.Id); - squareVm.Name.ShouldBe(square.Name); - squareVm.NumberOfSides.ShouldBe(square.NumberOfSides); - squareVm.SideLength.ShouldBe(square.SideLength); - - var circleVm = shapeVms.Second() as CircleViewModel; - circleVm.ShouldNotBeNull(); - - // ReSharper disable once PossibleNullReferenceException - circleVm.Id.ShouldBe(circle.Id); - circleVm.Name.ShouldBe(circle.Name); - circleVm.Diameter.ShouldBe(circle.Diameter); - - var mappedSquareVm = mapper.Map(square).ToANew(); + await context.Shapes.AddRange(square, circle); + await context.SaveChanges(); - mappedSquareVm.Id.ShouldBe(square.Id); - mappedSquareVm.Name.ShouldBe(square.Name); - mappedSquareVm.NumberOfSides.ShouldBe(square.NumberOfSides); - mappedSquareVm.SideLength.ShouldBe(square.SideLength); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(s => s.Name == "Square") + .MapTo() + .But + .If(s => s.Name == "Circle") + .MapTo(); + + var shapeVms = context + .Shapes + .ProjectUsing(mapper).To() + .OrderBy(a => a.Id) + .ToArray(); + + shapeVms.Length.ShouldBe(2); + + var squareVm = shapeVms.First() as SquareViewModel; + squareVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + squareVm.Id.ShouldBe(square.Id); + squareVm.Name.ShouldBe(square.Name); + squareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + squareVm.SideLength.ShouldBe(square.SideLength); + + var circleVm = shapeVms.Second() as CircleViewModel; + circleVm.ShouldNotBeNull(); + + // ReSharper disable once PossibleNullReferenceException + circleVm.Id.ShouldBe(circle.Id); + circleVm.Name.ShouldBe(circle.Name); + circleVm.Diameter.ShouldBe(circle.Diameter); + + var mappedSquareVm = mapper.Map(square).ToANew(); + + mappedSquareVm.Id.ShouldBe(square.Id); + mappedSquareVm.Name.ShouldBe(square.Name); + mappedSquareVm.NumberOfSides.ShouldBe(square.NumberOfSides); + mappedSquareVm.SideLength.ShouldBe(square.SideLength); } #endregion diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs index 2a35fee21..840326804 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringEnumMapping.cs @@ -19,7 +19,7 @@ protected WhenConfiguringEnumMapping(ITestContext context) : base(c [Fact] public Task ShouldPairEnumMembers() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var order1 = new OrderUk { @@ -42,32 +42,29 @@ public Task ShouldPairEnumMembers() await context.Orders.AddRange(order1, order2, order3); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check) - .And - .PairEnum(PaymentTypeUk.Card).With(PaymentTypeUs.Check); + mapper.WhenMapping + .From() + .ProjectedTo() + .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check) + .And + .PairEnum(PaymentTypeUk.Card).With(PaymentTypeUs.Check); - var orderVms = context - .Orders - .ProjectUsing(mapper).To() - .OrderBy(o => o.DatePlaced) - .ToArray(); + var orderVms = context + .Orders + .ProjectUsing(mapper).To() + .OrderBy(o => o.DatePlaced) + .ToArray(); - orderVms.Length.ShouldBe(3); + orderVms.Length.ShouldBe(3); - orderVms.First().DatePlaced.ShouldBe(order1.DatePlaced); - orderVms.First().PaymentType.ShouldBe(PaymentTypeUs.Check); + orderVms.First().DatePlaced.ShouldBe(order1.DatePlaced); + orderVms.First().PaymentType.ShouldBe(PaymentTypeUs.Check); - orderVms.Second().DatePlaced.ShouldBe(order2.DatePlaced); - orderVms.Second().PaymentType.ShouldBe(PaymentTypeUs.Cash); + orderVms.Second().DatePlaced.ShouldBe(order2.DatePlaced); + orderVms.Second().PaymentType.ShouldBe(PaymentTypeUs.Cash); - orderVms.Third().DatePlaced.ShouldBe(order3.DatePlaced); - orderVms.Third().PaymentType.ShouldBe(PaymentTypeUs.Check); - } + orderVms.Third().DatePlaced.ShouldBe(order3.DatePlaced); + orderVms.Third().PaymentType.ShouldBe(PaymentTypeUs.Check); }); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs index b851b8acc..6323be8ad 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -17,32 +17,29 @@ protected WhenConfiguringObjectCreation(ITestContext context) [Fact] public Task ShouldUseACustomObjectFactory() { - return RunTest(async context => + return RunTest(async (context, mapper) => { await context.StringItems.Add(new PublicString { Value = "Ctor!" }); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .ProjectionsTo() - .CreateInstancesUsing(o => new PublicStringDto { Value = "PANTS" }); - - var ctorDto = context - .StringItems - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - ctorDto.Value.ShouldBe("PANTS"); - } + mapper.WhenMapping + .ProjectionsTo() + .CreateInstancesUsing(o => new PublicStringDto { Value = "PANTS" }); + + var ctorDto = context + .StringItems + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + ctorDto.Value.ShouldBe("PANTS"); }); } [Fact] public Task ShouldUseACustomObjectFactoryForASpecifiedType() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var person = new Person { @@ -53,29 +50,26 @@ public Task ShouldUseACustomObjectFactoryForASpecifiedType() await context.Persons.Add(person); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .CreateInstancesOf().Using(p => new AddressDto - { - Line1 = p.Address.Line1 + "!", - Line2 = p.Address.Line2 + "?", - Postcode = "BA7 8RD" - }); - - var personDto = context - .Persons - .ProjectUsing(mapper) - .To() - .ShouldHaveSingleItem(); - - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBe("1!"); - personDto.Address.Line2.ShouldBe("2?"); - personDto.Address.Postcode.ShouldBe("BA7 8RD"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .CreateInstancesOf().Using(p => new AddressDto + { + Line1 = p.Address.Line1 + "!", + Line2 = p.Address.Line2 + "?", + Postcode = "BA7 8RD" + }); + + var personDto = context + .Persons + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("1!"); + personDto.Address.Line2.ShouldBe("2?"); + personDto.Address.Postcode.ShouldBe("BA7 8RD"); }); } @@ -87,32 +81,29 @@ protected Task RunShouldUseAConditionalObjectFactory() protected Task RunShouldErrorUsingAConditionalObjectFactory() => RunTestAndExpectThrow(DoShouldUseAConditionalObjectFactory); - private static async Task DoShouldUseAConditionalObjectFactory(TOrmContext context) + private static async Task DoShouldUseAConditionalObjectFactory(TOrmContext context, IMapper mapper) { await context.IntItems.Add(new PublicInt { Value = 1 }); await context.IntItems.Add(new PublicInt { Value = 2 }); await context.IntItems.Add(new PublicInt { Value = 3 }); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.Value % 2 == 0) - .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString())); - - var stringDtos = context - .IntItems - .OrderBy(p => p.Id) - .ProjectUsing(mapper) - .To() - .ToArray(); - - stringDtos.First().Value.ShouldBe("1"); - stringDtos.Second().Value.ShouldBe("4"); - stringDtos.Third().Value.ShouldBe("3"); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Value % 2 == 0) + .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString())); + + var stringDtos = context + .IntItems + .OrderBy(p => p.Id) + .ProjectUsing(mapper) + .To() + .ToArray(); + + stringDtos.First().Value.ShouldBe("1"); + stringDtos.Second().Value.ShouldBe("4"); + stringDtos.Third().Value.ShouldBe("3"); } #endregion diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index 2173ab1b5..74d7d0ed8 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -17,35 +17,32 @@ protected WhenIgnoringMembers(ITestContext context) [Fact] public Task DoShouldIgnoreAConfiguredMember() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product = new Product { Name = "P1" }; await context.Products.Add(product); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .Ignore(p => p.Name); - - var productDto = context - .Products - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - productDto.ProductId.ShouldBe(product.ProductId); - productDto.Name.ShouldBeNull(); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .Ignore(p => p.Name); + + var productDto = context + .Products + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBeNull(); }); } [Fact] public Task DoShouldIgnoreAConfiguredMemberConditionally() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var product1 = new Product { Name = "P.1" }; var product2 = new Product { Name = "P.2" }; @@ -53,32 +50,29 @@ public Task DoShouldIgnoreAConfiguredMemberConditionally() await context.Products.AddRange(product1, product2); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From() - .ProjectedTo() - .If(p => p.Name.EndsWith(2 + "")) - .Ignore(p => p.Name); - - var productDtos = context - .Products - .ProjectUsing(mapper).To() - .OrderBy(p => p.ProductId) - .ToArray(); - - productDtos.First().ProductId.ShouldBe(product1.ProductId); - productDtos.First().Name.ShouldBe("P.1"); - productDtos.Second().ProductId.ShouldBe(product2.ProductId); - productDtos.Second().Name.ShouldBeNull(); - } + mapper.WhenMapping + .From() + .ProjectedTo() + .If(p => p.Name.EndsWith(2 + "")) + .Ignore(p => p.Name); + + var productDtos = context + .Products + .ProjectUsing(mapper).To() + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBe("P.1"); + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBeNull(); }); } [Fact] public Task DoShouldIgnoreMembersByTypeAndTargetType() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var person = new Person { @@ -89,34 +83,31 @@ public Task DoShouldIgnoreMembersByTypeAndTargetType() await context.Persons.Add(person); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .From
() - .ProjectedTo() - .IgnoreTargetMembersOfType(); - - var personDto = context - .Persons - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - personDto.Id.ShouldBe(person.PersonId); - personDto.Name.ShouldBe("Mac"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Id.ShouldNotBe(person.Address.AddressId); - personDto.Address.Id.ShouldBeDefault(); - personDto.Address.Line1.ShouldBe("1"); - personDto.Address.Line2.ShouldBe("2"); - personDto.Address.Postcode.ShouldBe("3"); - } + mapper.WhenMapping + .From
() + .ProjectedTo() + .IgnoreTargetMembersOfType(); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Mac"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Id.ShouldNotBe(person.Address.AddressId); + personDto.Address.Id.ShouldBeDefault(); + personDto.Address.Line1.ShouldBe("1"); + personDto.Address.Line2.ShouldBe("2"); + personDto.Address.Postcode.ShouldBe("3"); }); } [Fact] public Task DoShouldIgnorePropertiesByPropertyInfoMatcher() { - return RunTest(async context => + return RunTest(async (context, mapper) => { var person = new Person { @@ -127,31 +118,28 @@ public Task DoShouldIgnorePropertiesByPropertyInfoMatcher() await context.Persons.Add(person); await context.SaveChanges(); - using (var mapper = Mapper.CreateNew()) - { - mapper.WhenMapping - .To() - .IgnoreTargetMembersWhere(member => - member.IsPropertyMatching(p => p.GetGetMethod().Name.StartsWith("get_Line2"))); - - mapper.WhenMapping - .From
() - .ProjectedTo() - .IgnoreTargetMembersWhere(member => - member.IsPropertyMatching(p => p.GetSetMethod().Name.EndsWith("Line1"))); - - var personDto = context - .Persons - .ProjectUsing(mapper).To() - .ShouldHaveSingleItem(); - - personDto.Id.ShouldBe(person.PersonId); - personDto.Name.ShouldBe("Frankie"); - personDto.Address.ShouldNotBeNull(); - personDto.Address.Line1.ShouldBeNull(); - personDto.Address.Line2.ShouldBeNull(); - personDto.Address.Postcode.ShouldBe("3"); - } + mapper.WhenMapping + .To() + .IgnoreTargetMembersWhere(member => + member.IsPropertyMatching(p => p.GetGetMethod().Name.StartsWith("get_Line2"))); + + mapper.WhenMapping + .From
() + .ProjectedTo() + .IgnoreTargetMembersWhere(member => + member.IsPropertyMatching(p => p.GetSetMethod().Name.EndsWith("Line1"))); + + var personDto = context + .Persons + .ProjectUsing(mapper).To() + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Frankie"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBeNull(); + personDto.Address.Line2.ShouldBeNull(); + personDto.Address.Postcode.ShouldBe("3"); }); } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 74d557635..e9c7e46ee 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -1,7 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Infrastructure { using System; - using System.Diagnostics; using System.Threading.Tasks; using Xunit; @@ -16,14 +15,11 @@ protected OrmTestClassBase(ITestContext context) protected TOrmContext Context { get; } - protected Task RunTestAndExpectThrow(Func test) - => RunTestAndExpectThrow(test); + protected Task RunTestAndExpectThrow(Func test) + => Should.ThrowAsync(() => RunTest(test)); - protected Task RunTestAndExpectThrow(Func test) - where TException : Exception - { - return Should.ThrowAsync(() => RunTest(test)); - } + protected Task RunTestAndExpectThrow(Func test) + => Should.ThrowAsync(() => RunTest(test)); protected Task RunTest(Func test) => RunTest(mapper => test.Invoke(Context, mapper)); diff --git a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs index 446b1a54c..91a6b1f50 100644 --- a/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs +++ b/AgileMapper.UnitTests.Orms/WhenValidatingProjections.cs @@ -17,14 +17,11 @@ protected WhenValidatingProjections(ITestContext context) [Fact] public Task ShouldSupportCachedProjectionValidation() { - return RunTest(context => + return RunTest((context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanForProjecting(context.Addresses).To(); + mapper.GetPlanForProjecting(context.Addresses).To(); - Should.NotThrow(() => mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); - } + Should.NotThrow(mapper.ThrowNowIfAnyMappingPlanIsIncomplete); return Task.CompletedTask; }); @@ -33,21 +30,18 @@ public Task ShouldSupportCachedProjectionValidation() [Fact] public Task ShouldErrorIfCachedProjectionMembersHaveNoDataSources() { - return RunTest(context => + return RunTest((context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanForProjecting(context.RotaEntries).To(); + mapper.GetPlanForProjecting(context.RotaEntries).To(); - var validationEx = Should.Throw(() => - mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); + var validationEx = Should.Throw(() => + mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); - validationEx.Message.ShouldContain("IQueryable -> IQueryable"); - validationEx.Message.ShouldContain("Rule set: Project"); - validationEx.Message.ShouldContain("Unmapped target members"); - validationEx.Message.ShouldContain("IQueryable[i].StartTime"); - validationEx.Message.ShouldContain("IQueryable[i].EndTime"); - } + validationEx.Message.ShouldContain("IQueryable -> IQueryable"); + validationEx.Message.ShouldContain("Rule set: Project"); + validationEx.Message.ShouldContain("Unmapped target members"); + validationEx.Message.ShouldContain("IQueryable[i].StartTime"); + validationEx.Message.ShouldContain("IQueryable[i].EndTime"); return Task.CompletedTask; }); @@ -56,20 +50,17 @@ public Task ShouldErrorIfCachedProjectionMembersHaveNoDataSources() [Fact] public Task ShouldErrorIfCachedProjectionTargetTypeIsUnconstructable() { - return RunTest(context => + return RunTest((context, mapper) => { - using (var mapper = Mapper.CreateNew()) - { - mapper.GetPlanForProjecting(context.Addresses).To(); + mapper.GetPlanForProjecting(context.Addresses).To(); - var validationEx = Should.Throw(() => - mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); + var validationEx = Should.Throw(() => + mapper.ThrowNowIfAnyMappingPlanIsIncomplete()); - validationEx.Message.ShouldContain("IQueryable
-> IQueryable"); - validationEx.Message.ShouldContain("Rule set: Project"); - validationEx.Message.ShouldContain("Unconstructable target Types"); - validationEx.Message.ShouldContain("Address -> ProductStruct"); - } + validationEx.Message.ShouldContain("IQueryable
-> IQueryable"); + validationEx.Message.ShouldContain("Rule set: Project"); + validationEx.Message.ShouldContain("Unconstructable target Types"); + validationEx.Message.ShouldContain("Address -> ProductStruct"); return Task.CompletedTask; }); diff --git a/AgileMapper.UnitTests/Should.cs b/AgileMapper.UnitTests/Should.cs index 18c866966..4f363c113 100644 --- a/AgileMapper.UnitTests/Should.cs +++ b/AgileMapper.UnitTests/Should.cs @@ -31,30 +31,18 @@ public static TException Throw(Func testFunc) throw new Exception("Expected exception of type " + typeof(TException).Name); } - public static Task ThrowAsync(Func test) - where TException : Exception - { - return ThrowAsync(async () => - { - await test.Invoke(); - - return new object(); - }); - } - - public static async Task ThrowAsync(Func> test) - where TException : Exception + public static async Task ThrowAsync(Func test) { try { await test.Invoke(); } - catch (TException ex) + catch (Exception ex) { return ex; } - throw new Exception("Expected exception of type " + typeof(TException).Name); + throw new Exception("Expected exception"); } public static void NotThrow(Action testAction) => NotThrow(testAction); From 8355dd45eb33b473719b41322bc469207975517e Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 14:15:11 +0000 Subject: [PATCH 157/176] Extending test coverage for project-to-null configuration --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../Configuration/WhenMappingToNull.cs | 18 ++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../Configuration/WhenMappingToNull.cs | 18 ++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../Configuration/WhenMappingToNull.cs | 18 ++++ .../Configuration/WhenMappingToNull.cs | 5 ++ .../Configuration/WhenMappingToNull.cs | 87 ++++++++++--------- 8 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenMappingToNull.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenMappingToNull.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenMappingToNull.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 0f6964365..06bdf9e89 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -91,6 +91,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenMappingToNull.cs new file mode 100644 index 000000000..aea186bf9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/WhenMappingToNull.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenMappingToNull : WhenMappingToNull + { + public WhenMappingToNull(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorApplyingAUserConfiguration() => RunShouldErrorApplyingAUserConfiguration(); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index f3d09d91d..0643be80b 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -93,6 +93,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenMappingToNull.cs new file mode 100644 index 000000000..e17e181cf --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenMappingToNull.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenMappingToNull : WhenMappingToNull + { + public WhenMappingToNull(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAUserConfiguration() => RunShouldApplyAUserConfiguration(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index c5df830ab..eaedba0b9 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -219,6 +219,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenMappingToNull.cs new file mode 100644 index 000000000..4809eb3d6 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenMappingToNull.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenMappingToNull : WhenMappingToNull + { + public WhenMappingToNull(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAUserConfiguration() => RunShouldApplyAUserConfiguration(); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs index 09c465eb8..ada6d48eb 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenMappingToNull.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; + using Xunit; public class WhenMappingToNull : WhenMappingToNull { @@ -9,5 +11,8 @@ public WhenMappingToNull(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldApplyAUserConfiguration() => RunShouldApplyAUserConfiguration(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs index 959cf9ec3..93766e5ae 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenMappingToNull.cs @@ -5,7 +5,6 @@ using AgileMapper.Extensions.Internal; using Infrastructure; using TestClasses; - using Xunit; public abstract class WhenMappingToNull : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -15,48 +14,54 @@ protected WhenMappingToNull(ITestContext context) { } - [Fact] - public Task ShouldApplyAUserConfiguration() + #region Project -> Configured Null + + protected Task RunShouldApplyAUserConfiguration() + => RunTest(DoShouldApplyAUserConfiguration); + + protected Task RunShouldErrorApplyingAUserConfiguration() + => RunTestAndExpectThrow(DoShouldApplyAUserConfiguration); + + private static async Task DoShouldApplyAUserConfiguration(TOrmContext context, IMapper mapper) { - return RunTest(async (context, mapper) => + var person1 = new Person { - var person1 = new Person - { - Name = "Frank", - Address = new Address { Line1 = "1" } - }; - - var person2 = new Person - { - Name = "Dee", - Address = new Address { Line1 = "Paddie's Pub" } - }; - - await context.Persons.AddRange(person1, person2); - await context.SaveChanges(); - - mapper.WhenMapping - .From
() - .ProjectedTo() - .If(a => a.Line1.Length == 1) - .MapToNull(); - - var personDtos = context - .Persons - .ProjectUsing(mapper) - .To() - .OrderBy(p => p.Id) - .ToArray(); - - personDtos.Length.ShouldBe(2); - - personDtos.First().Name.ShouldBe("Frank"); - personDtos.First().Address.ShouldBeNull(); - - personDtos.Second().Name.ShouldBe("Dee"); - personDtos.Second().Address.ShouldNotBeNull(); - personDtos.Second().Address.Line1.ShouldBe("Paddie's Pub"); - }); + Name = "Frank", + Address = new Address { Line1 = "1" } + }; + + var person2 = new Person + { + Name = "Dee", + Address = new Address { Line1 = "Paddie's Pub" } + }; + + await context.Persons.AddRange(person1, person2); + await context.SaveChanges(); + + mapper.WhenMapping + .From
() + .ProjectedTo() + .If(a => a.Line1.Length == 1) + .MapToNull(); + + var personDtos = context + .Persons + .ProjectUsing(mapper) + .To() + .OrderBy(p => p.Id) + .ToArray(); + + personDtos.Length.ShouldBe(2); + + personDtos.First().Name.ShouldBe("Frank"); + personDtos.First().Address.ShouldBeNull(); + + personDtos.Second().Name.ShouldBe("Dee"); + personDtos.Second().Address.ShouldNotBeNull(); + personDtos.Second().Address.Line1.ShouldBe("Paddie's Pub"); } + + #endregion } } From ed2cd1031042840aa9e43af3589b6a715e764e2a Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 14:29:05 +0000 Subject: [PATCH 158/176] Start of string-formatting projection tests --- .../Infrastructure/Ef5TestDbContext.cs | 8 +++- .../Infrastructure/Ef6TestDbContext.cs | 4 ++ .../Infrastructure/EfCore1TestDbContext.cs | 4 ++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringStringFormatting.cs | 13 ++++++ .../Infrastructure/EfCore2TestDbContext.cs | 4 ++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../WhenConfiguringStringFormatting.cs | 43 +++++++++++++++++++ .../Infrastructure/ITestDbContext.cs | 2 + .../Infrastructure/OrmTestClassBase.cs | 1 + .../TestClasses/PublicDateTime.cs | 14 ++++++ 11 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicDateTime.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 890b476a3..b8a5c0d6c 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -21,7 +21,7 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet Animals { get; set; } - public DbSet Shapes { get; set; } + public DbSet Shapes { get; set; } public DbSet Companies { get; set; } @@ -53,6 +53,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet LongItems { get; set; } + public DbSet DateTimeItems { get; set; } + public DbSet StringItems { get; set; } public DbSet TitleItems { get; set; } @@ -71,7 +73,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) #region ITestDbContext Members IDbSetWrapper ITestDbContext.Animals => new Ef5DbSetWrapper(this); - + IDbSetWrapper ITestDbContext.Shapes => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.Companies => new Ef5DbSetWrapper(this); @@ -104,6 +106,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.StringItems => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.TitleItems => new Ef5DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 2795f9d01..16a3cf5c8 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -53,6 +53,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet LongItems { get; set; } + public DbSet DateTimeItems { get; set; } + public DbSet StringItems { get; set; } public DbSet TitleItems { get; set; } @@ -104,6 +106,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.TitleItems => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index ea7bcae4e..7b4ef3f6d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -51,6 +51,8 @@ public EfCore1TestDbContext() public DbSet LongItems { get; set; } + public DbSet DateTimeItems { get; set; } + public DbSet StringItems { get; set; } public DbSet TitleItems { get; set; } @@ -111,6 +113,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.StringItems => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.TitleItems => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 458e8c28d..fb8f6d65c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -156,6 +156,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs new file mode 100644 index 000000000..675c7a11b --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using Infrastructure; + using Orms.Configuration; + + public class WhenConfiguringStringFormatting : WhenConfiguringStringFormatting + { + public WhenConfiguringStringFormatting(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 34ab8ecc4..36a092f7e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -59,6 +59,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet LongItems { get; set; } + public DbSet DateTimeItems { get; set; } + public DbSet StringItems { get; set; } public DbSet TitleItems { get; set; } @@ -119,6 +121,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.StringItems => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.TitleItems => new EfCore2DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 824fe18bc..f3963f934 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -77,6 +77,7 @@ + @@ -126,6 +127,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs new file mode 100644 index 000000000..b3b3b2634 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration +{ + using System; + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenConfiguringStringFormatting : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringStringFormatting(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldFormatDateTimesMapperWide() + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping + .StringsFrom(c => c.FormatUsing("o")); + + var source = new PublicDateTime { Value = DateTime.Now }; + var result = mapper.Map(source).ToANew(); + + result.Value.ShouldBe(source.Value.ToString("o")); + + await context.DateTimeItems.Add(source); + await context.SaveChanges(); + + var stringDto = context + .DateTimeItems + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + stringDto.Value.ShouldBe(source.Value.ToString("o")); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 6d676d11f..8a8316fcd 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -40,6 +40,8 @@ public interface ITestDbContext : IDisposable IDbSetWrapper LongItems { get; } + IDbSetWrapper DateTimeItems { get; } + IDbSetWrapper StringItems { get; } IDbSetWrapper TitleItems { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index e9c7e46ee..0c2a006b0 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -70,6 +70,7 @@ private async Task EmptyDbContext() Context.ShortItems.Clear(); Context.IntItems.Clear(); Context.LongItems.Clear(); + Context.DateTimeItems.Clear(); Context.StringItems.Clear(); Context.TitleItems.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTime.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTime.cs new file mode 100644 index 000000000..0ac60c7b2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicDateTime.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System; + using System.ComponentModel.DataAnnotations; + + public class PublicDateTime + { + [Key] + public int Id { get; set; } + + + public DateTime Value { get; set; } + } +} \ No newline at end of file From 762b78e3b9ea36eb8a63ffb517cfa362ba76b5d6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 17:25:56 +0000 Subject: [PATCH 159/176] Support for DateTime -> String conversion --- .../WhenConvertingToStrings.cs | 6 + .../WhenConvertingToStrings.cs | 6 + .../WhenConvertingToStrings.cs | 7 ++ .../WhenConvertingToStrings.cs | 7 ++ .../WhenConvertingToStrings.cs | 26 +++- .../Internal/ExpressionExtensions.cs | 4 + .../Converters/ToStringConverter.cs | 27 +++- .../Settings/DefaultQueryProviderSettings.cs | 2 + .../Ef5QueryProviderSettings.cs | 115 ++++++++++++++++-- .../EfCore2QueryProviderSettings.cs | 2 + .../LegacyEfQueryProviderSettings.cs | 16 ++- .../Settings/IQueryProviderSettings.cs | 2 + .../TypeConversion/ToStringConverter.cs | 7 +- 13 files changed, 194 insertions(+), 33 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs index ac726676d..d497baec8 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,8 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToStrings : WhenConvertingToStrings { @@ -10,5 +12,9 @@ public WhenConvertingToStrings(LocalDbTestContext context : base(context) { } + + [Fact] + public Task ShouldProjectADateTimeToAString() + => DoShouldProjectADateTimeToAString(d => d.ToString("yyyy-%M-dd HH:mm:ss")); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs index 327f8d648..7b4dff0ac 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.SimpleTypeConversion { + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToStrings : WhenConvertingToStrings { @@ -9,5 +11,9 @@ public WhenConvertingToStrings(InMemoryEf6TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectADateTimeToAString() + => DoShouldProjectADateTimeToAString(d => d.ToString("MM/dd/yyyy HH:mm:ss")); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs index 9a05e7ca8..967fc4951 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,7 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { + using System.Globalization; + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToStrings : WhenConvertingToStrings { @@ -9,5 +12,9 @@ public WhenConvertingToStrings(InMemoryEfCore1TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectADateTimeToAString() + => DoShouldProjectADateTimeToAString(d => d.ToString(CultureInfo.CurrentCulture.DateTimeFormat)); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs index f9b77d730..1653b3c0d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,7 +1,10 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { + using System.Globalization; + using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; + using Xunit; public class WhenConvertingToStrings : WhenConvertingToStrings { @@ -9,5 +12,9 @@ public WhenConvertingToStrings(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldProjectADateTimeToAString() + => DoShouldProjectADateTimeToAString(d => d.ToString(CultureInfo.CurrentCulture.DateTimeFormat)); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index ab35d027e..101242ef3 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { + using System; using System.Linq; using System.Threading.Tasks; using Infrastructure; @@ -14,6 +15,20 @@ protected WhenConvertingToStrings(ITestContext context) { } + [Fact] + public Task ShouldProjectABoolToAString() + { + return RunTest(async context => + { + await context.BoolItems.Add(new PublicBool { Value = true }); + await context.SaveChanges(); + + var stringItem = context.BoolItems.Project().To().First(); + + stringItem.Value.ShouldBe("true"); + }); + } + [Fact] public Task ShouldProjectAnIntToAString() { @@ -28,17 +43,18 @@ public Task ShouldProjectAnIntToAString() }); } - [Fact] - public Task ShouldProjectABoolToAString() + protected Task DoShouldProjectADateTimeToAString(Func expectedDateStringFactory) { return RunTest(async context => { - await context.BoolItems.Add(new PublicBool { Value = true }); + var now = DateTime.Now; + + await context.DateTimeItems.Add(new PublicDateTime { Value = now }); await context.SaveChanges(); - var stringItem = context.BoolItems.Project().To().First(); + var stringItem = context.DateTimeItems.Project().To().First(); - stringItem.Value.ShouldBe("true"); + stringItem.Value.ShouldBe(expectedDateStringFactory.Invoke(now)); }); } } diff --git a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs index 57fd3e03b..43dfd034f 100644 --- a/AgileMapper/Extensions/Internal/ExpressionExtensions.cs +++ b/AgileMapper/Extensions/Internal/ExpressionExtensions.cs @@ -238,6 +238,10 @@ private static bool TryGetWrapperMethod( private static MethodInfo GetNonListToArrayConversionMethod(EnumerableTypeHelper typeHelper) => typeHelper.HasCollectionInterface ? _collectionToArrayMethod : _linqToArrayMethod; + [DebuggerStepThrough] + public static MethodCallExpression WithToStringCall(this Expression value) + => Expression.Call(value, value.Type.GetPublicInstanceMethod("ToString", parameterCount: 0)); + public static Expression WithToReadOnlyCollectionCall(this Expression enumerable, Type elementType) { var typeHelper = new EnumerableTypeHelper(enumerable.Type, elementType); diff --git a/AgileMapper/Queryables/Converters/ToStringConverter.cs b/AgileMapper/Queryables/Converters/ToStringConverter.cs index 653145090..50ba76189 100644 --- a/AgileMapper/Queryables/Converters/ToStringConverter.cs +++ b/AgileMapper/Queryables/Converters/ToStringConverter.cs @@ -7,24 +7,39 @@ internal static class ToStringConverter { public static bool TryConvert( MethodCallExpression methodCall, - IQueryProjectionModifier context, + IQueryProjectionModifier modifier, out Expression converted) { - if (context.Settings.SupportsToString || IsNotToStringCall(methodCall)) + if (IsNotToStringCall(methodCall)) { converted = null; return false; } - converted = context.Settings.ConvertToStringCall(methodCall); + if (modifier.Settings.SupportsToString) + { + converted = AdjustForFormatStringIfNecessary(methodCall, modifier); + return converted != methodCall; + } + + methodCall = AdjustForFormatStringIfNecessary(methodCall, modifier); + converted = modifier.Settings.ConvertToStringCall(methodCall); return true; } private static bool IsNotToStringCall(MethodCallExpression methodCall) + => methodCall.Method.IsStatic || (methodCall.Method.Name != nameof(ToString)); + + private static MethodCallExpression AdjustForFormatStringIfNecessary( + MethodCallExpression methodCall, + IQueryProjectionModifier context) { - return methodCall.Arguments.Any() || - methodCall.Method.IsStatic || - (methodCall.Method.Name != nameof(ToString)); + if (context.Settings.SupportsToStringWithFormat || methodCall.Arguments.None()) + { + return methodCall; + } + + return methodCall.Object.WithToStringCall(); } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 48e223a83..640a2096a 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -29,6 +29,8 @@ public DefaultQueryProviderSettings() public virtual bool SupportsToString => true; + public virtual bool SupportsToStringWithFormat => false; + public virtual bool SupportsStringEqualsIgnoreCase => false; public virtual bool SupportsStringToEnumConversion => true; diff --git a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs index 647bfe818..f7a016abd 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.Queryables.Settings.EntityFramework { using System; + using System.Globalization; using System.Linq.Expressions; + using System.Reflection; using Extensions.Internal; using NetStandardPolyfills; @@ -34,22 +36,21 @@ public override Expression ConvertToStringCall(MethodCallExpression call) return base.ConvertToStringCall(call); } - var stringConvertCall = GetStringConvertCall(call.Object, sqlFunctionsType); - var trimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); - var trimCall = Expression.Call(stringConvertCall, trimMethod); - - return trimCall; + return GetStringConvertCall(call.Object, sqlFunctionsType); } private static Expression GetStringConvertCall(Expression subject, Type sqlFunctionsType) { var subjectType = subject.Type.GetNonNullableType(); + if (subjectType == typeof(DateTime)) + { + return GetParseDateTimeToStringOrNull(subject, sqlFunctionsType); + } + if (subjectType == typeof(decimal)) { - return Expression.Call( - sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(decimal?)), - subject); + return GetTrimmedStringConvertCall(sqlFunctionsType, subject); } if (subjectType != typeof(double)) @@ -57,9 +58,103 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct subject = Expression.Convert(subject, typeof(double?)); } - return Expression.Call( - sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(double?)), + return GetTrimmedStringConvertCall(sqlFunctionsType, subject); + } + + private static Expression GetTrimmedStringConvertCall(Type sqlFunctionsType, Expression subject) + { + var stringConvertCall = Expression.Call( + sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(TArgument)), subject); + + var trimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); + var trimCall = Expression.Call(stringConvertCall, trimMethod); + + return trimCall; + } + + private static Expression GetParseDateTimeToStringOrNull(Expression dateValue, Type sqlFunctionsType) + { + if (!TryGetDatePartMethod(sqlFunctionsType, out var datePartMethod)) + { + return null; + } + + dateValue = dateValue.GetConversionTo(); + + var dateTimePattern = CultureInfo + .CurrentCulture + .DateTimeFormat + .SortableDateTimePattern + .Replace("mm", "mi") // Minutes + .Replace('T', ' ') // Date-Time separator + .Replace("'", null) + .ToLowerInvariant(); + + Expression valueConcatenation = null; + var datePartStartIndex = 0; + var stringConcat = StringExpressionExtensions.GetConcatMethod(parameterCount: 2); + + for (var i = 0; i < dateTimePattern.Length; ++i) + { + if (char.IsLetter(dateTimePattern[i])) + { + continue; + } + + var datePartNameCall = GetDatePartCall( + datePartMethod, + dateTimePattern, + datePartStartIndex, + i, + dateValue, + sqlFunctionsType); + + var added = Expression.Call( + stringConcat, + datePartNameCall, + dateTimePattern[i].ToString().ToConstantExpression()); + + valueConcatenation = (valueConcatenation != null) + ? Expression.Call(stringConcat, valueConcatenation, added) : added; + + datePartStartIndex = i + 1; + } + + var finalDatePartNameCall = GetDatePartCall( + datePartMethod, + dateTimePattern, + datePartStartIndex, + dateTimePattern.Length, + dateValue, + sqlFunctionsType); + + // ReSharper disable once AssignNullToNotNullAttribute + return Expression.Call(stringConcat, valueConcatenation, finalDatePartNameCall); + } + + private static Expression GetDatePartCall( + MethodInfo datePartMethod, + string dateTimePattern, + int datePartStartIndex, + int datePartEndIndex, + Expression dateValue, + Type sqlFunctionsType) + { + var datePartNameCall = GetDatePartCall( + datePartMethod, + GetDatePart(dateTimePattern, datePartStartIndex, datePartEndIndex), + dateValue); + + return GetStringConvertCall(datePartNameCall, sqlFunctionsType); + } + + private static string GetDatePart( + string dateTimePattern, + int datePartStartIndex, + int datePartEndIndex) + { + return dateTimePattern.Substring(datePartStartIndex, datePartEndIndex - datePartStartIndex); } } } \ No newline at end of file diff --git a/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs index 96c1227fe..1988fb562 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/EfCore2QueryProviderSettings.cs @@ -2,6 +2,8 @@ { internal class EfCore2QueryProviderSettings : DefaultQueryProviderSettings { + public override bool SupportsToStringWithFormat => true; + public override bool SupportsStringEqualsIgnoreCase => true; // EF Core translates navigation property-to-null comparisons to compare diff --git a/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs index 8afd5f9b0..0d32af5e2 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/LegacyEfQueryProviderSettings.cs @@ -30,11 +30,7 @@ protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpressio return null; } - var datePartMethod = SqlFunctionsType - .GetPublicStaticMethods("DatePart") - .FirstOrDefault(m => m.GetParameters().All(p => p.ParameterType == typeof(string))); - - if (datePartMethod == null) + if (!TryGetDatePartMethod(SqlFunctionsType, out var datePartMethod)) { return null; } @@ -63,7 +59,15 @@ protected override Expression GetParseStringToDateTimeOrNull(MethodCallExpressio #region GetCreateDateTimeFromStringOrNull Helpers - private static Expression GetDatePartCall( + protected static bool TryGetDatePartMethod(Type sqlFunctionsType, out MethodInfo datePartMethod) + { + datePartMethod = sqlFunctionsType + .GetPublicStaticMethod("DatePart", typeof(string), typeof(TArgument)); + + return datePartMethod != null; + } + + protected static Expression GetDatePartCall( MethodInfo datePartMethod, string datePart, Expression sourceValue) diff --git a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs index d93beb378..ab1b107e7 100644 --- a/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/IQueryProviderSettings.cs @@ -11,6 +11,8 @@ internal interface IQueryProviderSettings bool SupportsToString { get; } + bool SupportsToStringWithFormat { get; } + bool SupportsStringEqualsIgnoreCase { get; } bool SupportsStringToEnumConversion { get; } diff --git a/AgileMapper/TypeConversion/ToStringConverter.cs b/AgileMapper/TypeConversion/ToStringConverter.cs index a117e3f5f..d33ffe5c6 100644 --- a/AgileMapper/TypeConversion/ToStringConverter.cs +++ b/AgileMapper/TypeConversion/ToStringConverter.cs @@ -66,12 +66,7 @@ public Expression GetConversion(Expression sourceValue) return Expression.Call(operatorMethod, sourceValue); } - var toStringMethod = sourceValue.Type - .GetPublicInstanceMethod("ToString", parameterCount: 0); - - var toStringCall = Expression.Call(sourceValue, toStringMethod); - - return toStringCall; + return sourceValue.WithToStringCall(); } #region Byte[] Conversion From 3e5b19979c644e717e7e3be6590452dd3f264fb1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 17:38:08 +0000 Subject: [PATCH 160/176] Support for configured string formatting --- ...leMapper.UnitTests.Orms.Ef5.LocalDb.csproj | 1 + .../WhenConfiguringStringFormatting.cs | 20 +++++++++++++++++++ .../WhenConvertingToStrings.cs | 2 +- .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringStringFormatting.cs | 19 ++++++++++++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringStringFormatting.cs | 18 +++++++++++++++++ .../WhenConfiguringStringFormatting.cs | 5 +++++ .../WhenConfiguringStringFormatting.cs | 6 ++---- 9 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj index 53e2ffb20..66af87562 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/AgileMapper.UnitTests.Orms.Ef5.LocalDb.csproj @@ -81,6 +81,7 @@ VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs new file mode 100644 index 000000000..d8fb7f010 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.LocalDb.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Orms.Infrastructure; + using Xunit; + + public class WhenConfiguringStringFormatting : WhenConfiguringStringFormatting + { + public WhenConfiguringStringFormatting(LocalDbTestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldFormatDateTimes() + => DoShouldFormatDateTimes(d => d.ToString("yyyy-%M-%d %H:%m:%s")); + } +} diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs index d497baec8..d122dd0a8 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -15,6 +15,6 @@ public WhenConvertingToStrings(LocalDbTestContext context [Fact] public Task ShouldProjectADateTimeToAString() - => DoShouldProjectADateTimeToAString(d => d.ToString("yyyy-%M-dd HH:mm:ss")); + => DoShouldProjectADateTimeToAString(d => d.ToString("yyyy-%M-%d %H:%m:%s")); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 0643be80b..645d51773 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -92,6 +92,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs new file mode 100644 index 000000000..609f0d13a --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs @@ -0,0 +1,19 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringStringFormatting : WhenConfiguringStringFormatting + { + public WhenConfiguringStringFormatting(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldFormatDateTimes() + => DoShouldFormatDateTimes(d => d.ToString("MM/dd/yyyy HH:mm:ss")); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index eaedba0b9..d13da51cb 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -218,6 +218,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs new file mode 100644 index 000000000..fd1f2cf96 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration; + using Xunit; + + public class WhenConfiguringStringFormatting : WhenConfiguringStringFormatting + { + public WhenConfiguringStringFormatting(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs index 675c7a11b..281851bdc 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs @@ -1,7 +1,9 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System.Threading.Tasks; using Infrastructure; using Orms.Configuration; + using Xunit; public class WhenConfiguringStringFormatting : WhenConfiguringStringFormatting { @@ -9,5 +11,8 @@ public WhenConfiguringStringFormatting(InMemoryEfCore2TestContext context) : base(context) { } + + [Fact] + public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs index b3b3b2634..7f34460bc 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; - using Xunit; public abstract class WhenConfiguringStringFormatting : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -14,8 +13,7 @@ protected WhenConfiguringStringFormatting(ITestContext context) { } - [Fact] - public Task ShouldFormatDateTimesMapperWide() + protected Task DoShouldFormatDateTimes(Func expectedDateStringFactory) { return RunTest(async (context, mapper) => { @@ -36,7 +34,7 @@ public Task ShouldFormatDateTimesMapperWide() .To() .ShouldHaveSingleItem(); - stringDto.Value.ShouldBe(source.Value.ToString("o")); + stringDto.Value.ShouldBe(expectedDateStringFactory.Invoke(source.Value)); }); } } From f88e6a07358d155c07646694c0674956a4b69c2d Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Tue, 13 Feb 2018 21:19:39 +0000 Subject: [PATCH 161/176] Test coverage + support for decimal -> formatted string projection --- .../WhenConfiguringStringFormatting.cs | 3 ++ .../Infrastructure/Ef5TestDbContext.cs | 8 +++ .../WhenConfiguringStringFormatting.cs | 3 ++ .../Infrastructure/Ef6TestDbContext.cs | 8 +++ .../WhenConfiguringStringFormatting.cs | 3 ++ .../Infrastructure/EfCore1TestDbContext.cs | 8 +++ .../WhenConfiguringStringFormatting.cs | 3 ++ .../Infrastructure/EfCore2TestDbContext.cs | 8 +++ .../AgileMapper.UnitTests.Orms.csproj | 2 + .../WhenConfiguringStringFormatting.cs | 25 +++++++++ .../Infrastructure/ITestDbContext.cs | 4 ++ .../Infrastructure/OrmTestClassBase.cs | 2 + .../TestClasses/PublicDecimal.cs | 13 +++++ .../TestClasses/PublicDouble.cs | 13 +++++ .../Extensions/Internal/TypeExtensions.cs | 3 ++ .../Ef5QueryProviderSettings.cs | 52 +++++++++++++------ 16 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicDecimal.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicDouble.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs index d8fb7f010..0cae07312 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs @@ -16,5 +16,8 @@ public WhenConfiguringStringFormatting(LocalDbTestContext [Fact] public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("yyyy-%M-%d %H:%m:%s")); + + [Fact] + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString(".000000")); } } diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index b8a5c0d6c..6685b64b7 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -53,6 +53,10 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet LongItems { get; set; } + public DbSet DecimalItems { get; set; } + + public DbSet DoubleItems { get; set; } + public DbSet DateTimeItems { get; set; } public DbSet StringItems { get; set; } @@ -106,6 +110,10 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DecimalItems => new Ef5DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.DoubleItems => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new Ef5DbSetWrapper(this); IDbSetWrapper ITestDbContext.StringItems => new Ef5DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs index 609f0d13a..42ca72fe8 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs @@ -15,5 +15,8 @@ public WhenConfiguringStringFormatting(InMemoryEf6TestContext context) [Fact] public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("MM/dd/yyyy HH:mm:ss")); + + [Fact] + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d + ""); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 16a3cf5c8..fb56b0603 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -53,6 +53,10 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet LongItems { get; set; } + public DbSet DecimalItems { get; set; } + + public DbSet DoubleItems { get; set; } + public DbSet DateTimeItems { get; set; } public DbSet StringItems { get; set; } @@ -106,6 +110,10 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DecimalItems => new Ef6DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.DoubleItems => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs index fd1f2cf96..aca3c9b92 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs @@ -14,5 +14,8 @@ public WhenConfiguringStringFormatting(InMemoryEfCore1TestContext context) [Fact] public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); + + [Fact] + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString("C")); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 7b4ef3f6d..95f317c7b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -51,6 +51,10 @@ public EfCore1TestDbContext() public DbSet LongItems { get; set; } + public DbSet DecimalItems { get; set; } + + public DbSet DoubleItems { get; set; } + public DbSet DateTimeItems { get; set; } public DbSet StringItems { get; set; } @@ -113,6 +117,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DecimalItems => new EfCore1DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.DoubleItems => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new EfCore1DbSetWrapper(this); IDbSetWrapper ITestDbContext.StringItems => new EfCore1DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs index 281851bdc..d1ae5a048 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs @@ -14,5 +14,8 @@ public WhenConfiguringStringFormatting(InMemoryEfCore2TestContext context) [Fact] public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); + + [Fact] + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString("C")); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 36a092f7e..29421b6ac 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -59,6 +59,10 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet LongItems { get; set; } + public DbSet DecimalItems { get; set; } + + public DbSet DoubleItems { get; set; } + public DbSet DateTimeItems { get; set; } public DbSet StringItems { get; set; } @@ -121,6 +125,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.LongItems => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DecimalItems => new EfCore2DbSetWrapper(this); + + IDbSetWrapper ITestDbContext.DoubleItems => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.DateTimeItems => new EfCore2DbSetWrapper(this); IDbSetWrapper ITestDbContext.StringItems => new EfCore2DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index f3963f934..0ada5acc2 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -129,8 +129,10 @@ + + diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs index 7f34460bc..e4f5541fc 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs @@ -37,5 +37,30 @@ protected Task DoShouldFormatDateTimes(Func expectedDateString stringDto.Value.ShouldBe(expectedDateStringFactory.Invoke(source.Value)); }); } + + protected Task DoShouldFormatDecimals(Func expectedDecimalStringFactory) + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping + .StringsFrom(c => c.FormatUsing("C")); + + var source = new PublicDecimal { Value = 674378.52m }; + var result = mapper.Map(source).ToANew(); + + result.Value.ShouldBe(source.Value.ToString("C")); + + await context.DecimalItems.Add(source); + await context.SaveChanges(); + + var stringDto = context + .DecimalItems + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + stringDto.Value.ShouldBe(expectedDecimalStringFactory.Invoke(source.Value)); + }); + } } } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 8a8316fcd..98a0cfd3b 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -40,6 +40,10 @@ public interface ITestDbContext : IDisposable IDbSetWrapper LongItems { get; } + IDbSetWrapper DecimalItems { get; } + + IDbSetWrapper DoubleItems { get; } + IDbSetWrapper DateTimeItems { get; } IDbSetWrapper StringItems { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 0c2a006b0..4a9b9741c 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -70,6 +70,8 @@ private async Task EmptyDbContext() Context.ShortItems.Clear(); Context.IntItems.Clear(); Context.LongItems.Clear(); + Context.DecimalItems.Clear(); + Context.DoubleItems.Clear(); Context.DateTimeItems.Clear(); Context.StringItems.Clear(); Context.TitleItems.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicDecimal.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicDecimal.cs new file mode 100644 index 000000000..8fca6251d --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicDecimal.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicDecimal + { + [Key] + public int Id { get; set; } + + + public decimal Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicDouble.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicDouble.cs new file mode 100644 index 000000000..3cf2cd01f --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicDouble.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + public class PublicDouble + { + [Key] + public int Id { get; set; } + + + public double Value { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper/Extensions/Internal/TypeExtensions.cs b/AgileMapper/Extensions/Internal/TypeExtensions.cs index cc817e984..f39740928 100644 --- a/AgileMapper/Extensions/Internal/TypeExtensions.cs +++ b/AgileMapper/Extensions/Internal/TypeExtensions.cs @@ -289,6 +289,9 @@ public static bool IsUnsignedNumeric(this Type type) public static bool IsWholeNumberNumeric(this Type type) => Constants.WholeNumberNumericTypes.Contains(type); + public static bool IsNonWholeNumberNumeric(this Type type) + => IsNumeric(type) && !IsWholeNumberNumeric(type); + private static double GetMaxValueFor(Type type) => GetValueFor(type, Constants.NumericTypeMaxValuesByType, values => values.Max()); diff --git a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs index f7a016abd..5291b44fa 100644 --- a/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/EntityFramework/Ef5QueryProviderSettings.cs @@ -39,7 +39,10 @@ public override Expression ConvertToStringCall(MethodCallExpression call) return GetStringConvertCall(call.Object, sqlFunctionsType); } - private static Expression GetStringConvertCall(Expression subject, Type sqlFunctionsType) + private static Expression GetStringConvertCall( + Expression subject, + Type sqlFunctionsType, + bool includeDecimalPlaces = true) { var subjectType = subject.Type.GetNonNullableType(); @@ -48,24 +51,40 @@ private static Expression GetStringConvertCall(Expression subject, Type sqlFunct return GetParseDateTimeToStringOrNull(subject, sqlFunctionsType); } - if (subjectType == typeof(decimal)) - { - return GetTrimmedStringConvertCall(sqlFunctionsType, subject); - } + return (subjectType == typeof(decimal)) + ? GetTrimmedStringConvertCall(sqlFunctionsType, subject, subjectType, includeDecimalPlaces) + : GetTrimmedStringConvertCall(sqlFunctionsType, subject, subjectType, includeDecimalPlaces); + } - if (subjectType != typeof(double)) + private static Expression GetTrimmedStringConvertCall( + Type sqlFunctionsType, + Expression subject, + Type subjectType, + bool includeDecimalPlaces) + { + if (includeDecimalPlaces) { - subject = Expression.Convert(subject, typeof(double?)); + includeDecimalPlaces = subjectType.IsNonWholeNumberNumeric(); } - return GetTrimmedStringConvertCall(sqlFunctionsType, subject); - } + subject = subject.GetConversionTo(); - private static Expression GetTrimmedStringConvertCall(Type sqlFunctionsType, Expression subject) - { - var stringConvertCall = Expression.Call( - sqlFunctionsType.GetPublicStaticMethod("StringConvert", typeof(TArgument)), - subject); + Expression stringConvertCall; + + if (includeDecimalPlaces) + { + stringConvertCall = Expression.Call( + GetConvertMethod(sqlFunctionsType, typeof(TSubject), typeof(int?), typeof(int?)), + subject.GetConversionTo(), + 20.ToConstantExpression(typeof(int?)), // <-- Total Length + 6.ToConstantExpression(typeof(int?))); // <-- Decimal places + } + else + { + stringConvertCall = Expression.Call( + GetConvertMethod(sqlFunctionsType, typeof(TSubject)), + subject); + } var trimMethod = typeof(string).GetPublicInstanceMethod("Trim", parameterCount: 0); var trimCall = Expression.Call(stringConvertCall, trimMethod); @@ -73,6 +92,9 @@ private static Expression GetTrimmedStringConvertCall(Type sqlFunctio return trimCall; } + private static MethodInfo GetConvertMethod(Type sqlFunctionsType, params Type[] argumentTypes) + => sqlFunctionsType.GetPublicStaticMethod("StringConvert", argumentTypes); + private static Expression GetParseDateTimeToStringOrNull(Expression dateValue, Type sqlFunctionsType) { if (!TryGetDatePartMethod(sqlFunctionsType, out var datePartMethod)) @@ -146,7 +168,7 @@ private static Expression GetDatePartCall( GetDatePart(dateTimePattern, datePartStartIndex, datePartEndIndex), dateValue); - return GetStringConvertCall(datePartNameCall, sqlFunctionsType); + return GetStringConvertCall(datePartNameCall, sqlFunctionsType, false); } private static string GetDatePart( From 8dd32f71e4cbb87c42454f26708c7525b09d5ba9 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 08:47:41 +0000 Subject: [PATCH 162/176] Test coverage for double -> string projections --- .../WhenConfiguringStringFormatting.cs | 3 ++ .../WhenConfiguringStringFormatting.cs | 3 ++ .../WhenConfiguringStringFormatting.cs | 7 ++- .../WhenConfiguringStringFormatting.cs | 7 ++- .../WhenConfiguringStringFormatting.cs | 44 ++++++++++++++++++- 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs index 0cae07312..041e8259e 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/Configuration/WhenConfiguringStringFormatting.cs @@ -19,5 +19,8 @@ public Task ShouldFormatDateTimes() [Fact] public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString(".000000")); + + [Fact] + public Task ShouldFormatDoubles() => DoShouldFormatDoubles(d => d.ToString(".000000")); } } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs index 42ca72fe8..30c191fe0 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/WhenConfiguringStringFormatting.cs @@ -18,5 +18,8 @@ public Task ShouldFormatDateTimes() [Fact] public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d + ""); + + [Fact] + public Task ShouldFormatDoubles() => DoShouldFormatDoubles(d => d + ""); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs index aca3c9b92..e5e7bf19b 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/WhenConfiguringStringFormatting.cs @@ -13,9 +13,12 @@ public WhenConfiguringStringFormatting(InMemoryEfCore1TestContext context) } [Fact] - public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); + public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(); [Fact] - public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString("C")); + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(); + + [Fact] + public Task ShouldFormatDoubles() => DoShouldFormatDoubles(); } } diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs index d1ae5a048..b21e60195 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringStringFormatting.cs @@ -13,9 +13,12 @@ public WhenConfiguringStringFormatting(InMemoryEfCore2TestContext context) } [Fact] - public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(d => d.ToString("o")); + public Task ShouldFormatDateTimes() => DoShouldFormatDateTimes(); [Fact] - public Task ShouldFormatDecimals() => DoShouldFormatDecimals(d => d.ToString("C")); + public Task ShouldFormatDecimals() => DoShouldFormatDecimals(); + + [Fact] + public Task ShouldFormatDoubles() => DoShouldFormatDoubles(); } } diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs index e4f5541fc..7384cf2a5 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringStringFormatting.cs @@ -13,8 +13,13 @@ protected WhenConfiguringStringFormatting(ITestContext context) { } - protected Task DoShouldFormatDateTimes(Func expectedDateStringFactory) + protected Task DoShouldFormatDateTimes(Func expectedDateStringFactory = null) { + if (expectedDateStringFactory == null) + { + expectedDateStringFactory = d => d.ToString("o"); + } + return RunTest(async (context, mapper) => { mapper.WhenMapping @@ -38,8 +43,13 @@ protected Task DoShouldFormatDateTimes(Func expectedDateString }); } - protected Task DoShouldFormatDecimals(Func expectedDecimalStringFactory) + protected Task DoShouldFormatDecimals(Func expectedDecimalStringFactory = null) { + if (expectedDecimalStringFactory == null) + { + expectedDecimalStringFactory = d => d.ToString("C"); + } + return RunTest(async (context, mapper) => { mapper.WhenMapping @@ -62,5 +72,35 @@ protected Task DoShouldFormatDecimals(Func expectedDecimalStrin stringDto.Value.ShouldBe(expectedDecimalStringFactory.Invoke(source.Value)); }); } + + protected Task DoShouldFormatDoubles(Func expectedDoubleStringFactory = null) + { + if (expectedDoubleStringFactory == null) + { + expectedDoubleStringFactory = d => d.ToString("0.000"); + } + + return RunTest(async (context, mapper) => + { + mapper.WhenMapping + .StringsFrom(c => c.FormatUsing("0.000")); + + var source = new PublicDouble { Value = 6778.52423 }; + var result = mapper.Map(source).ToANew(); + + result.Value.ShouldBe(source.Value.ToString("0.000")); + + await context.DoubleItems.Add(source); + await context.SaveChanges(); + + var stringDto = context + .DoubleItems + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + stringDto.Value.ShouldBe(expectedDoubleStringFactory.Invoke(source.Value)); + }); + } } } From 728c274c642282fa85c89b8a2d4f7e824ed0a4d1 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 09:15:07 +0000 Subject: [PATCH 163/176] Test coverage for decimal -> int, double -> int and double -> string projection --- .../WhenConvertingToStrings.cs | 8 ++ .../WhenConvertingToStrings.cs | 6 + .../WhenConvertingToStrings.cs | 10 +- .../WhenConvertingToInts.cs | 6 +- .../WhenConvertingToStrings.cs | 10 +- .../WhenConvertingToInts.cs | 116 +++++++++++++++++- .../WhenConvertingToStrings.cs | 44 ++++++- 7 files changed, 187 insertions(+), 13 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs index d122dd0a8..d65585a03 100644 --- a/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef5.LocalDb/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -13,6 +13,14 @@ public WhenConvertingToStrings(LocalDbTestContext context { } + [Fact] + public Task ShouldProjectADecimalToAString() + => DoShouldProjectADecimalToAString(d => d.ToString("0.00") + "0000"); + + [Fact] + public Task ShouldProjectADoubleToAString() + => DoShouldProjectADoubleToAString(d => d.ToString("0.00") + "0000"); + [Fact] public Task ShouldProjectADateTimeToAString() => DoShouldProjectADateTimeToAString(d => d.ToString("yyyy-%M-%d %H:%m:%s")); diff --git a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs index 7b4dff0ac..ebc0fb04f 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -12,6 +12,12 @@ public WhenConvertingToStrings(InMemoryEf6TestContext context) { } + [Fact] + public Task ShouldProjectADecimalToAString() => DoShouldProjectADecimalToAString(); + + [Fact] + public Task ShouldProjectADoubleToAString() => DoShouldProjectADoubleToAString(); + [Fact] public Task ShouldProjectADateTimeToAString() => DoShouldProjectADateTimeToAString(d => d.ToString("MM/dd/yyyy HH:mm:ss")); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs index 967fc4951..342a72ecc 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.SimpleTypeConversion { - using System.Globalization; using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; @@ -14,7 +13,12 @@ public WhenConvertingToStrings(InMemoryEfCore1TestContext context) } [Fact] - public Task ShouldProjectADateTimeToAString() - => DoShouldProjectADateTimeToAString(d => d.ToString(CultureInfo.CurrentCulture.DateTimeFormat)); + public Task ShouldProjectADecimalToAString() => DoShouldProjectADecimalToAString(); + + [Fact] + public Task ShouldProjectADoubleToAString() => DoShouldProjectADoubleToAString(); + + [Fact] + public Task ShouldProjectADateTimeToAString() => DoShouldProjectADateTimeToAString(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs index 15113e1ef..755e7f71d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToInts.cs @@ -16,12 +16,10 @@ public WhenConvertingToInts(InMemoryEfCore2TestContext context) } [Fact] - public Task ShouldProjectAParseableString() - => RunShouldProjectAParseableStringToAnInt(); + public Task ShouldProjectAParseableString() => RunShouldProjectAParseableStringToAnInt(); [Fact] - public Task ShouldProjectANullString() - => RunShouldProjectANullStringToAnInt(); + public Task ShouldProjectANullString() => RunShouldProjectANullStringToAnInt(); [Fact] public Task ShouldErrorProjectingAnUnparseableString() diff --git a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs index 1653b3c0d..a7be8a6be 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,6 +1,5 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.SimpleTypeConversion { - using System.Globalization; using System.Threading.Tasks; using Infrastructure; using Orms.SimpleTypeConversion; @@ -14,7 +13,12 @@ public WhenConvertingToStrings(InMemoryEfCore2TestContext context) } [Fact] - public Task ShouldProjectADateTimeToAString() - => DoShouldProjectADateTimeToAString(d => d.ToString(CultureInfo.CurrentCulture.DateTimeFormat)); + public Task ShouldProjectADecimalToAString() => DoShouldProjectADecimalToAString(); + + [Fact] + public Task ShouldProjectADoubleToAString() => DoShouldProjectADoubleToAString(); + + [Fact] + public Task ShouldProjectADateTimeToAString() => DoShouldProjectADateTimeToAString(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs index 8cf128691..2588c98c9 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToInts.cs @@ -52,7 +52,7 @@ public Task ShouldProjectATooBigLongToAnInt() var intItem = context.LongItems.Project().To().First(); - intItem.Value.ShouldBe(0); + intItem.Value.ShouldBeDefault(); }); } @@ -66,7 +66,119 @@ public Task ShouldProjectATooSmallLongToAnInt() var intItem = context.LongItems.Project().To().First(); - intItem.Value.ShouldBe(0); + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectAnInRangeDecimalToAnInt() + { + return RunTest(async context => + { + await context.DecimalItems.Add(new PublicDecimal { Value = 73872 }); + await context.SaveChanges(); + + var intItem = context.DecimalItems.Project().To().First(); + + intItem.Value.ShouldBe(73872); + }); + } + + [Fact] + public Task ShouldProjectATooBigDecimalToAnInt() + { + return RunTest(async context => + { + await context.DecimalItems.Add(new PublicDecimal { Value = decimal.MaxValue }); + await context.SaveChanges(); + + var intItem = context.DecimalItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectATooSmallDecimalToAnInt() + { + return RunTest(async context => + { + await context.DecimalItems.Add(new PublicDecimal { Value = int.MinValue - 1.0m }); + await context.SaveChanges(); + + var intItem = context.DecimalItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectANonWholeNumberDecimalToAnInt() + { + return RunTest(async context => + { + await context.DecimalItems.Add(new PublicDecimal { Value = 829.26m }); + await context.SaveChanges(); + + var intItem = context.DecimalItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectAnInRangeDoubleToAnInt() + { + return RunTest(async context => + { + await context.DoubleItems.Add(new PublicDouble { Value = 7382.00 }); + await context.SaveChanges(); + + var intItem = context.DoubleItems.Project().To().First(); + + intItem.Value.ShouldBe(7382); + }); + } + + [Fact] + public Task ShouldProjectATooBigDoubleToAnInt() + { + return RunTest(async context => + { + await context.DoubleItems.Add(new PublicDouble { Value = double.MaxValue }); + await context.SaveChanges(); + + var intItem = context.DoubleItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectATooSmallDoubleToAnInt() + { + return RunTest(async context => + { + await context.DoubleItems.Add(new PublicDouble { Value = int.MinValue - 1.00 }); + await context.SaveChanges(); + + var intItem = context.DoubleItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); + }); + } + + [Fact] + public Task ShouldProjectANonWholeNumberDoubleToAnInt() + { + return RunTest(async context => + { + await context.DoubleItems.Add(new PublicDouble { Value = 82.271 }); + await context.SaveChanges(); + + var intItem = context.DoubleItems.Project().To().First(); + + intItem.Value.ShouldBeDefault(); }); } diff --git a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs index 101242ef3..55944cfa6 100644 --- a/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs +++ b/AgileMapper.UnitTests.Orms/SimpleTypeConversion/WhenConvertingToStrings.cs @@ -1,6 +1,7 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.SimpleTypeConversion { using System; + using System.Globalization; using System.Linq; using System.Threading.Tasks; using Infrastructure; @@ -43,8 +44,49 @@ public Task ShouldProjectAnIntToAString() }); } - protected Task DoShouldProjectADateTimeToAString(Func expectedDateStringFactory) + protected Task DoShouldProjectADecimalToAString(Func expectedDecimalStringFactory = null) { + if (expectedDecimalStringFactory == null) + { + expectedDecimalStringFactory = d => d + ""; + } + + return RunTest(async context => + { + await context.DecimalItems.Add(new PublicDecimal { Value = 728.261m }); + await context.SaveChanges(); + + var stringItem = context.DecimalItems.Project().To().First(); + + stringItem.Value.ShouldBe(expectedDecimalStringFactory.Invoke(728.261m)); + }); + } + + protected Task DoShouldProjectADoubleToAString(Func expectedDoubleStringFactory = null) + { + if (expectedDoubleStringFactory == null) + { + expectedDoubleStringFactory = d => d + ""; + } + + return RunTest(async context => + { + await context.DoubleItems.Add(new PublicDouble { Value = 7212.34 }); + await context.SaveChanges(); + + var stringItem = context.DoubleItems.Project().To().First(); + + stringItem.Value.ShouldBe(expectedDoubleStringFactory.Invoke(7212.34)); + }); + } + + protected Task DoShouldProjectADateTimeToAString(Func expectedDateStringFactory = null) + { + if (expectedDateStringFactory == null) + { + expectedDateStringFactory = d => d.ToString(CultureInfo.CurrentCulture.DateTimeFormat); + } + return RunTest(async context => { var now = DateTime.Now; From 7ec8fdaff5823fa2feebc35d62f06481d3477381 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 09:23:39 +0000 Subject: [PATCH 164/176] Test coverage for projection with global exception swalling configured --- .../Configuration/WhenConfiguringCallbacks.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs index 0a5a9bb01..e13c6d480 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringCallbacks.cs @@ -1,5 +1,6 @@ namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration { + using System; using System.Threading.Tasks; using Infrastructure; using Orms.Infrastructure; @@ -215,5 +216,35 @@ public Task ShouldNotAttemptToCallAPostMemberMappingCallback() callbackCalled.ShouldBeTrue(); }); } + + [Fact] + public Task ShouldBeTolerantOfGlobalExceptionSwallowing() + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping + .SwallowAllExceptions() + .AndWhenMapping + .To() + .After + .CreatingInstances + .Call(ctx => throw new InvalidOperationException("BOOM")); + + var circle = new Circle { Diameter = 10 }; + + await context.Shapes.AddAsync(circle); + await context.SaveChangesAsync(); + + var circleVm = context + .Shapes + .ProjectUsing(mapper) + .To() + .ShouldHaveSingleItem(); + + circleVm.Diameter.ShouldBe(10); + + Should.NotThrow(() => mapper.Map(circle).ToANew()); + }); + } } } From 575b2bab3738408389661adc2f83edfe9fc944c6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 09:40:22 +0000 Subject: [PATCH 165/176] Test coverage for projection use of custom name matching patterns --- .../Infrastructure/Ef6TestDbContext.cs | 2 +- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringNameMatching.cs | 98 +++++++++++++++++++ .../Infrastructure/EfCore2TestDbContext.cs | 9 +- .../AgileMapper.UnitTests.Orms.csproj | 1 + .../TestClasses/PublicStringNames.cs | 18 ++++ .../WhenConfiguringNameMatching.cs | 59 ++++------- 7 files changed, 146 insertions(+), 42 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringNameMatching.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/PublicStringNames.cs diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index fb56b0603..73edece5e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -115,7 +115,7 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.DoubleItems => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.DateTimeItems => new Ef6DbSetWrapper(this); - + IDbSetWrapper ITestDbContext.StringItems => new Ef6DbSetWrapper(this); IDbSetWrapper ITestDbContext.TitleItems => new Ef6DbSetWrapper(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index fb8f6d65c..b731af92d 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -154,6 +154,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringNameMatching.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringNameMatching.cs new file mode 100644 index 000000000..2e76ebba9 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/WhenConfiguringNameMatching.cs @@ -0,0 +1,98 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration +{ + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenConfiguringNameMatching : OrmTestClassBase + { + public WhenConfiguringNameMatching(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldUseACustomPrefix() + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping.UseNamePrefix("_"); + + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To() + .FirstAsync(); + + stringDto.Value.ShouldBe("123"); + }); + } + + [Fact] + public Task ShouldUseACustomSuffix() + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping.UseNameSuffix("_"); + + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To() + .FirstAsync(); + + stringDto.Value.ShouldBe("789"); + }); + } + + [Fact] + public Task ShouldUseACustomNamingPattern() + { + return RunTest(async (context, mapper) => + { + mapper.WhenMapping.UseNamePattern("^_(.+)_$"); + + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To() + .FirstAsync(); + + stringDto.Value.ShouldBe("456"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 29421b6ac..22caef417 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -67,6 +67,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet StringItems { get; set; } + public DbSet StringNameItems { get; set; } + public DbSet TitleItems { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -135,7 +137,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.TitleItems => new EfCore2DbSetWrapper(this); - Task ITestDbContext.SaveChanges() => SaveChangesAsync(); + Task ITestDbContext.SaveChanges() + { + StringNameItems.RemoveRange(StringNameItems); + + return SaveChangesAsync(); + } #endregion } diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 0ada5acc2..e36357733 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -135,6 +135,7 @@ + diff --git a/AgileMapper.UnitTests.Orms/TestClasses/PublicStringNames.cs b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringNames.cs new file mode 100644 index 000000000..355512c52 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/PublicStringNames.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.ComponentModel.DataAnnotations; + + // ReSharper disable InconsistentNaming + public class PublicStringNames + { + [Key] + public int Id { get; set; } + + public string _Value { get; set; } + + public string _Value_ { get; set; } + + public string Value_ { get; set; } + } + // ReSharper restore InconsistentNaming +} \ No newline at end of file diff --git a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs index 5ed794ebf..9670c77db 100644 --- a/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs +++ b/AgileMapper.UnitTests/Configuration/WhenConfiguringNameMatching.cs @@ -10,13 +10,11 @@ public class WhenConfiguringNameMatching { [Fact] - public void ShouldHandleACustomPrefix() + public void ShouldUseACustomPrefix() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePrefix("_p"); + mapper.WhenMapping.UseNamePrefix("_p"); var source = new { _pValue = "Help!" }; var result = mapper.Map(source).ToANew>(); @@ -26,13 +24,11 @@ public void ShouldHandleACustomPrefix() } [Fact] - public void ShouldHandleMultipleCustomPrefixes() + public void ShouldUseMultipleCustomPrefixes() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePrefixes("_p", "_f"); + mapper.WhenMapping.UseNamePrefixes("_p", "_f"); var source = new { _fValue = "Oops!" }; var result = mapper.Map(source).ToANew>(); @@ -42,13 +38,11 @@ public void ShouldHandleMultipleCustomPrefixes() } [Fact] - public void ShouldHandleACustomSuffix() + public void ShouldUseACustomSuffix() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNameSuffix("Str"); + mapper.WhenMapping.UseNameSuffix("Str"); var source = new { ValueStr = "La la la!" }; var result = mapper.Map(source).ToANew>(); @@ -58,13 +52,11 @@ public void ShouldHandleACustomSuffix() } [Fact] - public void ShouldHandleMultipleCustomSuffixes() + public void ShouldUseMultipleCustomSuffixes() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNameSuffixes("Str", "Int"); + mapper.WhenMapping.UseNameSuffixes("Str", "Int"); var source = new { ValueInt = 12345 }; var result = mapper.Map(source).ToANew>(); @@ -74,7 +66,7 @@ public void ShouldHandleMultipleCustomSuffixes() } [Fact] - public void ShouldHandleACustomNamingPattern() + public void ShouldUseACustomNamingPattern() { using (var mapper = Mapper.CreateNew()) { @@ -109,13 +101,11 @@ public void ShouldUseACustomNamingPatternInIdentifierMatching() } [Fact] - public void ShouldHandleACustomNamingPrefixPattern() + public void ShouldUseACustomNamingPrefixPattern() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePattern("^__(.+)$"); + mapper.WhenMapping.UseNamePattern("^__(.+)$"); var source = new { __Value = 911 }; var result = mapper.Map(source).ToANew>(); @@ -125,13 +115,11 @@ public void ShouldHandleACustomNamingPrefixPattern() } [Fact] - public void ShouldHandleACustomNamingSuffixPattern() + public void ShouldUseACustomNamingSuffixPattern() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePattern("^(.+)__$"); + mapper.WhenMapping.UseNamePattern("^(.+)__$"); var source = new { Value__ = 878 }; var result = mapper.Map(source).ToANew>(); @@ -141,13 +129,11 @@ public void ShouldHandleACustomNamingSuffixPattern() } [Fact] - public void ShouldHandleCustomNamingPatterns() + public void ShouldUseCustomNamingPatterns() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePatterns("^_abc(.+)xyz_$", "^__(.+)__$"); + mapper.WhenMapping.UseNamePatterns("^_abc(.+)xyz_$", "^__(.+)__$"); var source = new { __Value__ = 456 }; var result = mapper.Map(source).ToANew>(); @@ -163,9 +149,7 @@ public void ShouldErrorIfInvalidNamePatternFormatSpecified() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePatterns("^_[Name]_$"); + mapper.WhenMapping.UseNamePatterns("^_[Name]_$"); } }); } @@ -193,8 +177,7 @@ public void ShouldErrorIfNamePatternIsNull() { using (var mapper = Mapper.CreateNew()) { - mapper.WhenMapping - .UseNamePattern(null); + mapper.WhenMapping.UseNamePattern(null); } }); } @@ -206,9 +189,7 @@ public void ShouldErrorIfNoPatternsSupplied() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePatterns(); + mapper.WhenMapping.UseNamePatterns(); } }); } @@ -220,9 +201,7 @@ public void ShouldErrorIfPatternHasNoPrefixOrSuffix() { using (var mapper = Mapper.CreateNew()) { - mapper - .WhenMapping - .UseNamePattern("(.+)"); + mapper.WhenMapping.UseNamePattern("(.+)"); } }); } From 46f2ec5629eec96251c542fd112b1999d752f899 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 09:53:49 +0000 Subject: [PATCH 166/176] Test coverage for inline-configured custom constructor data sources --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 20 +++++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 20 +++++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 20 +++++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 20 +++++++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 43 +++++++++++++++++++ 10 files changed, 128 insertions(+) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 06bdf9e89..ec854ac44 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -85,6 +85,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs new file mode 100644 index 000000000..389e230e1 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration.Inline; + using Xunit; + + public class WhenConfiguringConstructorDataSourcesInline + : WhenConfiguringConstructorDataSourcesInline + { + public WhenConfiguringConstructorDataSourcesInline(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorApplyingAConfiguredConstantByParameterTypeInline() + => RunShouldErrorApplyingAConfiguredConstantByParameterTypeInline(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 645d51773..29363ef1c 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,6 +87,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs new file mode 100644 index 000000000..29000c136 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration.Inline; + using Xunit; + + public class WhenConfiguringConstructorDataSourcesInline + : WhenConfiguringConstructorDataSourcesInline + { + public WhenConfiguringConstructorDataSourcesInline(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorApplyingAConfiguredConstantByParameterTypeInline() + => RunShouldErrorApplyingAConfiguredConstantByParameterTypeInline(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index d13da51cb..341d429c2 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -213,6 +213,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs new file mode 100644 index 000000000..47d3aeb1a --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration.Inline; + using Xunit; + + public class WhenConfiguringConstructorDataSourcesInline + : WhenConfiguringConstructorDataSourcesInline + { + public WhenConfiguringConstructorDataSourcesInline(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstantByParameterTypeInline() + => RunShouldApplyAConfiguredConstantByParameterTypeInline(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index b731af92d..b6f0a1eee 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -149,6 +149,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs new file mode 100644 index 000000000..688265a84 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -0,0 +1,20 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Orms.Configuration.Inline; + using Xunit; + + public class WhenConfiguringConstructorDataSourcesInline + : WhenConfiguringConstructorDataSourcesInline + { + public WhenConfiguringConstructorDataSourcesInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstantByParameterTypeInline() + => RunShouldApplyAConfiguredConstantByParameterTypeInline(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index e36357733..b5566848f 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,6 +73,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs new file mode 100644 index 000000000..d7f27f43c --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + + public abstract class WhenConfiguringConstructorDataSourcesInline : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringConstructorDataSourcesInline(ITestContext context) + : base(context) + { + } + + #region Project -> Ctor Parameter by Type Inline + + protected Task RunShouldApplyAConfiguredConstantByParameterTypeInline() + => RunTest(DoShouldApplyAConfiguredConstantByParameterType); + + protected Task RunShouldErrorApplyingAConfiguredConstantByParameterTypeInline() + => RunTestAndExpectThrow(DoShouldApplyAConfiguredConstantByParameterType); + + private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context, IMapper mapper) + { + var product = new Product { Name = "Prod.1" }; + + await context.Products.Add(product); + await context.SaveChanges(); + + var productDto = context + .Products + .ProjectUsing(mapper).To(cfg => cfg + .Map("GRAPES!") + .ToCtor()) + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("GRAPES!"); + } + + #endregion + } +} From 4db27e4c8a1342c16c2136d3a0e6dcdfe97a26ff Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 10:22:29 +0000 Subject: [PATCH 167/176] Test coverage for inline member data source configuration --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 1 + .../WhenConfiguringDataSourcesInline.cs | 14 +++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 1 + .../WhenConfiguringDataSourcesInline.cs | 14 +++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 1 + .../WhenConfiguringDataSourcesInline.cs | 14 +++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../WhenConfiguringDataSourcesInline.cs | 14 +++++++ .../AgileMapper.UnitTests.Orms.csproj | 1 + ...ConfiguringConstructorDataSourcesInline.cs | 4 +- .../WhenConfiguringDataSourcesInline.cs | 38 +++++++++++++++++++ 11 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDataSourcesInline.cs create mode 100644 AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index ec854ac44..6cb0c722b 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -86,6 +86,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringDataSourcesInline.cs new file mode 100644 index 000000000..8e243be65 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Configuration.Inline +{ + using Infrastructure; + using Orms.Configuration.Inline; + + public class WhenConfiguringDataSourcesInline + : WhenConfiguringDataSourcesInline + { + public WhenConfiguringDataSourcesInline(InMemoryEf5TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index 29363ef1c..c65bd5447 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -87,6 +87,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringDataSourcesInline.cs new file mode 100644 index 000000000..3985148d7 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Configuration.Inline +{ + using Infrastructure; + using Orms.Configuration.Inline; + + public class WhenConfiguringDataSourcesInline + : WhenConfiguringDataSourcesInline + { + public WhenConfiguringDataSourcesInline(InMemoryEf6TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 341d429c2..06e481e9e 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -214,6 +214,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringDataSourcesInline.cs new file mode 100644 index 000000000..9c0beb6db --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Configuration.Inline +{ + using Infrastructure; + using Orms.Configuration.Inline; + + public class WhenConfiguringDataSourcesInline + : WhenConfiguringDataSourcesInline + { + public WhenConfiguringDataSourcesInline(InMemoryEfCore1TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index b6f0a1eee..d799ff838 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -149,6 +149,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDataSourcesInline.cs new file mode 100644 index 000000000..e6e05c2d4 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -0,0 +1,14 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using Infrastructure; + using Orms.Configuration.Inline; + + public class WhenConfiguringDataSourcesInline + : WhenConfiguringDataSourcesInline + { + public WhenConfiguringDataSourcesInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index b5566848f..18c6886dc 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -73,6 +73,7 @@ Properties\VersionInfo.cs + diff --git a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs index d7f27f43c..15f222dd8 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringConstructorDataSourcesInline.cs @@ -20,7 +20,7 @@ protected Task RunShouldApplyAConfiguredConstantByParameterTypeInline() protected Task RunShouldErrorApplyingAConfiguredConstantByParameterTypeInline() => RunTestAndExpectThrow(DoShouldApplyAConfiguredConstantByParameterType); - private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context, IMapper mapper) + private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmContext context) { var product = new Product { Name = "Prod.1" }; @@ -29,7 +29,7 @@ private static async Task DoShouldApplyAConfiguredConstantByParameterType(TOrmCo var productDto = context .Products - .ProjectUsing(mapper).To(cfg => cfg + .Project().To(cfg => cfg .Map("GRAPES!") .ToCtor()) .ShouldHaveSingleItem(); diff --git a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs new file mode 100644 index 000000000..73e933171 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -0,0 +1,38 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using TestClasses; + using Xunit; + + public abstract class WhenConfiguringDataSourcesInline : OrmTestClassBase + where TOrmContext : ITestDbContext, new() + { + protected WhenConfiguringDataSourcesInline(ITestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAConfiguredConstantInline() + { + return RunTest(async context => + { + var product = new Product { Name = "P1" }; + + await context.Products.Add(product); + await context.SaveChanges(); + + var productDto = context + .Products + .Project().To(cfg => cfg + .Map("PROD!!") + .To(dto => dto.Name)) + .ShouldHaveSingleItem(); + + productDto.ProductId.ShouldBe(product.ProductId); + productDto.Name.ShouldBe("PROD!!"); + }); + } + } +} From 66cb17c7dde3c5f38f889ee05ff3e07f621b74d0 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 12:25:48 +0000 Subject: [PATCH 168/176] Extending test coverage for inline data source configuration / Support for switching configuration types inline in projections --- .../WhenConfiguringDataSourcesInline.cs | 32 +++++++++++++++++++ .../IFullProjectionInlineConfigurator.cs | 6 ++++ 2 files changed, 38 insertions(+) diff --git a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs index 73e933171..e9de614f5 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/Inline/WhenConfiguringDataSourcesInline.cs @@ -34,5 +34,37 @@ public Task ShouldApplyAConfiguredConstantInline() productDto.Name.ShouldBe("PROD!!"); }); } + + [Fact] + public Task ShouldApplyAConfiguredConstantToANestedMemberInline() + { + return RunTest(async context => + { + var person = new Person + { + Name = "Person One", + Address = new Address { Line1 = "Line One", Postcode = "Postcode" } + }; + + await context.Persons.Add(person); + await context.SaveChanges(); + + var personDto = context + .Persons + .Project().To(cfg => cfg + .WhenMapping + .ProjectionsTo() + .Map("LINE TWO?!") + .To(a => a.Line2)) + .ShouldHaveSingleItem(); + + personDto.Id.ShouldBe(person.PersonId); + personDto.Name.ShouldBe("Person One"); + personDto.Address.ShouldNotBeNull(); + personDto.Address.Line1.ShouldBe("Line One"); + personDto.Address.Line2.ShouldBe("LINE TWO?!"); + personDto.Address.Postcode.ShouldBe("Postcode"); + }); + } } } diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index eb10baa4a..eea57fcb5 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -8,5 +8,11 @@ public interface IFullProjectionInlineConfigurator : IFullProjectionConfigurator { + /// + /// Configure how this mapper performs a projection, inline. Use this property to switch from + /// configuration of the root Types on which the projection is being performed to configuration + /// of any other Types. + /// + MappingConfigStartingPoint WhenMapping { get; } } } From f2ce5f9c3d3c025c6c8ec6a126be10a28ee8f402 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 13:16:34 +0000 Subject: [PATCH 169/176] Test coverage + support for inline-validated projections --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 1 + .../Inline/WhenValidatingProjectionsInline.cs | 63 +++++++++++++++++++ AgileMapper.UnitTests/Should.cs | 9 ++- .../IFullMappingInlineConfigurator.cs | 5 +- .../MappingConfigStartingPoint.cs | 4 +- .../IFullProjectionInlineConfigurator.cs | 9 +++ 6 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenValidatingProjectionsInline.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index d799ff838..680ec1ce1 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -151,6 +151,7 @@ + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenValidatingProjectionsInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenValidatingProjectionsInline.cs new file mode 100644 index 000000000..7fa69dd0d --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenValidatingProjectionsInline.cs @@ -0,0 +1,63 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Validation; + using Xunit; + + public class WhenValidatingProjectionsInline : OrmTestClassBase + { + public WhenValidatingProjectionsInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldSupportCachedProjectionValidationInline() + { + return RunTest(async context => + { + var address = new Address { Line1 = "1" }; + + await context.Addresses.AddAsync(address); + await context.SaveChangesAsync(); + + Should.NotThrow(async () => + { + var addressDto = await context + .Addresses + .Project().To(cfg => cfg + .ThrowNowIfMappingPlanIsIncomplete()) + .FirstAsync(); + + addressDto.Line1.ShouldBe("1"); + }); + }); + } + + [Fact] + public Task ShouldErrorIfCachedProjectionTargetTypeIsUnconstructableInline() + { + return RunTest(async (context, mapper) => + { + var validationEx = await Should.ThrowAsync(async () => + { + await context + .Addresses + .ProjectUsing(mapper) + .To(cfg => cfg + .ThrowNowIfMappingPlanIsIncomplete()) + .FirstOrDefaultAsync(); + }); + + validationEx.Message.ShouldContain("IQueryable
-> IQueryable"); + validationEx.Message.ShouldContain("Rule set: Project"); + validationEx.Message.ShouldContain("Unconstructable target Types"); + validationEx.Message.ShouldContain("Address -> ProductStruct"); + }); + } + } +} diff --git a/AgileMapper.UnitTests/Should.cs b/AgileMapper.UnitTests/Should.cs index 4f363c113..26c900af5 100644 --- a/AgileMapper.UnitTests/Should.cs +++ b/AgileMapper.UnitTests/Should.cs @@ -31,18 +31,21 @@ public static TException Throw(Func testFunc) throw new Exception("Expected exception of type " + typeof(TException).Name); } - public static async Task ThrowAsync(Func test) + public static Task ThrowAsync(Func test) => ThrowAsync(test); + + public static async Task ThrowAsync(Func test) + where TException : Exception { try { await test.Invoke(); } - catch (Exception ex) + catch (TException ex) { return ex; } - throw new Exception("Expected exception"); + throw new Exception("Expected exception of type " + typeof(TException).Name); } public static void NotThrow(Action testAction) => NotThrow(testAction); diff --git a/AgileMapper/Api/Configuration/IFullMappingInlineConfigurator.cs b/AgileMapper/Api/Configuration/IFullMappingInlineConfigurator.cs index 946460c8b..caf84b2c9 100644 --- a/AgileMapper/Api/Configuration/IFullMappingInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/IFullMappingInlineConfigurator.cs @@ -32,8 +32,9 @@ public interface IFullMappingInlineConfigurator : IFullMapping /// /// Throw an exception upon execution of this statement if the mapping being configured has any target members - /// which will not be mapped, or maps from a source enum to a target enum which does not support all of its - /// values. Use calls to this method to validate a mapping plan; remove them in production code. + /// which will not be mapped, maps from a source enum to a target enum which does not support all of its values, + /// or includes complex types which cannot be constructed. Use calls to this method to validate a mapping plan; + /// remove them in production code. /// void ThrowNowIfMappingPlanIsIncomplete(); diff --git a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs index 0c3c97ae9..cbe06a58e 100644 --- a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs +++ b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs @@ -190,8 +190,8 @@ public IGlobalConfigSettings MapNullCollectionsToNull() /// /// Throw an exception upon creation of a mapper if the mapping plan has any target members which will not be mapped, - /// or maps from a source enum to a target enum which does not support all of its values. Call this method to validate - /// mapping plans during development; remove it in production code. + /// maps from a source enum to a target enum which does not support all of its values, or includes complex types which + /// cannot be constructed. Call this method to validate mapping plans during development; remove it in production code. /// /// /// This with which to globally configure other mapping aspects. diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index eea57fcb5..6dc24e054 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -14,5 +14,14 @@ public interface IFullProjectionInlineConfigurator MappingConfigStartingPoint WhenMapping { get; } + + /// + /// Throw an exception upon execution of this statement if the projection being configured has any result + /// members which will not be mapped, projects from a source enum to a target enum which does not support + /// all of its values, or includes complex types which cannot be constructed. Use calls to this method to + /// validate a mapping plan; remove them in production + /// code. + /// + void ThrowNowIfMappingPlanIsIncomplete(); } } From 3c1b7b8a205a73019feebd6d33d6f86a446935b4 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 14:03:29 +0000 Subject: [PATCH 170/176] Test coverage and support for 'continued' inline configuration / Test coverage for inline configuration of decimal -> string formatting --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 3 + .../WhenConfiguringEnumMappingInline.cs | 67 +++++++++++++++ .../WhenConfiguringStringFormattingInline.cs | 44 ++++++++++ .../Inline/WhenIgnoringMembersInline.cs | 43 ++++++++++ .../Configuration/WhenIgnoringMembers.cs | 8 +- ...gSettings.cs => IGlobalMappingSettings.cs} | 46 +++++----- .../MappingConfigStartingPoint.cs | 86 +++++++++++-------- .../Api/Configuration/MappingConfigurator.cs | 3 + .../IFullProjectionInlineConfigurator.cs | 2 +- .../Projection/IGlobalProjectionSettings.cs | 13 +++ .../IProjectionConfigStartingPoint.cs | 42 +++++++++ .../Projection/IProjectionResultSelector.cs | 18 ++++ .../Api/Configuration/TargetSpecifier.cs | 2 +- 13 files changed, 314 insertions(+), 63 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringEnumMappingInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringStringFormattingInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenIgnoringMembersInline.cs rename AgileMapper/Api/Configuration/{IGlobalConfigSettings.cs => IGlobalMappingSettings.cs} (73%) create mode 100644 AgileMapper/Api/Configuration/Projection/IGlobalProjectionSettings.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionConfigStartingPoint.cs create mode 100644 AgileMapper/Api/Configuration/Projection/IProjectionResultSelector.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 680ec1ce1..70c9493f6 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -151,6 +151,9 @@ + + + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringEnumMappingInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringEnumMappingInline.cs new file mode 100644 index 000000000..effcb7689 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringEnumMappingInline.cs @@ -0,0 +1,67 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + using PaymentTypeUk = UnitTests.TestClasses.PaymentTypeUk; + using PaymentTypeUs = UnitTests.TestClasses.PaymentTypeUs; + + public class WhenConfiguringEnumMappingInline : OrmTestClassBase + { + public WhenConfiguringEnumMappingInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldPairEnumMembersInline() + { + return RunTest(async (context, mapper) => + { + var order1 = new OrderUk + { + DatePlaced = DateTime.Now, + PaymentType = PaymentTypeUk.Cheque + }; + + var order2 = new OrderUk + { + DatePlaced = DateTime.Now.AddMinutes(1), + PaymentType = PaymentTypeUk.Cash + }; + + var order3 = new OrderUk + { + DatePlaced = DateTime.Now.AddMinutes(2), + PaymentType = PaymentTypeUk.Card + }; + + await context.Orders.AddRangeAsync(order1, order2, order3); + await context.SaveChangesAsync(); + + var orderVms = await context + .Orders + .ProjectUsing(mapper).To(cfg => cfg + .PairEnum(PaymentTypeUk.Cheque).With(PaymentTypeUs.Check)) + .OrderByDescending(o => o.DatePlaced) + .ToArrayAsync(); + + orderVms.Length.ShouldBe(3); + + orderVms.First().DatePlaced.ShouldBe(order3.DatePlaced); + orderVms.First().PaymentType.ShouldBe(PaymentTypeUs.Card); + + orderVms.Second().DatePlaced.ShouldBe(order2.DatePlaced); + orderVms.Second().PaymentType.ShouldBe(PaymentTypeUs.Cash); + + orderVms.Third().DatePlaced.ShouldBe(order1.DatePlaced); + orderVms.Third().PaymentType.ShouldBe(PaymentTypeUs.Check); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringStringFormattingInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringStringFormattingInline.cs new file mode 100644 index 000000000..a1213cd3c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringStringFormattingInline.cs @@ -0,0 +1,44 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenConfiguringStringFormattingInline : OrmTestClassBase + { + public WhenConfiguringStringFormattingInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldFormatDecimalsInline() + { + return RunTest(async (context, mapper) => + { + var source = new PublicDecimal { Value = 123.00m }; + + await context.DecimalItems.AddAsync(source); + await context.SaveChangesAsync(); + + var stringDto = await context + .DecimalItems + .ProjectUsing(mapper) + .To(cfg => cfg + .WhenMapping + .StringsFrom(c => c.FormatUsing("C")) + .AndWhenMapping + .From() + .ProjectedTo() + .Map(d => d.Value * 2) + .To(dto => dto.Value)) + .SingleAsync(); + + stringDto.Value.ShouldBe(246.00m.ToString("C")); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenIgnoringMembersInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenIgnoringMembersInline.cs new file mode 100644 index 000000000..f22d8d7f1 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenIgnoringMembersInline.cs @@ -0,0 +1,43 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenIgnoringMembersInline : OrmTestClassBase + { + public WhenIgnoringMembersInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldIgnoreAConfiguredMemberConditionallyInline() + { + return RunTest(async (context, mapper) => + { + var product1 = new Product { Name = "1" }; + var product2 = new Product { Name = "P.2" }; + + await context.Products.AddRangeAsync(product1, product2); + await context.SaveChangesAsync(); + + var productDtos = context + .Products + .ProjectUsing(mapper).To(cfg => cfg + .If(p => p.Name.Length < 2) + .Ignore(p => p.Name)) + .OrderBy(p => p.ProductId) + .ToArray(); + + productDtos.First().ProductId.ShouldBe(product1.ProductId); + productDtos.First().Name.ShouldBeNull(); + productDtos.Second().ProductId.ShouldBe(product2.ProductId); + productDtos.Second().Name.ShouldBe("P.2"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs index 74d7d0ed8..611c8114c 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenIgnoringMembers.cs @@ -15,7 +15,7 @@ protected WhenIgnoringMembers(ITestContext context) } [Fact] - public Task DoShouldIgnoreAConfiguredMember() + public Task ShouldIgnoreAConfiguredMember() { return RunTest(async (context, mapper) => { @@ -40,7 +40,7 @@ public Task DoShouldIgnoreAConfiguredMember() } [Fact] - public Task DoShouldIgnoreAConfiguredMemberConditionally() + public Task ShouldIgnoreAConfiguredMemberConditionally() { return RunTest(async (context, mapper) => { @@ -70,7 +70,7 @@ public Task DoShouldIgnoreAConfiguredMemberConditionally() } [Fact] - public Task DoShouldIgnoreMembersByTypeAndTargetType() + public Task ShouldIgnoreMembersByTypeAndTargetType() { return RunTest(async (context, mapper) => { @@ -105,7 +105,7 @@ public Task DoShouldIgnoreMembersByTypeAndTargetType() } [Fact] - public Task DoShouldIgnorePropertiesByPropertyInfoMatcher() + public Task ShouldIgnorePropertiesByPropertyInfoMatcher() { return RunTest(async (context, mapper) => { diff --git a/AgileMapper/Api/Configuration/IGlobalConfigSettings.cs b/AgileMapper/Api/Configuration/IGlobalMappingSettings.cs similarity index 73% rename from AgileMapper/Api/Configuration/IGlobalConfigSettings.cs rename to AgileMapper/Api/Configuration/IGlobalMappingSettings.cs index 41e4aa994..f9b0377dd 100644 --- a/AgileMapper/Api/Configuration/IGlobalConfigSettings.cs +++ b/AgileMapper/Api/Configuration/IGlobalMappingSettings.cs @@ -6,7 +6,7 @@ /// /// Provides options for globally configuring how all mappers will perform mappings. /// - public interface IGlobalConfigSettings + public interface IGlobalMappingSettings { #region Exception Handling @@ -15,9 +15,9 @@ public interface IGlobalConfigSettings /// encounter an Exception will return null. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings SwallowAllExceptions(); + IGlobalMappingSettings SwallowAllExceptions(); /// /// Pass Exceptions thrown during a mapping to the given instead of throwing @@ -28,9 +28,9 @@ public interface IGlobalConfigSettings /// swallowed, it should be rethrown inside the callback. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings PassExceptionsTo(Action callback); + IGlobalMappingSettings PassExceptionsTo(Action callback); #endregion @@ -42,9 +42,9 @@ public interface IGlobalConfigSettings /// /// The prefix to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNamePrefix(string prefix); + IGlobalMappingSettings UseNamePrefix(string prefix); /// /// Expect members of all source and target types to potentially have any of the given name . @@ -52,9 +52,9 @@ public interface IGlobalConfigSettings /// /// The prefixes to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNamePrefixes(params string[] prefixes); + IGlobalMappingSettings UseNamePrefixes(params string[] prefixes); /// /// Expect members of all source and target types to potentially have the given name . @@ -62,9 +62,9 @@ public interface IGlobalConfigSettings /// /// The suffix to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNameSuffix(string suffix); + IGlobalMappingSettings UseNameSuffix(string suffix); /// /// Expect members of all source and target types to potentially have any of the given name . @@ -72,9 +72,9 @@ public interface IGlobalConfigSettings /// /// The suffixes to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNameSuffixes(params string[] suffixes); + IGlobalMappingSettings UseNameSuffixes(params string[] suffixes); /// /// Expect members of all source and target types to potentially match the given name . @@ -85,9 +85,9 @@ public interface IGlobalConfigSettings /// ^ character, end with the $ character and contain a single capturing group wrapped in parentheses, e.g. ^__(.+)__$ /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNamePattern(string pattern); + IGlobalMappingSettings UseNamePattern(string pattern); /// /// Expect members of all source and target types to potentially match the given name . @@ -98,9 +98,9 @@ public interface IGlobalConfigSettings /// ^ character, end with the $ character and contain a single capturing group wrapped in parentheses, e.g. ^__(.+)__$ /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings UseNamePatterns(params string[] patterns); + IGlobalMappingSettings UseNamePatterns(params string[] patterns); #endregion @@ -111,9 +111,9 @@ public interface IGlobalConfigSettings /// this option is not necessary just to map circular relationships. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings MaintainIdentityIntegrity(); + IGlobalMappingSettings MaintainIdentityIntegrity(); /// /// Disable tracking of objects during circular relationship mapping between all source and target types. @@ -123,17 +123,17 @@ public interface IGlobalConfigSettings /// only once, disabling object tracking will increase mapping performance. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings DisableObjectTracking(); + IGlobalMappingSettings DisableObjectTracking(); /// /// Map null source collections to null instead of an empty collection, for all source and target types. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - IGlobalConfigSettings MapNullCollectionsToNull(); + IGlobalMappingSettings MapNullCollectionsToNull(); /// /// Gets a link back to the full , for api fluency. diff --git a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs index cbe06a58e..a5549fc04 100644 --- a/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs +++ b/AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs @@ -18,7 +18,10 @@ /// /// Provides options for configuring how a mapper performs a mapping. /// - public class MappingConfigStartingPoint : IGlobalConfigSettings + public class MappingConfigStartingPoint : + IGlobalMappingSettings, + IGlobalProjectionSettings, + IProjectionConfigStartingPoint { private readonly MappingConfigInfo _configInfo; @@ -38,9 +41,9 @@ internal MappingConfigStartingPoint(MapperContext mapperContext) /// encounter an Exception will return null. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings SwallowAllExceptions() => PassExceptionsTo(ctx => { }); + public IGlobalMappingSettings SwallowAllExceptions() => PassExceptionsTo(ctx => { }); /// /// Pass Exceptions thrown during a mapping to the given instead of throwing @@ -51,9 +54,9 @@ internal MappingConfigStartingPoint(MapperContext mapperContext) /// swallowed, it should be rethrown inside the callback. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings PassExceptionsTo(Action callback) + public IGlobalMappingSettings PassExceptionsTo(Action callback) { var exceptionCallback = new ExceptionCallback(GlobalConfigInfo, callback.ToConstantExpression()); @@ -71,9 +74,9 @@ public IGlobalConfigSettings PassExceptionsTo(Action call /// /// The prefix to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNamePrefix(string prefix) => UseNamePrefixes(prefix); + public IGlobalMappingSettings UseNamePrefix(string prefix) => UseNamePrefixes(prefix); /// /// Expect members of all source and target types to potentially have any of the given name . @@ -81,9 +84,9 @@ public IGlobalConfigSettings PassExceptionsTo(Action call /// /// The prefixes to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNamePrefixes(params string[] prefixes) + public IGlobalMappingSettings UseNamePrefixes(params string[] prefixes) { MapperContext.Naming.AddNamePrefixes(prefixes); return this; @@ -95,9 +98,9 @@ public IGlobalConfigSettings UseNamePrefixes(params string[] prefixes) /// /// The suffix to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNameSuffix(string suffix) => UseNameSuffixes(suffix); + public IGlobalMappingSettings UseNameSuffix(string suffix) => UseNameSuffixes(suffix); /// /// Expect members of all source and target types to potentially have any of the given name . @@ -105,9 +108,9 @@ public IGlobalConfigSettings UseNamePrefixes(params string[] prefixes) /// /// The suffixes to ignore when matching source and target members. /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNameSuffixes(params string[] suffixes) + public IGlobalMappingSettings UseNameSuffixes(params string[] suffixes) { MapperContext.Naming.AddNameSuffixes(suffixes); return this; @@ -122,9 +125,9 @@ public IGlobalConfigSettings UseNameSuffixes(params string[] suffixes) /// ^ character, end with the $ character and contain a single capturing group wrapped in parentheses, e.g. ^__(.+)__$ /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNamePattern(string pattern) => UseNamePatterns(pattern); + public IGlobalMappingSettings UseNamePattern(string pattern) => UseNamePatterns(pattern); /// /// Expect members of all source and target types to potentially match the given name . @@ -135,9 +138,9 @@ public IGlobalConfigSettings UseNameSuffixes(params string[] suffixes) /// ^ character, end with the $ character and contain a single capturing group wrapped in parentheses, e.g. ^__(.+)__$ /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings UseNamePatterns(params string[] patterns) + public IGlobalMappingSettings UseNamePatterns(params string[] patterns) { MapperContext.Naming.AddNameMatchers(patterns); return this; @@ -152,9 +155,9 @@ public IGlobalConfigSettings UseNamePatterns(params string[] patterns) /// this option is not necessary when mapping circular relationships. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings MaintainIdentityIntegrity() + public IGlobalMappingSettings MaintainIdentityIntegrity() { MapperContext.UserConfigurations.Add(MappedObjectCachingSettings.CacheAll); return this; @@ -168,9 +171,9 @@ public IGlobalConfigSettings MaintainIdentityIntegrity() /// only once, disabling object tracking will increase mapping performance. /// /// - /// An with which to globally configure other mapping aspects. + /// An with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings DisableObjectTracking() + public IGlobalMappingSettings DisableObjectTracking() { MapperContext.UserConfigurations.Add(MappedObjectCachingSettings.CacheNone); return this; @@ -180,9 +183,9 @@ public IGlobalConfigSettings DisableObjectTracking() /// Map null source collections to null instead of an empty collection, for all source and target types. /// /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings MapNullCollectionsToNull() + public IGlobalMappingSettings MapNullCollectionsToNull() { MapperContext.UserConfigurations.Add(new NullCollectionsSetting(GlobalConfigInfo)); return this; @@ -194,9 +197,9 @@ public IGlobalConfigSettings MapNullCollectionsToNull() /// cannot be constructed. Call this method to validate mapping plans during development; remove it in production code. /// /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings ThrowIfAnyMappingPlanIsIncomplete() + public IGlobalMappingSettings ThrowIfAnyMappingPlanIsIncomplete() { MapperContext.UserConfigurations.ValidateMappingPlans = true; return this; @@ -242,9 +245,9 @@ public IMappingEnumPairSpecifier PairEnums(params /// /// The assemblies in which to look for derived types. /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings LookForDerivedTypesIn(params Assembly[] assemblies) + public IGlobalMappingSettings LookForDerivedTypesIn(params Assembly[] assemblies) { SetDerivedTypeAssemblies(assemblies); return this; @@ -277,9 +280,9 @@ internal static void SetDerivedTypeAssemblies(Assembly[] assemblies) /// /// The Type of target member to ignore. /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings IgnoreTargetMembersOfType() + public IGlobalMappingSettings IgnoreTargetMembersOfType() { return IgnoreTargetMembersWhere(member => member.HasType()); } @@ -290,9 +293,9 @@ public IGlobalConfigSettings IgnoreTargetMembersOfType() /// /// The matching function with which to select target members to ignore. /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings IgnoreTargetMembersWhere(Expression> memberFilter) + public IGlobalMappingSettings IgnoreTargetMembersWhere(Expression> memberFilter) { var configuredIgnoredMember = new ConfiguredIgnoredMember(GlobalConfigInfo, memberFilter); @@ -309,9 +312,19 @@ public IGlobalConfigSettings IgnoreTargetMembersWhere(ExpressionThe source value type to which to apply a formatting string. /// An action which supplies the formatting string. /// - /// This with which to globally configure other mapping aspects. + /// This with which to globally configure other mapping aspects. /// - public IGlobalConfigSettings StringsFrom(Action formatSelector) + public IGlobalMappingSettings StringsFrom(Action formatSelector) + => RegisterStringFormatter(formatSelector); + + IGlobalProjectionSettings IProjectionConfigStartingPoint.StringsFrom( + Action formatSelector) + { + return RegisterStringFormatter(formatSelector); + } + + private MappingConfigStartingPoint RegisterStringFormatter( + Action formatSelector) { var formatSpecifier = new StringFormatSpecifier(MapperContext, typeof(TSourceValue)); @@ -322,7 +335,9 @@ public IGlobalConfigSettings StringsFrom(Action this; + MappingConfigStartingPoint IGlobalMappingSettings.AndWhenMapping => this; + + IProjectionConfigStartingPoint IGlobalProjectionSettings.AndWhenMapping => this; private MappingConfigInfo GlobalConfigInfo => _configInfo.ForAllRuleSets().ForAllSourceTypes().ForAllTargetTypes(); @@ -434,6 +449,9 @@ private DictionaryMappingConfigurator CreateDictionaryConfigurator From() => GetTargetTypeSpecifier(ci => ci.ForSourceType()); + IProjectionResultSelector IProjectionConfigStartingPoint.From() + => From(); + /// /// Configure how this mapper performs mappings from all source types and MappingRuleSets (create new, /// overwrite, etc), to the Type. diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index fa1d49568..6bd383d87 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -38,6 +38,9 @@ public MappingConfigurator(MappingConfigInfo configInfo) public MappingConfigStartingPoint WhenMapping => new MappingConfigStartingPoint(MapperContext); + IProjectionConfigStartingPoint IFullProjectionInlineConfigurator.WhenMapping + => WhenMapping; + public ITargetDictionaryMappingInlineConfigurator ForDictionaries => new TargetDictionaryMappingConfigurator(ConfigInfo); diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index 6dc24e054..5d179511e 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -13,7 +13,7 @@ public interface IFullProjectionInlineConfigurator - MappingConfigStartingPoint WhenMapping { get; } + IProjectionConfigStartingPoint WhenMapping { get; } /// /// Throw an exception upon execution of this statement if the projection being configured has any result diff --git a/AgileMapper/Api/Configuration/Projection/IGlobalProjectionSettings.cs b/AgileMapper/Api/Configuration/Projection/IGlobalProjectionSettings.cs new file mode 100644 index 000000000..b8a712068 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IGlobalProjectionSettings.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for globally configuring how all mappers will perform query projections. + /// + public interface IGlobalProjectionSettings + { + /// + /// Gets a link back to the full , for api fluency. + /// + IProjectionConfigStartingPoint AndWhenMapping { get; } + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionConfigStartingPoint.cs b/AgileMapper/Api/Configuration/Projection/IProjectionConfigStartingPoint.cs new file mode 100644 index 000000000..44f8f8540 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionConfigStartingPoint.cs @@ -0,0 +1,42 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + using System; + using AgileMapper.Configuration; + + /// + /// Provides options for configuring how a mapper performs a query projection. + /// + public interface IProjectionConfigStartingPoint + { + /// + /// Configure a formatting string to use when projecting from the given + /// to strings, for all source and result Types. The configured formatting string will have to be supported + /// by the QueryProvider, and may be ignored if it is not. + /// + /// The source value type to which to apply a formatting string. + /// An action which supplies the formatting string. + /// + /// An with which to globally configure other query projection + /// aspects. + /// + IGlobalProjectionSettings StringsFrom(Action formatSelector); + + /// + /// Configure how this mapper performs projections from the Type. + /// + /// The source Type to which the configuration will apply. + /// + /// An IProjectionResultSelector with which to specify to which result Type the configuration + /// will apply. + /// + IProjectionResultSelector From() where TSource : class; + + /// + /// Configure how this mapper performs query projection mappings from any source type to the + /// Type. + /// + /// The result Type to which the configuration will apply. + /// An IFullProjectionConfigurator with which to complete the configuration. + IFullProjectionConfigurator ProjectionsTo(); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/Projection/IProjectionResultSelector.cs b/AgileMapper/Api/Configuration/Projection/IProjectionResultSelector.cs new file mode 100644 index 000000000..3181e6136 --- /dev/null +++ b/AgileMapper/Api/Configuration/Projection/IProjectionResultSelector.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.Api.Configuration.Projection +{ + /// + /// Provides options for specifying the target type and mapping rule set to which the configuration should + /// apply. + /// + /// The source type being configured. + public interface IProjectionResultSelector + { + /// + /// Configure how this mapper performs query projections from the source Type being configured to the + /// result Type specified by the given argument. + /// + /// The result Type to which the configuration will apply. + /// An IFullProjectionConfigurator with which to complete the configuration. + IFullProjectionConfigurator ProjectedTo(); + } +} \ No newline at end of file diff --git a/AgileMapper/Api/Configuration/TargetSpecifier.cs b/AgileMapper/Api/Configuration/TargetSpecifier.cs index 34e1cb232..8a396b79f 100644 --- a/AgileMapper/Api/Configuration/TargetSpecifier.cs +++ b/AgileMapper/Api/Configuration/TargetSpecifier.cs @@ -10,7 +10,7 @@ /// apply. /// /// The source type being configured. - public class TargetSpecifier + public class TargetSpecifier : IProjectionResultSelector { private readonly MappingConfigInfo _configInfo; From fbee7827dfd51126f4d92e28f4933f6ac39b4a67 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 17:03:27 +0000 Subject: [PATCH 171/176] Extending inline projection configuration test coverage --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 + .../WhenConfiguringDerivedTypesInline.cs | 53 +++++++++++++++++++ .../WhenConfiguringObjectCreationInline.cs | 44 +++++++++++++++ .../WhenConfiguringObjectCreation.cs | 8 +-- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringObjectCreationInline.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 70c9493f6..f667b2393 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -151,7 +151,9 @@ + + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs new file mode 100644 index 000000000..0cd609617 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringDerivedTypesInline.cs @@ -0,0 +1,53 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + using static TestClasses.Animal.AnimalType; + + public class WhenConfiguringDerivedTypesInline : OrmTestClassBase + { + public WhenConfiguringDerivedTypesInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToConfiguredDerivedTypesInline() + { + return RunTest(async context => + { + var fido = new Animal { Type = Dog, Name = "Fido" }; + var nelly = new Animal { Type = Elephant, Name = "Nelly" }; + + await context.Animals.AddRangeAsync(fido, nelly); + await context.SaveChangesAsync(); + + var animalDtos = await context + .Animals + .Project().To(cfg => cfg + .If(a => a.Type == Dog) + .MapTo() + .And + .If(a => a.Type == Elephant) + .MapTo()) + .OrderBy(a => a.Id) + .ToArrayAsync(); + + animalDtos.First().ShouldBeOfType(); + animalDtos.First().Id.ShouldBe(fido.Id); + animalDtos.First().Name.ShouldBe("Fido"); + animalDtos.First().Sound.ShouldBe("Woof"); + + animalDtos.Second().ShouldBeOfType(); + animalDtos.Second().Id.ShouldBe(nelly.Id); + animalDtos.Second().Name.ShouldBe("Nelly"); + animalDtos.Second().Sound.ShouldBe("Trumpet"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringObjectCreationInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringObjectCreationInline.cs new file mode 100644 index 000000000..8db1959b2 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringObjectCreationInline.cs @@ -0,0 +1,44 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenConfiguringObjectCreationInline : OrmTestClassBase + { + public WhenConfiguringObjectCreationInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldUseAConditionalObjectFactoryInline() + { + return RunTest(async context => + { + await context.IntItems.AddRangeAsync( + new PublicInt { Value = 1 }, + new PublicInt { Value = 2 }, + new PublicInt { Value = 3 }); + + await context.SaveChangesAsync(); + + var stringDtos = context + .IntItems + .OrderBy(p => p.Id) + .Project() + .To(cfg => cfg + .If(p => p.Value % 2 == 0) + .CreateInstancesUsing(p => new PublicStringCtorDto((p.Value * 2).ToString()))) + .ToArray(); + + stringDtos.First().Value.ShouldBe("1"); + stringDtos.Second().Value.ShouldBe("4"); + stringDtos.Third().Value.ShouldBe("3"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs index 6323be8ad..6b490b7ed 100644 --- a/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs +++ b/AgileMapper.UnitTests.Orms/Configuration/WhenConfiguringObjectCreation.cs @@ -83,9 +83,11 @@ protected Task RunShouldErrorUsingAConditionalObjectFactory() private static async Task DoShouldUseAConditionalObjectFactory(TOrmContext context, IMapper mapper) { - await context.IntItems.Add(new PublicInt { Value = 1 }); - await context.IntItems.Add(new PublicInt { Value = 2 }); - await context.IntItems.Add(new PublicInt { Value = 3 }); + await context.IntItems.AddRange( + new PublicInt { Value = 1 }, + new PublicInt { Value = 2 }, + new PublicInt { Value = 3 }); + await context.SaveChanges(); mapper.WhenMapping From cc5a10f16eeb9476cd858e4987abd7010a5be284 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Wed, 14 Feb 2018 17:25:31 +0000 Subject: [PATCH 172/176] Support for inline projection naming patterns / Test coverage for configured map-to-null projections --- .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 + .../WhenConfiguringNameMatchingInline.cs | 167 ++++++++++++++++++ .../Inline/WhenMappingToNullInline.cs | 60 +++++++ .../Api/Configuration/MappingConfigurator.cs | 34 ++++ .../IFullProjectionInlineConfigurator.cs | 84 ++++++++- 5 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringNameMatchingInline.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenMappingToNullInline.cs diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index f667b2393..3146b2205 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -153,9 +153,11 @@ + + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringNameMatchingInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringNameMatchingInline.cs new file mode 100644 index 000000000..9fdacf15c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenConfiguringNameMatchingInline.cs @@ -0,0 +1,167 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Threading.Tasks; + using Infrastructure; + using Microsoft.EntityFrameworkCore; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenConfiguringNameMatchingInline : OrmTestClassBase + { + public WhenConfiguringNameMatchingInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldUseACustomPrefixInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNamePrefix("_")) + .FirstAsync(); + + stringDto.Value.ShouldBe("123"); + }); + } + + [Fact] + public Task ShouldUseMultipleCustomPrefixesInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "aaa", + _Value_ = "bbb", + Value_ = "ccc" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNamePrefixes("str", "_")) + .FirstAsync(); + + stringDto.Value.ShouldBe("aaa"); + }); + } + + [Fact] + public Task ShouldUseACustomSuffixInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNameSuffix("_")) + .FirstAsync(); + + stringDto.Value.ShouldBe("789"); + }); + } + + [Fact] + public Task ShouldUseMultipleCustomSuffixesInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "aaa", + _Value_ = "bbb", + Value_ = "ccc" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNameSuffixes("Val", "_")) + .FirstAsync(); + + stringDto.Value.ShouldBe("ccc"); + }); + } + + [Fact] + public Task ShouldUseACustomNamingPatternInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "123", + _Value_ = "456", + Value_ = "789" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNamePattern("^_(.+)_$")) + .FirstAsync(); + + stringDto.Value.ShouldBe("456"); + }); + } + + [Fact] + public Task ShouldUseMultipleCustomNamingPatternsInline() + { + return RunTest(async (context, mapper) => + { + var names = new PublicStringNames + { + _Value = "aaa", + _Value_ = "bbb", + Value_ = "ccc" + }; + + await context.StringNameItems.AddAsync(names); + await context.SaveChangesAsync(); + + var stringDto = await context + .StringNameItems + .ProjectUsing(mapper) + .To(cfg => cfg.UseNamePatterns("^str(.+)Val$", "^_(.+)_$")) + .FirstAsync(); + + stringDto.Value.ShouldBe("bbb"); + }); + } + } +} diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenMappingToNullInline.cs b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenMappingToNullInline.cs new file mode 100644 index 000000000..c946474f3 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/Configuration/Inline/WhenMappingToNullInline.cs @@ -0,0 +1,60 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Configuration.Inline +{ + using System.Linq; + using System.Threading.Tasks; + using Infrastructure; + using Orms.Infrastructure; + using TestClasses; + using Xunit; + + public class WhenMappingToNullInline : OrmTestClassBase + { + public WhenMappingToNullInline(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldApplyAUserConfigurationInline() + { + return RunTest(async (context, mapper) => + { + var person1 = new Person + { + Name = "Frank", + Address = new Address { Line1 = "1" } + }; + + var person2 = new Person + { + Name = "Dee", + Address = new Address { Line1 = "Paddie's Pub" } + }; + + await context.Persons.AddRangeAsync(person1, person2); + await context.SaveChangesAsync(); + + var personDtos = context + .Persons + .ProjectUsing(mapper) + .To(cfg => cfg + .WhenMapping + .From
() + .ProjectedTo() + .If(a => a.Line1.Length != 1) + .MapToNull()) + .OrderBy(p => p.Id) + .ToArray(); + + personDtos.Length.ShouldBe(2); + + personDtos.First().Name.ShouldBe("Frank"); + personDtos.First().Address.ShouldNotBeNull(); + personDtos.First().Address.Line1.ShouldBe("1"); + + personDtos.Second().Name.ShouldBe("Dee"); + personDtos.Second().Address.ShouldBeNull(); + }); + } + } +} diff --git a/AgileMapper/Api/Configuration/MappingConfigurator.cs b/AgileMapper/Api/Configuration/MappingConfigurator.cs index 6bd383d87..e6d278ebb 100644 --- a/AgileMapper/Api/Configuration/MappingConfigurator.cs +++ b/AgileMapper/Api/Configuration/MappingConfigurator.cs @@ -98,6 +98,40 @@ public IFullProjectionInlineConfigurator RecurseToDepth(int re public IConditionalRootProjectionConfigurator If(Expression> condition) => SetCondition(condition); + #region Naming + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNamePrefix( + string prefix) => ((IFullProjectionInlineConfigurator)this).UseNamePrefixes(prefix); + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNamePrefixes( + params string[] prefixes) + { + MapperContext.Naming.AddNamePrefixes(prefixes); + return this; + } + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNameSuffix( + string suffix) => ((IFullProjectionInlineConfigurator)this).UseNameSuffixes(suffix); + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNameSuffixes( + params string[] suffixes) + { + MapperContext.Naming.AddNameSuffixes(suffixes); + return this; + } + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNamePattern( + string pattern) => ((IFullProjectionInlineConfigurator)this).UseNamePatterns(pattern); + + IFullProjectionInlineConfigurator IFullProjectionInlineConfigurator.UseNamePatterns( + params string[] patterns) + { + MapperContext.Naming.AddNameMatchers(patterns); + return this; + } + + #endregion + #endregion #region If Overloads diff --git a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs index 5d179511e..32ef53c1f 100644 --- a/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs +++ b/AgileMapper/Api/Configuration/Projection/IFullProjectionInlineConfigurator.cs @@ -18,10 +18,92 @@ public interface IFullProjectionInlineConfigurator /// Throw an exception upon execution of this statement if the projection being configured has any result /// members which will not be mapped, projects from a source enum to a target enum which does not support - /// all of its values, or includes complex types which cannot be constructed. Use calls to this method to + /// all of its values, or includes complex Types which cannot be constructed. Use calls to this method to /// validate a mapping plan; remove them in production /// code. ///
void ThrowNowIfMappingPlanIsIncomplete(); + + #region Naming + + /// + /// Expect members of the source and result element Types being projected to potentially have the given + /// name . Source and result element members will be matched as if the prefix + /// is absent. + /// + /// The prefix to ignore when matching source and result element members. + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNamePrefix(string prefix); + + /// + /// Expect members of the source and result element Types being mapped to potentially have any of the given + /// name . Source and result element members will be matched as if the prefixes + /// are absent. + /// + /// The prefixes to ignore when matching source and result element members. + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNamePrefixes(params string[] prefixes); + + /// + /// Expect members of the source and result element Types being mapped to potentially have the given name + /// . Source and target members will be matched as if the suffix is absent. + /// + /// The suffix to ignore when matching source and result element members. + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNameSuffix(string suffix); + + /// + /// Expect members of the source and result element Types being mapped to potentially have any of the given name + /// . Source and target members will be matched as if the suffixes are absent. + /// + /// The suffixes to ignore when matching source and result element members. + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNameSuffixes(params string[] suffixes); + + /// + /// Expect members of the source and result element Types being mapped to potentially match the given name + /// . The pattern will be used to find the part of a name which should be used + /// to match a source and result element member. + /// + /// + /// The Regex pattern to check against source and result element member names. The pattern is expected to + /// start with the ^ character, end with the $ character and contain a single capturing group wrapped in + /// parentheses, e.g. ^__(.+)__$ + /// + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNamePattern(string pattern); + + /// + /// Expect members of the source and result element Types being mapped to potentially match the given name + /// . The patterns will be used to find the part of a name which should be used + /// to match a source and result element member. + /// + /// + /// The Regex patterns to check against source and result element member names. Each pattern is expected to + /// start with the ^ character, end with the $ character and contain a single capturing group wrapped in + /// parentheses, e.g. ^__(.+)__$ + /// + /// + /// This with which to + /// configure further settings for the source and result element Types being mapped. + /// + IFullProjectionInlineConfigurator UseNamePatterns(params string[] patterns); + + #endregion } } From 232d2c15a11182cd74b65b5515177597daf388e6 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 15 Feb 2018 09:55:13 +0000 Subject: [PATCH 173/176] Organising project-to-enumerable tests --- .../AgileMapper.UnitTests.Orms.Ef5.csproj | 2 +- .../WhenProjectingToEnumerableMembers.cs | 26 --------- .../WhenProjectingToEnumerableMembers.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.Ef6.csproj | 2 +- .../WhenProjectingToEnumerableMembers.cs | 26 --------- .../WhenProjectingToEnumerableMembers.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.EfCore1.csproj | 2 +- .../WhenProjectingToEnumerableMembers.cs | 26 --------- .../WhenProjectingToEnumerableMembers.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.EfCore2.csproj | 2 +- .../WhenProjectingToEnumerableMembers.cs | 26 --------- .../WhenProjectingToEnumerableMembers.cs | 18 ++++++ .../AgileMapper.UnitTests.Orms.csproj | 5 +- .../ICollectionMemberProjectionFailureTest.cs | 9 --- .../ICollectionMemberProjectorTest.cs | 9 --- .../IEnumerableMemberProjectorTest.cs | 9 --- .../WhenProjectingToEnumerableMembers.cs | 56 +++++++++---------- 17 files changed, 104 insertions(+), 168 deletions(-) delete mode 100644 AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs delete mode 100644 AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs delete mode 100644 AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs create mode 100644 AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs delete mode 100644 AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs delete mode 100644 AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs rename AgileMapper.UnitTests.Orms/{Enumerables => }/WhenProjectingToEnumerableMembers.cs (73%) diff --git a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj index 6cb0c722b..3817966ae 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj +++ b/AgileMapper.UnitTests.Orms.Ef5/AgileMapper.UnitTests.Orms.Ef5.csproj @@ -107,7 +107,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs deleted file mode 100644 index c664b19ba..000000000 --- a/AgileMapper.UnitTests.Orms.Ef5/Enumerables/WhenProjectingToEnumerableMembers.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5.Enumerables -{ - using System.Threading.Tasks; - using Infrastructure; - using Orms.Enumerables; - using Xunit; - - public class WhenProjectingToEnumerableMembers : - WhenProjectingToEnumerableMembers, - ICollectionMemberProjectionFailureTest, - IEnumerableMemberProjectorTest - { - public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) - : base(context) - { - } - - [Fact] - public Task ShouldErrorProjectingToAComplexTypeCollectionMember() - => RunShouldErrorProjectingToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectToAComplexTypeEnumerableMember() - => RunShouldProjectToAComplexTypeEnumerableMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..5ad85f04f --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef5/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef5 +{ + using System.Threading.Tasks; + using Infrastructure; + using Xunit; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEf5TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldErrorProjectingToAComplexTypeCollectionMember() + => RunShouldErrorProjectingToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj index c65bd5447..e52983991 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj +++ b/AgileMapper.UnitTests.Orms.Ef6/AgileMapper.UnitTests.Orms.Ef6.csproj @@ -111,7 +111,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs deleted file mode 100644 index befda8339..000000000 --- a/AgileMapper.UnitTests.Orms.Ef6/Enumerables/WhenProjectingToEnumerableMembers.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6.Enumerables -{ - using System.Threading.Tasks; - using Infrastructure; - using Orms.Enumerables; - using Xunit; - - public class WhenProjectingToEnumerableMembers : - WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest, - IEnumerableMemberProjectorTest - { - public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) - : base(context) - { - } - - [Fact] - public Task ShouldProjectToAComplexTypeCollectionMember() - => RunShouldProjectToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectToAComplexTypeEnumerableMember() - => RunShouldProjectToAComplexTypeEnumerableMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..7486662d0 --- /dev/null +++ b/AgileMapper.UnitTests.Orms.Ef6/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.Ef6 +{ + using System.Threading.Tasks; + using Infrastructure; + using Xunit; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEf6TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj index 06e481e9e..fd1c25c47 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore1/AgileMapper.UnitTests.Orms.EfCore1.csproj @@ -238,7 +238,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs deleted file mode 100644 index 55dd40601..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore1/Enumerables/WhenProjectingToEnumerableMembers.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1.Enumerables -{ - using System.Threading.Tasks; - using Infrastructure; - using Orms.Enumerables; - using Xunit; - - public class WhenProjectingToEnumerableMembers : - WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest, - IEnumerableMemberProjectorTest - { - public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) - : base(context) - { - } - - [Fact] - public Task ShouldProjectToAComplexTypeCollectionMember() - => RunShouldProjectToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectToAComplexTypeEnumerableMember() - => RunShouldProjectToAComplexTypeEnumerableMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..19a1c8b5c --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore1 +{ + using System.Threading.Tasks; + using Infrastructure; + using Xunit; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj index 3146b2205..e7c54f90a 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj +++ b/AgileMapper.UnitTests.Orms.EfCore2/AgileMapper.UnitTests.Orms.EfCore2.csproj @@ -187,7 +187,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs deleted file mode 100644 index 242c7847b..000000000 --- a/AgileMapper.UnitTests.Orms.EfCore2/Enumerables/WhenProjectingToEnumerableMembers.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2.Enumerables -{ - using System.Threading.Tasks; - using Infrastructure; - using Orms.Enumerables; - using Xunit; - - public class WhenProjectingToEnumerableMembers : - WhenProjectingToEnumerableMembers, - ICollectionMemberProjectorTest, - IEnumerableMemberProjectorTest - { - public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) - : base(context) - { - } - - [Fact] - public Task ShouldProjectToAComplexTypeCollectionMember() - => RunShouldProjectToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectToAComplexTypeEnumerableMember() - => RunShouldProjectToAComplexTypeEnumerableMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs new file mode 100644 index 000000000..8e956dbea --- /dev/null +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -0,0 +1,18 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.EfCore2 +{ + using System.Threading.Tasks; + using Infrastructure; + using Xunit; + + public class WhenProjectingToEnumerableMembers : WhenProjectingToEnumerableMembers + { + public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) + : base(context) + { + } + + [Fact] + public Task ShouldProjectToAComplexTypeCollectionMember() + => RunShouldProjectToAComplexTypeCollectionMember(); + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index 18c6886dc..d5d523f26 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -82,9 +82,6 @@ - - - @@ -167,7 +164,7 @@ - + diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs deleted file mode 100644 index 0e6e97778..000000000 --- a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectionFailureTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables -{ - using System.Threading.Tasks; - - public interface ICollectionMemberProjectionFailureTest - { - Task ShouldErrorProjectingToAComplexTypeCollectionMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs deleted file mode 100644 index a69c1e068..000000000 --- a/AgileMapper.UnitTests.Orms/Enumerables/ICollectionMemberProjectorTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables -{ - using System.Threading.Tasks; - - public interface ICollectionMemberProjectorTest - { - Task ShouldProjectToAComplexTypeCollectionMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs b/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs deleted file mode 100644 index 86bc7318a..000000000 --- a/AgileMapper.UnitTests.Orms/Enumerables/IEnumerableMemberProjectorTest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables -{ - using System.Threading.Tasks; - - public interface IEnumerableMemberProjectorTest - { - Task ShouldProjectToAComplexTypeEnumerableMember(); - } -} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs similarity index 73% rename from AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs rename to AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs index 652919319..21b74b5c4 100644 --- a/AgileMapper.UnitTests.Orms/Enumerables/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs @@ -1,4 +1,4 @@ -namespace AgileObjects.AgileMapper.UnitTests.Orms.Enumerables +namespace AgileObjects.AgileMapper.UnitTests.Orms { using System; using System.Collections.Generic; @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Infrastructure; using TestClasses; + using Xunit; public abstract class WhenProjectingToEnumerableMembers : OrmTestClassBase where TOrmContext : ITestDbContext, new() @@ -93,43 +94,40 @@ private static async Task ProjectToComplexTypeCollectionMember(TOrmContext conte #endregion - #region Project -> Enumerable - - protected Task RunShouldProjectToAComplexTypeEnumerableMember() - => RunTest(ProjectToComplexTypeEnumerableMember); - - protected async Task ProjectToComplexTypeEnumerableMember(TOrmContext context) + [Fact] + public Task ShouldProjectToComplexTypeEnumerableMember() { - var item1 = new OrderItem(); - var item2 = new OrderItem(); - - var order = new OrderUk + return RunTest(async context => { - DatePlaced = DateTime.Now, - Items = new List { item1, item2 } - }; + var item1 = new OrderItem(); + var item2 = new OrderItem(); - await context.Orders.Add(order); - await context.SaveChanges(); + var order = new OrderUk + { + DatePlaced = DateTime.Now, + Items = new List { item1, item2 } + }; - var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); + await context.Orders.Add(order); + await context.SaveChanges(); - rotaDto.Id.ShouldBe(1); - rotaDto.DatePlaced.ShouldBe(order.DatePlaced); - rotaDto.Items.Count().ShouldBe(order.Items.Count); + var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); - var i = 0; + rotaDto.Id.ShouldBe(1); + rotaDto.DatePlaced.ShouldBe(order.DatePlaced); + rotaDto.Items.Count().ShouldBe(order.Items.Count); - foreach (var orderItem in order.Items) - { - var orderItemDto = order.Items.ElementAt(i); + var i = 0; - orderItemDto.Id.ShouldBe(orderItem.Id); + foreach (var orderItem in order.Items) + { + var orderItemDto = order.Items.ElementAt(i); - ++i; - } - } + orderItemDto.Id.ShouldBe(orderItem.Id); - #endregion + ++i; + } + }); + } } } From 94951792a4a6b70b4297e957d3640261e03eaf72 Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 15 Feb 2018 10:04:13 +0000 Subject: [PATCH 174/176] Differentiating assembly-loading in .NET Standard --- .../Settings/DefaultQueryProviderSettings.cs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs index 640a2096a..6f066dbdc 100644 --- a/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs +++ b/AgileMapper/Queryables/Settings/DefaultQueryProviderSettings.cs @@ -106,8 +106,28 @@ protected virtual Expression GetParseStringToDateTimeOrNull(MethodCallExpression public virtual Expression ConvertStringToEnumConversion(ConditionalExpression conversion) => conversion; protected static Type GetTypeOrNull(string loadedAssemblyName, string typeName) - => GetTypeOrNull(Assembly.Load(new AssemblyName(loadedAssemblyName)), typeName); + => GetTypeOrNull(GetAssemblyOrNull(loadedAssemblyName), typeName); - protected static Type GetTypeOrNull(Assembly assembly, string typeName) => assembly.GetType(typeName); + private static Assembly GetAssemblyOrNull(string loadedAssemblyName) + { +#if NET_STANDARD + try + { + return Assembly.Load(new AssemblyName(loadedAssemblyName)); + } + catch + { + return null; + } +#else + var assemblyName = loadedAssemblyName.Substring(0, loadedAssemblyName.IndexOf(',')); + + return AppDomain.CurrentDomain + .GetAssemblies() + .FirstOrDefault(assembly => assembly.GetName().Name == assemblyName); +#endif + } + + protected static Type GetTypeOrNull(Assembly assembly, string typeName) => assembly?.GetType(typeName); } } \ No newline at end of file From 7c1e715c40d191c399476999e5cbb6809de7ad7f Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 15 Feb 2018 10:41:28 +0000 Subject: [PATCH 175/176] Test coverage for projection via a linking type in Ef Core 1+ 2 --- .../Infrastructure/Ef5TestDbContext.cs | 4 ++ .../Infrastructure/Ef6TestDbContext.cs | 4 ++ .../Infrastructure/EfCore1TestDbContext.cs | 12 ++++ .../WhenProjectingToEnumerableMembers.cs | 3 + .../Infrastructure/EfCore2TestDbContext.cs | 12 ++++ .../WhenProjectingToEnumerableMembers.cs | 3 + .../AgileMapper.UnitTests.Orms.csproj | 3 + .../Infrastructure/ITestDbContext.cs | 2 + .../Infrastructure/OrmTestClassBase.cs | 1 + .../TestClasses/Account.cs | 33 +++++++++ .../TestClasses/AccountAddress.cs | 13 ++++ .../TestClasses/AccountDto.cs | 13 ++++ .../WhenProjectingToEnumerableMembers.cs | 67 +++++++++++++++++-- 13 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/Account.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/AccountAddress.cs create mode 100644 AgileMapper.UnitTests.Orms/TestClasses/AccountDto.cs diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 6685b64b7..26dd9852f 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -31,6 +31,8 @@ protected Ef5TestDbContext(DbConnection dbConnection) public DbSet Products { get; set; } + public DbSet Accounts { get; set; } + public DbSet Persons { get; set; } public DbSet
Addresses { get; set; } @@ -88,6 +90,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.Products => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Accounts => new Ef5DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Persons => new Ef5DbSetWrapper(this); IDbSetWrapper
ITestDbContext.Addresses => new Ef5DbSetWrapper
(this); diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 73edece5e..1048e923e 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -31,6 +31,8 @@ protected Ef6TestDbContext(DbConnection dbConnection) public DbSet Products { get; set; } + public DbSet Accounts { get; set; } + public DbSet Persons { get; set; } public DbSet
Addresses { get; set; } @@ -88,6 +90,8 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.Products => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Accounts => new Ef6DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Persons => new Ef6DbSetWrapper(this); IDbSetWrapper
ITestDbContext.Addresses => new Ef6DbSetWrapper
(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs index 95f317c7b..8ef8822b8 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/Infrastructure/EfCore1TestDbContext.cs @@ -29,6 +29,8 @@ public EfCore1TestDbContext() public DbSet Products { get; set; } + public DbSet Accounts { get; set; } + public DbSet Persons { get; set; } public DbSet
Addresses { get; set; } @@ -78,6 +80,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(); modelBuilder.Entity(); + modelBuilder.Entity() + .HasKey(aa => new { aa.AccountId, aa.AddressId }); + + modelBuilder.Entity() + .HasOne(aa => aa.Account) + .WithMany(a => a.DeliveryAddresses) + .HasForeignKey(aa => aa.AccountId); + base.OnModelCreating(modelBuilder); } @@ -95,6 +105,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.Products => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Accounts => new EfCore1DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Persons => new EfCore1DbSetWrapper(this); IDbSetWrapper
ITestDbContext.Addresses => new EfCore1DbSetWrapper
(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs index 19a1c8b5c..cdbe03bf5 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -14,5 +14,8 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) [Fact] public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); + + [Fact] + public Task ShouldProjectViaLinkingType() => RunShouldProjectViaLinkingType(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs index 22caef417..ee1e20445 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/Infrastructure/EfCore2TestDbContext.cs @@ -37,6 +37,8 @@ protected EfCore2TestDbContext(DbContextOptions options) public DbSet Products { get; set; } + public DbSet Accounts { get; set; } + public DbSet Persons { get; set; } public DbSet
Addresses { get; set; } @@ -88,6 +90,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(); modelBuilder.Entity(); + modelBuilder.Entity() + .HasKey(aa => new { aa.AccountId, aa.AddressId }); + + modelBuilder.Entity() + .HasOne(aa => aa.Account) + .WithMany(a => a.DeliveryAddresses) + .HasForeignKey(aa => aa.AccountId); + base.OnModelCreating(modelBuilder); } @@ -105,6 +115,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) IDbSetWrapper ITestDbContext.Products => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Accounts => new EfCore2DbSetWrapper(this); + IDbSetWrapper ITestDbContext.Persons => new EfCore2DbSetWrapper(this); IDbSetWrapper
ITestDbContext.Addresses => new EfCore2DbSetWrapper
(this); diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs index 8e956dbea..7b599bfd7 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -14,5 +14,8 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) [Fact] public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); + + [Fact] + public Task ShouldProjectViaLinkingType() => RunShouldProjectViaLinkingType(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj index d5d523f26..6e3918f1c 100644 --- a/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj +++ b/AgileMapper.UnitTests.Orms/AgileMapper.UnitTests.Orms.csproj @@ -100,6 +100,9 @@ + + + diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs index 98a0cfd3b..70e29f56f 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/ITestDbContext.cs @@ -18,6 +18,8 @@ public interface ITestDbContext : IDisposable IDbSetWrapper Products { get; } + IDbSetWrapper Accounts { get; } + IDbSetWrapper Persons { get; } IDbSetWrapper
Addresses { get; } diff --git a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs index 4a9b9741c..62cc692ed 100644 --- a/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs +++ b/AgileMapper.UnitTests.Orms/Infrastructure/OrmTestClassBase.cs @@ -60,6 +60,7 @@ private async Task EmptyDbContext() Context.Categories.Clear(); Context.Products.Clear(); Context.Addresses.Clear(); + Context.Accounts.Clear(); Context.Persons.Clear(); Context.OrderItems.Clear(); Context.Orders.Clear(); diff --git a/AgileMapper.UnitTests.Orms/TestClasses/Account.cs b/AgileMapper.UnitTests.Orms/TestClasses/Account.cs new file mode 100644 index 000000000..7626ec3ae --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/Account.cs @@ -0,0 +1,33 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + + public class Account + { + public Account() + { + DeliveryAddresses = new List(); + } + + [Key] + public int Id { get; set; } + + public int UserId { get; set; } + + public Person User { get; set; } + + public Account AddDeliveryAddress(Address address) + { + DeliveryAddresses.Add(new AccountAddress + { + Account = this, + Address = address + }); + + return this; + } + + public ICollection DeliveryAddresses { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AccountAddress.cs b/AgileMapper.UnitTests.Orms/TestClasses/AccountAddress.cs new file mode 100644 index 000000000..4d072cc40 --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/AccountAddress.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + public class AccountAddress + { + public int AccountId { get; set; } + + public Account Account { get; set; } + + public int AddressId { get; set; } + + public Address Address { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/TestClasses/AccountDto.cs b/AgileMapper.UnitTests.Orms/TestClasses/AccountDto.cs new file mode 100644 index 000000000..237f451ea --- /dev/null +++ b/AgileMapper.UnitTests.Orms/TestClasses/AccountDto.cs @@ -0,0 +1,13 @@ +namespace AgileObjects.AgileMapper.UnitTests.Orms.TestClasses +{ + using System.Collections.Generic; + + public class AccountDto + { + public int Id { get; set; } + + public PersonDto User { get; set; } + + public IEnumerable DeliveryAddresses { get; set; } + } +} \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs index 21b74b5c4..6a8892117 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs @@ -111,11 +111,15 @@ public Task ShouldProjectToComplexTypeEnumerableMember() await context.Orders.Add(order); await context.SaveChanges(); - var rotaDto = context.Orders.Where(r => r.Id == 1).Project().To().First(); + var orderDto = context + .Orders + .Project() + .To() + .ShouldHaveSingleItem(); - rotaDto.Id.ShouldBe(1); - rotaDto.DatePlaced.ShouldBe(order.DatePlaced); - rotaDto.Items.Count().ShouldBe(order.Items.Count); + orderDto.Id.ShouldBe(order.Id); + orderDto.DatePlaced.ShouldBe(order.DatePlaced); + orderDto.Items.Count().ShouldBe(order.Items.Count); var i = 0; @@ -129,5 +133,60 @@ public Task ShouldProjectToComplexTypeEnumerableMember() } }); } + + #region Project -> Via Linking Type + + protected Task RunShouldProjectViaLinkingType() + => RunTest(DoShouldProjectViaLinkingType); + + private static async Task DoShouldProjectViaLinkingType(TOrmContext context) + { + var account = new Account + { + User = new Person + { + Name = "Mario", + Address = new Address { Line1 = "Here", Postcode = "HS93HS" } + } + }; + + account.AddDeliveryAddress(new Address { Line1 = "There", Postcode = "JS95TH" }); + account.AddDeliveryAddress(new Address { Line1 = "Somewhere", Postcode = "KA02ID" }); + + await context.Accounts.Add(account); + await context.SaveChanges(); + + var accountDto = context + .Accounts + .Project() + .To(cfg => cfg + .Map(a => a.DeliveryAddresses.Select(da => da.Address)) + .To(dto => dto.DeliveryAddresses)) + .ShouldHaveSingleItem(); + + accountDto.Id.ShouldBe(account.Id); + + accountDto.User.Id.ShouldBe(account.User.PersonId); + accountDto.User.Name.ShouldBe(account.User.Name); + accountDto.User.Address.Id.ShouldBe(account.User.Address.AddressId); + accountDto.User.Address.Line1.ShouldBe(account.User.Address.Line1); + accountDto.User.Address.Postcode.ShouldBe(account.User.Address.Postcode); + + accountDto.DeliveryAddresses.Count().ShouldBe(2); + + var addressDto1 = accountDto.DeliveryAddresses.First(); + var address1 = account.DeliveryAddresses.First().Address; + addressDto1.Id.ShouldBe(address1.AddressId); + addressDto1.Line1.ShouldBe(address1.Line1); + addressDto1.Postcode.ShouldBe(address1.Postcode); + + var addressDto2 = accountDto.DeliveryAddresses.Second(); + var address2 = account.DeliveryAddresses.Second().Address; + addressDto2.Id.ShouldBe(address2.AddressId); + addressDto2.Line1.ShouldBe(address2.Line1); + addressDto2.Postcode.ShouldBe(address2.Postcode); + } + + #endregion } } From 15285a4d7407cb3620975bc3227a5d40a93dd1fb Mon Sep 17 00:00:00 2001 From: Steve Wilkes Date: Thu, 15 Feb 2018 10:55:51 +0000 Subject: [PATCH 176/176] Test coverage for projection via a linking type in EF5 + EF6 --- .../Infrastructure/Ef5TestDbContext.cs | 3 + .../Infrastructure/Ef6TestDbContext.cs | 3 + .../WhenProjectingToEnumerableMembers.cs | 3 - .../WhenProjectingToEnumerableMembers.cs | 3 - .../WhenProjectingToEnumerableMembers.cs | 91 +++++++++---------- 5 files changed, 50 insertions(+), 53 deletions(-) diff --git a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs index 26dd9852f..6c1b7f9c3 100644 --- a/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef5/Infrastructure/Ef5TestDbContext.cs @@ -73,6 +73,9 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) .WithOptional(c => c.ParentCategory) .HasForeignKey(c => c.ParentCategoryId); + modelBuilder.Entity() + .HasKey(aa => new { aa.AccountId, aa.AddressId }); + base.OnModelCreating(modelBuilder); } diff --git a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs index 1048e923e..34f35ee8a 100644 --- a/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs +++ b/AgileMapper.UnitTests.Orms.Ef6/Infrastructure/Ef6TestDbContext.cs @@ -73,6 +73,9 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) .WithOptional(c => c.ParentCategory) .HasForeignKey(c => c.ParentCategoryId); + modelBuilder.Entity() + .HasKey(aa => new { aa.AccountId, aa.AddressId }); + base.OnModelCreating(modelBuilder); } diff --git a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs index cdbe03bf5..19a1c8b5c 100644 --- a/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore1/WhenProjectingToEnumerableMembers.cs @@ -14,8 +14,5 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore1TestContext context) [Fact] public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectViaLinkingType() => RunShouldProjectViaLinkingType(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs index 7b599bfd7..8e956dbea 100644 --- a/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms.EfCore2/WhenProjectingToEnumerableMembers.cs @@ -14,8 +14,5 @@ public WhenProjectingToEnumerableMembers(InMemoryEfCore2TestContext context) [Fact] public Task ShouldProjectToAComplexTypeCollectionMember() => RunShouldProjectToAComplexTypeCollectionMember(); - - [Fact] - public Task ShouldProjectViaLinkingType() => RunShouldProjectViaLinkingType(); } } \ No newline at end of file diff --git a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs index 6a8892117..2e3d731e2 100644 --- a/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs +++ b/AgileMapper.UnitTests.Orms/WhenProjectingToEnumerableMembers.cs @@ -134,59 +134,56 @@ public Task ShouldProjectToComplexTypeEnumerableMember() }); } - #region Project -> Via Linking Type - - protected Task RunShouldProjectViaLinkingType() - => RunTest(DoShouldProjectViaLinkingType); - - private static async Task DoShouldProjectViaLinkingType(TOrmContext context) + [Fact] + public Task ShouldProjectViaLinkingType() { - var account = new Account + return RunTest(async context => { - User = new Person + var account = new Account { - Name = "Mario", - Address = new Address { Line1 = "Here", Postcode = "HS93HS" } - } - }; + User = new Person + { + Name = "Mario", + Address = new Address { Line1 = "Here", Postcode = "HS93HS" } + } + }; - account.AddDeliveryAddress(new Address { Line1 = "There", Postcode = "JS95TH" }); - account.AddDeliveryAddress(new Address { Line1 = "Somewhere", Postcode = "KA02ID" }); + account.AddDeliveryAddress(new Address { Line1 = "There", Postcode = "JS95TH" }); + account.AddDeliveryAddress(new Address { Line1 = "Somewhere", Postcode = "KA02ID" }); - await context.Accounts.Add(account); - await context.SaveChanges(); + await context.Accounts.Add(account); + await context.SaveChanges(); - var accountDto = context - .Accounts - .Project() - .To(cfg => cfg - .Map(a => a.DeliveryAddresses.Select(da => da.Address)) - .To(dto => dto.DeliveryAddresses)) - .ShouldHaveSingleItem(); - - accountDto.Id.ShouldBe(account.Id); - - accountDto.User.Id.ShouldBe(account.User.PersonId); - accountDto.User.Name.ShouldBe(account.User.Name); - accountDto.User.Address.Id.ShouldBe(account.User.Address.AddressId); - accountDto.User.Address.Line1.ShouldBe(account.User.Address.Line1); - accountDto.User.Address.Postcode.ShouldBe(account.User.Address.Postcode); - - accountDto.DeliveryAddresses.Count().ShouldBe(2); - - var addressDto1 = accountDto.DeliveryAddresses.First(); - var address1 = account.DeliveryAddresses.First().Address; - addressDto1.Id.ShouldBe(address1.AddressId); - addressDto1.Line1.ShouldBe(address1.Line1); - addressDto1.Postcode.ShouldBe(address1.Postcode); - - var addressDto2 = accountDto.DeliveryAddresses.Second(); - var address2 = account.DeliveryAddresses.Second().Address; - addressDto2.Id.ShouldBe(address2.AddressId); - addressDto2.Line1.ShouldBe(address2.Line1); - addressDto2.Postcode.ShouldBe(address2.Postcode); - } + var accountDto = context + .Accounts + .Project() + .To(cfg => cfg + .Map(a => a.DeliveryAddresses.Select(da => da.Address)) + .To(dto => dto.DeliveryAddresses)) + .ShouldHaveSingleItem(); - #endregion + accountDto.Id.ShouldBe(account.Id); + + accountDto.User.Id.ShouldBe(account.User.PersonId); + accountDto.User.Name.ShouldBe(account.User.Name); + accountDto.User.Address.Id.ShouldBe(account.User.Address.AddressId); + accountDto.User.Address.Line1.ShouldBe(account.User.Address.Line1); + accountDto.User.Address.Postcode.ShouldBe(account.User.Address.Postcode); + + accountDto.DeliveryAddresses.Count().ShouldBe(2); + + var addressDto1 = accountDto.DeliveryAddresses.First(); + var address1 = account.DeliveryAddresses.First().Address; + addressDto1.Id.ShouldBe(address1.AddressId); + addressDto1.Line1.ShouldBe(address1.Line1); + addressDto1.Postcode.ShouldBe(address1.Postcode); + + var addressDto2 = accountDto.DeliveryAddresses.Second(); + var address2 = account.DeliveryAddresses.Second().Address; + addressDto2.Id.ShouldBe(address2.AddressId); + addressDto2.Line1.ShouldBe(address2.Line1); + addressDto2.Postcode.ShouldBe(address2.Postcode); + }); + } } }