diff --git a/.idea/.idea.ExpressMapper/.idea/modules.xml b/.idea/.idea.ExpressMapper/.idea/modules.xml new file mode 100644 index 0000000..66a9604 --- /dev/null +++ b/.idea/.idea.ExpressMapper/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.ExpressMapper/.idea/vcs.xml b/.idea/.idea.ExpressMapper/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.ExpressMapper/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.ExpressMapper/.idea/workspace.xml b/.idea/.idea.ExpressMapper/.idea/workspace.xml new file mode 100644 index 0000000..b4a3b92 --- /dev/null +++ b/.idea/.idea.ExpressMapper/.idea/workspace.xml @@ -0,0 +1,1858 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + typeof(Mapper + Mapper + Recur + Map + GetMapExpressions + MapProperty + recur + InitializeRecursiveMappings + RecursiveExpressionResult + BaseTy + RegisterInterna + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1484511416080 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.ExpressMapper/riderModule.iml b/.idea/.idea.ExpressMapper/riderModule.iml new file mode 100644 index 0000000..4243fce --- /dev/null +++ b/.idea/.idea.ExpressMapper/riderModule.iml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExpressMapper NET40/ExpressMapper NET40.csproj b/ExpressMapper NET40/ExpressMapper NET40.csproj index 6b06129..b245bef 100644 --- a/ExpressMapper NET40/ExpressMapper NET40.csproj +++ b/ExpressMapper NET40/ExpressMapper NET40.csproj @@ -36,7 +36,8 @@ true - Expressmapper.snk + + false @@ -51,49 +52,12 @@ - - - - - - - - - - - - - - - - Code - - - Code - - - - - - - - - - - - - - - - - - - + + --> \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/Models/BaseControl.cs b/ExpressMapper.Tests.Model/Models/BaseControl.cs new file mode 100644 index 0000000..0bf0709 --- /dev/null +++ b/ExpressMapper.Tests.Model/Models/BaseControl.cs @@ -0,0 +1,12 @@ +using System; + +namespace ExpressMapper.Tests.Model.Models +{ + public class BaseControl + { + public Guid Id { get; set; } + public string Name { get; set; } + + public string Description { get; set; } + } +} diff --git a/ExpressMapper.Tests.Model/Models/ComboBox.cs b/ExpressMapper.Tests.Model/Models/ComboBox.cs new file mode 100644 index 0000000..3a66b7a --- /dev/null +++ b/ExpressMapper.Tests.Model/Models/ComboBox.cs @@ -0,0 +1,8 @@ +namespace ExpressMapper.Tests.Model.Models +{ + public class ComboBox : BaseControl + { + public int NumberOfElements { get; set; } + public string GeneralName { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/Models/Gui.cs b/ExpressMapper.Tests.Model/Models/Gui.cs new file mode 100644 index 0000000..302a79a --- /dev/null +++ b/ExpressMapper.Tests.Model/Models/Gui.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace ExpressMapper.Tests.Model.Models +{ + public class Gui + { + public IList Controls { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/Models/Organization.cs b/ExpressMapper.Tests.Model/Models/Organization.cs index d054362..1818706 100644 --- a/ExpressMapper.Tests.Model/Models/Organization.cs +++ b/ExpressMapper.Tests.Model/Models/Organization.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace ExpressMapper.Tests.Model.Models +namespace ExpressMapper.Tests.Model.Models { public class Organization : Contact { diff --git a/ExpressMapper.Tests.Model/Models/TextBox.cs b/ExpressMapper.Tests.Model/Models/TextBox.cs new file mode 100644 index 0000000..3e809bf --- /dev/null +++ b/ExpressMapper.Tests.Model/Models/TextBox.cs @@ -0,0 +1,7 @@ +namespace ExpressMapper.Tests.Model.Models +{ + public class TextBox : BaseControl + { + public string Text { get; set; } + } +} diff --git a/ExpressMapper.Tests.Model/Models/UserInterface.cs b/ExpressMapper.Tests.Model/Models/UserInterface.cs new file mode 100644 index 0000000..1cd8eb6 --- /dev/null +++ b/ExpressMapper.Tests.Model/Models/UserInterface.cs @@ -0,0 +1,7 @@ +namespace ExpressMapper.Tests.Model.Models +{ + public class UserInterface + { + public BaseControl Control { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/ViewModels/BaseControlViewModel.cs b/ExpressMapper.Tests.Model/ViewModels/BaseControlViewModel.cs new file mode 100644 index 0000000..5ef9be7 --- /dev/null +++ b/ExpressMapper.Tests.Model/ViewModels/BaseControlViewModel.cs @@ -0,0 +1,12 @@ +using System; + +namespace ExpressMapper.Tests.Model.ViewModels +{ + public class BaseControlViewModel + { + public Guid id_ctrl { get; set; } + public string name_ctrl { get; set; } + + public string Description { get; set; } + } +} diff --git a/ExpressMapper.Tests.Model/ViewModels/ComboBoxViewModel.cs b/ExpressMapper.Tests.Model/ViewModels/ComboBoxViewModel.cs new file mode 100644 index 0000000..47f3ce8 --- /dev/null +++ b/ExpressMapper.Tests.Model/ViewModels/ComboBoxViewModel.cs @@ -0,0 +1,8 @@ +namespace ExpressMapper.Tests.Model.ViewModels +{ + public class ComboBoxViewModel : BaseControlViewModel + { + public int AmountOfElements { get; set; } + public string GeneralName { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/ViewModels/GuiViewModel.cs b/ExpressMapper.Tests.Model/ViewModels/GuiViewModel.cs new file mode 100644 index 0000000..f340631 --- /dev/null +++ b/ExpressMapper.Tests.Model/ViewModels/GuiViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace ExpressMapper.Tests.Model.ViewModels +{ + public class GuiViewModel + { + public IEnumerable ControlViewModels { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.Tests.Model/ViewModels/TextBoxViewModel.cs b/ExpressMapper.Tests.Model/ViewModels/TextBoxViewModel.cs new file mode 100644 index 0000000..5997fc1 --- /dev/null +++ b/ExpressMapper.Tests.Model/ViewModels/TextBoxViewModel.cs @@ -0,0 +1,7 @@ +namespace ExpressMapper.Tests.Model.ViewModels +{ + public class TextBoxViewModel : BaseControlViewModel + { + public string Text { get; set; } + } +} diff --git a/ExpressMapper.Tests.Model/ViewModels/UserInterfaceViewModel.cs b/ExpressMapper.Tests.Model/ViewModels/UserInterfaceViewModel.cs new file mode 100644 index 0000000..ff08306 --- /dev/null +++ b/ExpressMapper.Tests.Model/ViewModels/UserInterfaceViewModel.cs @@ -0,0 +1,7 @@ +namespace ExpressMapper.Tests.Model.ViewModels +{ + public class UserInterfaceViewModel + { + public BaseControlViewModel ControlViewModel { get; set; } + } +} \ No newline at end of file diff --git a/ExpressMapper.sln b/ExpressMapper.sln index 4a46e73..68424df 100644 --- a/ExpressMapper.sln +++ b/ExpressMapper.sln @@ -27,7 +27,16 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ExpressMapper.Tests NETCORE EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExpressMapper NET45", "ExpressMapper NET45\ExpressMapper NET45.csproj", "{5E211506-6D4E-4CD8-B454-D02672CF948A}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Expressmapper.Shared", "Expressmapper.Shared\Expressmapper.Shared.shproj", "{75C7DD14-FD53-462D-9FB3-AA5E31380B55}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Expressmapper.Shared\Expressmapper.Shared.projitems*{0b7a5bdb-6d9f-4a9e-a35d-081d0424d577}*SharedItemsImports = 4 + Expressmapper.Shared\Expressmapper.Shared.projitems*{5e211506-6d4e-4cd8-b454-d02672cf948a}*SharedItemsImports = 4 + Expressmapper.Shared\Expressmapper.Shared.projitems*{75c7dd14-fd53-462d-9fb3-aa5e31380b55}*SharedItemsImports = 13 + Expressmapper.Shared\Expressmapper.Shared.projitems*{909366ad-7ff2-4197-a340-cc858c4b8ad1}*SharedItemsImports = 4 + Expressmapper.Shared\Expressmapper.Shared.projitems*{e701191e-bf2e-41b8-a4f0-3d8954e3dd58}*SharedItemsImports = 4 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU diff --git a/ExpressMapper NET40/CollectionTypes.cs b/Expressmapper.Shared/CollectionTypes.cs similarity index 100% rename from ExpressMapper NET40/CollectionTypes.cs rename to Expressmapper.Shared/CollectionTypes.cs diff --git a/ExpressMapper NET40/CompilationTypes.cs b/Expressmapper.Shared/CompilationTypes.cs similarity index 100% rename from ExpressMapper NET40/CompilationTypes.cs rename to Expressmapper.Shared/CompilationTypes.cs diff --git a/ExpressMapper NET40/Constants.cs b/Expressmapper.Shared/Constants.cs similarity index 100% rename from ExpressMapper NET40/Constants.cs rename to Expressmapper.Shared/Constants.cs diff --git a/ExpressMapper NET40/DefaultMappingContext.cs b/Expressmapper.Shared/DefaultMappingContext.cs similarity index 100% rename from ExpressMapper NET40/DefaultMappingContext.cs rename to Expressmapper.Shared/DefaultMappingContext.cs diff --git a/ExpressMapper NET40/DelegateCustomTypeMapper.cs b/Expressmapper.Shared/DelegateCustomTypeMapper.cs similarity index 95% rename from ExpressMapper NET40/DelegateCustomTypeMapper.cs rename to Expressmapper.Shared/DelegateCustomTypeMapper.cs index 6c455dd..5aa3ff7 100644 --- a/ExpressMapper NET40/DelegateCustomTypeMapper.cs +++ b/Expressmapper.Shared/DelegateCustomTypeMapper.cs @@ -1,23 +1,23 @@ -using System; - -namespace ExpressMapper -{ - internal class DelegateCustomTypeMapper : ICustomTypeMapper - { - private readonly Func _mapFunc; - - public DelegateCustomTypeMapper(Func mapFunc) - { - _mapFunc = mapFunc; - } - - public TN Map(IMappingContext context) - { - if (context == null) - { - throw new ArgumentNullException("context"); - } - return _mapFunc(context.Source); - } - } -} +using System; + +namespace ExpressMapper +{ + internal class DelegateCustomTypeMapper : ICustomTypeMapper + { + private readonly Func _mapFunc; + + public DelegateCustomTypeMapper(Func mapFunc) + { + _mapFunc = mapFunc; + } + + public TN Map(IMappingContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + return _mapFunc(context.Source); + } + } +} diff --git a/ExpressMapper NET40/DestinationMappingService.cs b/Expressmapper.Shared/DestinationMappingService.cs similarity index 99% rename from ExpressMapper NET40/DestinationMappingService.cs rename to Expressmapper.Shared/DestinationMappingService.cs index c8dd56d..360fc92 100644 --- a/ExpressMapper NET40/DestinationMappingService.cs +++ b/Expressmapper.Shared/DestinationMappingService.cs @@ -245,7 +245,7 @@ private BlockExpression MapCollectionNotCountEquals(Type tCol, Type tnCol, Expre var closedEnumeratorDestType = typeof(IEnumerator<>).MakeGenericType(destType); var closedEnumerableDestType = GenericEnumerableType.MakeGenericType(destType); var enumeratorDest = Expression.Variable(closedEnumeratorDestType, - $"{Guid.NewGuid().ToString("N")}EnumDst"); + $"{Guid.NewGuid():N}EnumDst"); var assignToEnumDest = Expression.Assign(enumeratorDest, Expression.Call(destVariable, closedEnumerableDestType.GetInfo().GetMethod("GetEnumerator"))); var doMoveNextDest = Expression.Call(enumeratorDest, typeof(IEnumerator).GetInfo().GetMethod("MoveNext")); diff --git a/ExpressMapper NET40/DestinationTypeMapper.cs b/Expressmapper.Shared/DestinationTypeMapper.cs similarity index 100% rename from ExpressMapper NET40/DestinationTypeMapper.cs rename to Expressmapper.Shared/DestinationTypeMapper.cs diff --git a/Expressmapper.Shared/Expressmapper.Shared.projitems b/Expressmapper.Shared/Expressmapper.Shared.projitems new file mode 100644 index 0000000..dc225d7 --- /dev/null +++ b/Expressmapper.Shared/Expressmapper.Shared.projitems @@ -0,0 +1,51 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 75c7dd14-fd53-462d-9fb3-aa5e31380b55 + + + Expressmapper.Shared + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + Code + + + + + + + + + + + + \ No newline at end of file diff --git a/Expressmapper.Shared/Expressmapper.Shared.shproj b/Expressmapper.Shared/Expressmapper.Shared.shproj new file mode 100644 index 0000000..a94bd46 --- /dev/null +++ b/Expressmapper.Shared/Expressmapper.Shared.shproj @@ -0,0 +1,13 @@ + + + + 75c7dd14-fd53-462d-9fb3-aa5e31380b55 + 14.0 + + + + + + + + \ No newline at end of file diff --git a/ExpressMapper NET40/ExpressmapperException.cs b/Expressmapper.Shared/ExpressmapperException.cs similarity index 100% rename from ExpressMapper NET40/ExpressmapperException.cs rename to Expressmapper.Shared/ExpressmapperException.cs diff --git a/ExpressMapper NET40/ExpressmapperExtensions.cs b/Expressmapper.Shared/ExpressmapperExtensions.cs similarity index 100% rename from ExpressMapper NET40/ExpressmapperExtensions.cs rename to Expressmapper.Shared/ExpressmapperExtensions.cs diff --git a/ExpressMapper NET40/FlattenLinqMethod.cs b/Expressmapper.Shared/FlattenLinqMethod.cs similarity index 100% rename from ExpressMapper NET40/FlattenLinqMethod.cs rename to Expressmapper.Shared/FlattenLinqMethod.cs diff --git a/ExpressMapper NET40/FlattenMapper.cs b/Expressmapper.Shared/FlattenMapper.cs similarity index 100% rename from ExpressMapper NET40/FlattenMapper.cs rename to Expressmapper.Shared/FlattenMapper.cs diff --git a/ExpressMapper NET40/FlattenMemberInfo.cs b/Expressmapper.Shared/FlattenMemberInfo.cs similarity index 100% rename from ExpressMapper NET40/FlattenMemberInfo.cs rename to Expressmapper.Shared/FlattenMemberInfo.cs diff --git a/ExpressMapper NET40/ICustomTypeMapper.cs b/Expressmapper.Shared/ICustomTypeMapper.cs similarity index 100% rename from ExpressMapper NET40/ICustomTypeMapper.cs rename to Expressmapper.Shared/ICustomTypeMapper.cs diff --git a/ExpressMapper NET40/IMappingContext.cs b/Expressmapper.Shared/IMappingContext.cs similarity index 100% rename from ExpressMapper NET40/IMappingContext.cs rename to Expressmapper.Shared/IMappingContext.cs diff --git a/ExpressMapper NET40/IMappingService.cs b/Expressmapper.Shared/IMappingService.cs similarity index 97% rename from ExpressMapper NET40/IMappingService.cs rename to Expressmapper.Shared/IMappingService.cs index e796f7b..a5cc885 100644 --- a/ExpressMapper NET40/IMappingService.cs +++ b/Expressmapper.Shared/IMappingService.cs @@ -1,22 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace ExpressMapper -{ - public interface IMappingService - { - IDictionary TypeMappers { get; } - IDictionary CollectionMappers { get; } - void PrecompileCollection(); - bool DestinationSupport { get; } - MulticastDelegate MapCollection(long cacheKey); - void Reset(); - BlockExpression MapCollection(Type srcColtype, Type destColType, Expression srcExpression, Expression destExpression); - Expression GetDifferentTypeMemberMappingExpression(Expression srcExpression, Expression destExpression, bool newDest); - BlockExpression MapProperty(Type srcType, Type destType, Expression srcExpression, Expression destExpression, bool newDest); - Expression GetMemberMappingExpression(Expression left, Expression right, bool newDest); - Expression GetMemberQueryableExpression(Type srcType, Type dstType); - void Compile(CompilationTypes compilationType); - } -} +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace ExpressMapper +{ + public interface IMappingService + { + IDictionary TypeMappers { get; } + IDictionary CollectionMappers { get; } + void PrecompileCollection(); + bool DestinationSupport { get; } + MulticastDelegate MapCollection(long cacheKey); + void Reset(); + BlockExpression MapCollection(Type srcColtype, Type destColType, Expression srcExpression, Expression destExpression); + Expression GetDifferentTypeMemberMappingExpression(Expression srcExpression, Expression destExpression, bool newDest); + BlockExpression MapProperty(Type srcType, Type destType, Expression srcExpression, Expression destExpression, bool newDest); + Expression GetMemberMappingExpression(Expression left, Expression right, bool newDest); + Expression GetMemberQueryableExpression(Type srcType, Type dstType); + void Compile(CompilationTypes compilationType); + } +} diff --git a/ExpressMapper NET40/IMappingServiceProvider.cs b/Expressmapper.Shared/IMappingServiceProvider.cs similarity index 88% rename from ExpressMapper NET40/IMappingServiceProvider.cs rename to Expressmapper.Shared/IMappingServiceProvider.cs index f02847e..b797e95 100644 --- a/ExpressMapper NET40/IMappingServiceProvider.cs +++ b/Expressmapper.Shared/IMappingServiceProvider.cs @@ -17,12 +17,14 @@ public interface IMappingServiceProvider object Map(Type srcType, Type dstType, object src); object Map(Type srcType, Type dstType, object src, object dest); IMemberConfiguration Register(); + IMemberConfiguration Register(IMemberConfigParameters baseType); bool MapExists(Type sourceType, Type destinationType); void RegisterCustom() where TMapper : ICustomTypeMapper; void RegisterCustom(Func mapFunc); void Reset(); long CalculateCacheKey(Type src, Type dest); Dictionary> CustomMappers { get; } + Dictionary> CustomMappingsBySource { get; } IQueryable Project(IQueryable source); bool CaseSensetiveMemberMap { get; set; } } diff --git a/Expressmapper.Shared/IMemberConfigParameters.cs b/Expressmapper.Shared/IMemberConfigParameters.cs new file mode 100644 index 0000000..14edfd5 --- /dev/null +++ b/Expressmapper.Shared/IMemberConfigParameters.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace ExpressMapper +{ + public interface IMemberConfigParameters + { + List> CustomMembers { get; } + List> FlattenMembers { get; } + List> CustomFunctionMembers { get; } + List IgnoreMemberList { get; } + bool Flattened { get; set; } + bool CaseSensetiveMember { get; set; } + bool CaseSensetiveOverride { get; set; } + CompilationTypes CompilationTypeMember { get; set; } + bool CompilationTypeOverride { get; set; } + } +} diff --git a/ExpressMapper NET40/IMemberConfiguration.cs b/Expressmapper.Shared/IMemberConfiguration.cs similarity index 89% rename from ExpressMapper NET40/IMemberConfiguration.cs rename to Expressmapper.Shared/IMemberConfiguration.cs index 5a40117..0fed812 100644 --- a/ExpressMapper NET40/IMemberConfiguration.cs +++ b/Expressmapper.Shared/IMemberConfiguration.cs @@ -1,25 +1,28 @@ -using System; -using System.Linq.Expressions; - -namespace ExpressMapper -{ - /// - /// Interface to extend the mapping - /// - /// source - /// destination - public interface IMemberConfiguration - { - IMemberConfiguration InstantiateFunc(Func constructor); - IMemberConfiguration Instantiate(Expression> constructor); - IMemberConfiguration Before(Action beforeHandler); - IMemberConfiguration After(Action afterHandler); - IMemberConfiguration Member(Expression> dest, Expression> src); - IMemberConfiguration Function(Expression> dest, Func src); - IMemberConfiguration Ignore(Expression> dest); - IMemberConfiguration Value(Expression> dest, TNMember value); - IMemberConfiguration CaseSensitive(bool caseSensitive); - IMemberConfiguration CompileTo(CompilationTypes compilationType); - IMemberConfiguration Flatten(); - } -} +using System; +using System.Linq.Expressions; + +namespace ExpressMapper +{ + /// + /// Interface to extend the mapping + /// + /// source + /// destination + public interface IMemberConfiguration + { + IMemberConfiguration InstantiateFunc(Func constructor); + IMemberConfiguration Instantiate(Expression> constructor); + IMemberConfiguration Before(Action beforeHandler); + IMemberConfiguration After(Action afterHandler); + IMemberConfiguration Member(Expression> dest, Expression> src); + IMemberConfiguration Function(Expression> dest, Func src); + IMemberConfiguration Ignore(Expression> dest); + IMemberConfiguration Value(Expression> dest, TNMember value); + IMemberConfiguration CaseSensitive(bool caseSensitive); + IMemberConfiguration CompileTo(CompilationTypes compilationType); + IMemberConfiguration Flatten(); + + IMemberConfiguration Include() where TSub : T + where TNSub : TN; + } +} diff --git a/ExpressMapper NET40/ITypeMapper.cs b/Expressmapper.Shared/ITypeMapper.cs similarity index 85% rename from ExpressMapper NET40/ITypeMapper.cs rename to Expressmapper.Shared/ITypeMapper.cs index a24247f..c2768fe 100644 --- a/ExpressMapper NET40/ITypeMapper.cs +++ b/Expressmapper.Shared/ITypeMapper.cs @@ -7,6 +7,9 @@ namespace ExpressMapper { public interface ITypeMapper { + bool BaseType { get; set; } + Type SourceType { get; } + Type DestinationType { get; } Expression QueryableGeneralExpression { get; } Func GetNonGenericMapFunc(); Tuple, ParameterExpression, ParameterExpression> GetMapExpressions(); @@ -20,6 +23,7 @@ public interface ITypeMapper /// destination public interface ITypeMapper : ITypeMapper { + IMemberConfiguration MemberConfiguration { get; set; } Expression> QueryableExpression { get; } TN MapTo(T src, TN dest); void Ignore(Expression> left); @@ -35,5 +39,6 @@ public interface ITypeMapper : ITypeMapper void AfterMap(Action afterMap); void Flatten(); CompilationTypes MapperType { get; } + void ImportMemberConfigParameters(IMemberConfigParameters baseClassConfiguration); } } diff --git a/ExpressMapper NET40/MapNotImplemented.cs b/Expressmapper.Shared/MapNotImplemented.cs similarity index 96% rename from ExpressMapper NET40/MapNotImplemented.cs rename to Expressmapper.Shared/MapNotImplemented.cs index 116cecc..d29cfc4 100644 --- a/ExpressMapper NET40/MapNotImplemented.cs +++ b/Expressmapper.Shared/MapNotImplemented.cs @@ -1,31 +1,31 @@ -using System; - -namespace ExpressMapper -{ - /// - /// Mapping not implemented exception - /// - /// Serializable turned off - PCL support - //[Serializable] - public class MapNotImplementedException : Exception - { - public MapNotImplementedException() - { - } - public MapNotImplementedException(string message) - : base(message) - { - } - - public MapNotImplementedException(string message, Exception inner) - : base(message, inner) - { - } - - // Serializable turned off - PCL support - //protected MapNotImplementedException(SerializationInfo info, StreamingContext context) - // : base(info, context) - //{ - //} - } -} +using System; + +namespace ExpressMapper +{ + /// + /// Mapping not implemented exception + /// + /// Serializable turned off - PCL support + //[Serializable] + public class MapNotImplementedException : Exception + { + public MapNotImplementedException() + { + } + public MapNotImplementedException(string message) + : base(message) + { + } + + public MapNotImplementedException(string message, Exception inner) + : base(message, inner) + { + } + + // Serializable turned off - PCL support + //protected MapNotImplementedException(SerializationInfo info, StreamingContext context) + // : base(info, context) + //{ + //} + } +} diff --git a/ExpressMapper NET40/Mapper.cs b/Expressmapper.Shared/Mapper.cs similarity index 96% rename from ExpressMapper NET40/Mapper.cs rename to Expressmapper.Shared/Mapper.cs index 6621282..0c7ee2f 100644 --- a/ExpressMapper NET40/Mapper.cs +++ b/Expressmapper.Shared/Mapper.cs @@ -1,97 +1,97 @@ -using System; -using System.Linq; - -namespace ExpressMapper -{ - public static class Mapper - { - private static IMappingServiceProvider _instance; - - // todo: via Internal DependencyResolver - public static IMappingServiceProvider Instance - { - get { return _instance ?? (_instance = new MappingServiceProvider()); } - } - - public static void Compile() - { - Instance.Compile(); - } - - public static void Compile(CompilationTypes compilationType) - { - Instance.Compile(compilationType); - } - - public static void PrecompileCollection() - { - Instance.PrecompileCollection(); - } - - public static void PrecompileCollection(CompilationTypes compilationType) - { - Instance.PrecompileCollection(compilationType); - } - - public static TN Map(T src) - { - return Instance.Map(src); - } - - public static TN Map(T src, TN dest) - { - return Instance.Map(src, dest); - } - - internal static IQueryable Project(IQueryable source) - { - return Instance.Project(source); - } - - public static TN Map(T src, ICustomTypeMapper customMapper, TN dest = default(TN)) - { - return Instance.Map(src, customMapper, dest); - } - - public static object Map(object src, Type srcType, Type dstType) - { - return Instance.Map(srcType, dstType, src); - } - - public static object Map(object src, object dest, Type srcType, Type dstType) - { - return Instance.Map(srcType, dstType, src, dest); - } - - public static IMemberConfiguration Register() - { - return Instance.Register(); - } - - public static bool MapExists(Type sourceType, Type destinationType) - { - return Instance.MapExists(sourceType, destinationType); - } - - public static void RegisterCustom() - where TMapper : ICustomTypeMapper - { - Instance.RegisterCustom(); - } - - public static void RegisterCustom(Func mapFunc) - { - Instance.RegisterCustom(mapFunc); - } - - public static void Reset() - { - Instance.Reset(); - } - - public static void MemberCaseSensitiveMap(bool caseSensitive) - { - Instance.CaseSensetiveMemberMap = caseSensitive; - } - } -} +using System; +using System.Linq; + +namespace ExpressMapper +{ + public static class Mapper + { + private static IMappingServiceProvider _instance; + + // todo: via Internal DependencyResolver + public static IMappingServiceProvider Instance + { + get { return _instance ?? (_instance = new MappingServiceProvider()); } + } + + public static void Compile() + { + Instance.Compile(); + } + + public static void Compile(CompilationTypes compilationType) + { + Instance.Compile(compilationType); + } + + public static void PrecompileCollection() + { + Instance.PrecompileCollection(); + } + + public static void PrecompileCollection(CompilationTypes compilationType) + { + Instance.PrecompileCollection(compilationType); + } + + public static TN Map(T src) + { + return Instance.Map(src); + } + + public static TN Map(T src, TN dest) + { + return Instance.Map(src, dest); + } + + internal static IQueryable Project(IQueryable source) + { + return Instance.Project(source); + } + + public static TN Map(T src, ICustomTypeMapper customMapper, TN dest = default(TN)) + { + return Instance.Map(src, customMapper, dest); + } + + public static object Map(object src, Type srcType, Type dstType) + { + return Instance.Map(srcType, dstType, src); + } + + public static object Map(object src, object dest, Type srcType, Type dstType) + { + return Instance.Map(srcType, dstType, src, dest); + } + + public static IMemberConfiguration Register() + { + return Instance.Register(); + } + + public static bool MapExists(Type sourceType, Type destinationType) + { + return Instance.MapExists(sourceType, destinationType); + } + + public static void RegisterCustom() + where TMapper : ICustomTypeMapper + { + Instance.RegisterCustom(); + } + + public static void RegisterCustom(Func mapFunc) + { + Instance.RegisterCustom(mapFunc); + } + + public static void Reset() + { + Instance.Reset(); + } + + public static void MemberCaseSensitiveMap(bool caseSensitive) + { + Instance.CaseSensetiveMemberMap = caseSensitive; + } + } +} diff --git a/ExpressMapper NET40/MappingServiceBase.cs b/Expressmapper.Shared/MappingServiceBase.cs similarity index 97% rename from ExpressMapper NET40/MappingServiceBase.cs rename to Expressmapper.Shared/MappingServiceBase.cs index a469458..ba2ac10 100644 --- a/ExpressMapper NET40/MappingServiceBase.cs +++ b/Expressmapper.Shared/MappingServiceBase.cs @@ -1,471 +1,472 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace ExpressMapper -{ - internal abstract class MappingServiceBase - { - private readonly Type _enumImplicitConversionType = typeof(int); - protected readonly Dictionary CustomTypeMapperExpCache = new Dictionary(); - public IDictionary TypeMappers { get; set; } - protected IMappingServiceProvider MappingServiceProvider { get; private set; } - internal MappingServiceBase(IMappingServiceProvider mappingServiceProvider) - { - MappingServiceProvider = mappingServiceProvider; - TypeMappers = new Dictionary(); - } - - protected static readonly Type GenericEnumerableType = typeof(IEnumerable<>); - public abstract IDictionary CollectionMappers { get; } - public abstract void PrecompileCollection(); - public abstract bool DestinationSupport { get; } - public abstract MulticastDelegate MapCollection(long cacheKey); - public void Reset() - { - CollectionMappers.Clear(); - TypeMappers.Clear(); - } - - public void Compile(CompilationTypes compilationType) - { - var typeMappers = new Dictionary(TypeMappers); - foreach (var typeMapper in typeMappers) - { - typeMapper.Value.Compile(compilationType); - } - } - - protected BlockExpression GetCustomMapExpression(Type src, Type dest) - { - var cacheKey = MappingServiceProvider.CalculateCacheKey(src, dest); - if (!MappingServiceProvider.CustomMappers.ContainsKey(cacheKey)) return null; - CompileGenericCustomTypeMapper(src, dest, MappingServiceProvider.CustomMappers[cacheKey](), cacheKey); - return CustomTypeMapperExpCache[cacheKey]; - } - - protected Tuple, ParameterExpression, ParameterExpression> GetMapExpressions(Type src, Type dest) - { - var cacheKey = MappingServiceProvider.CalculateCacheKey(src, dest); - if (TypeMappers.ContainsKey(cacheKey)) - { - return TypeMappers[cacheKey].GetMapExpressions(); - } - - dynamic srcInst = Activator.CreateInstance(src); - dynamic destInst = Activator.CreateInstance(dest); - RegisterDynamic(srcInst, destInst); - if (TypeMappers.ContainsKey(cacheKey)) - { - return TypeMappers[cacheKey].GetMapExpressions(); - } - - throw new MapNotImplementedException(string.Format("There is no mapping has been found. Source Type: {0}, Destination Type: {1}", src.FullName, dest.FullName)); - } - - private void RegisterDynamic(T src, TN dest) - { - MappingServiceProvider.Register(); - } - - protected void CompileGenericCustomTypeMapper(Type srcType, Type dstType, ICustomTypeMapper typeMapper, long cacheKey) - { - if (CustomTypeMapperExpCache.ContainsKey(cacheKey)) return; - - var srcTypedExp = Expression.Variable(srcType, "srcTyped"); - var dstTypedExp = Expression.Variable(dstType, "dstTyped"); - - var customGenericType = typeof(ICustomTypeMapper<,>).MakeGenericType(srcType, dstType); - var castToCustomGeneric = Expression.Convert( - Expression.Constant(typeMapper, typeof(ICustomTypeMapper)), - customGenericType); - var genVariable = Expression.Variable(customGenericType); - var assignExp = Expression.Assign(genVariable, castToCustomGeneric); - var methodInfo = customGenericType.GetInfo().GetMethod("Map"); - var genericMappingContext = typeof(DefaultMappingContext<,>).MakeGenericType(srcType, dstType); - var newMappingContextExp = Expression.New(genericMappingContext); - var contextVarExp = Expression.Variable(genericMappingContext, string.Format("context{0}", Guid.NewGuid())); - var assignContextExp = Expression.Assign(contextVarExp, newMappingContextExp); - - var sourceExp = Expression.Property(contextVarExp, "Source"); - var sourceAssignedExp = Expression.Assign(sourceExp, srcTypedExp); - var destExp = Expression.Property(contextVarExp, "Destination"); - var destAssignedExp = Expression.Assign(destExp, dstTypedExp); - - var mapCall = Expression.Call(genVariable, methodInfo, contextVarExp); - //var resultVarExp = Expression.Variable(dstType, "result"); - //var resultAssignExp = Expression.Assign(resultVarExp, mapCall); - var resultAssignExp = Expression.Assign(dstTypedExp, mapCall); - - var blockExpression = Expression.Block(new[] { genVariable, contextVarExp }, assignExp, assignContextExp, sourceAssignedExp, destAssignedExp, resultAssignExp); - - CustomTypeMapperExpCache[cacheKey] = Expression.Block(new ParameterExpression[] { }, blockExpression); - } - - protected virtual bool ComplexMapCondition(Type src, Type dst) - { - return src != dst; - } - - public Expression GetMemberMappingExpression(Expression left, Expression right, bool newDest) - { - var nullCheckNestedMemberVisitor = new NullCheckNestedMemberVisitor(false); - nullCheckNestedMemberVisitor.Visit(right); - - var destNullableType = Nullable.GetUnderlyingType(left.Type); - var sourceNullableType = Nullable.GetUnderlyingType(right.Type); - - var destType = destNullableType ?? left.Type; - var sourceType = sourceNullableType ?? right.Type; - - if (ComplexMapCondition(sourceType, destType)) - { - var customMapExpression = GetCustomMapExpression(right.Type, left.Type); - if (customMapExpression != null) - { - var srcExp = Expression.Variable(right.Type, - string.Format("{0}Src", Guid.NewGuid().ToString("N"))); - var assignSrcExp = Expression.Assign(srcExp, right); - - var destExp = Expression.Variable(left.Type, - string.Format("{0}Dst", Guid.NewGuid().ToString("N"))); - var assignDestExp = Expression.Assign(destExp, left); - - // try precise substitute visitor - var substituteParameterVisitor = - new PreciseSubstituteParameterVisitor( - new KeyValuePair( - Expression.Variable(right.Type, "srcTyped"), srcExp), - new KeyValuePair( - Expression.Variable(left.Type, "dstTyped"), destExp)); - - var blockExpression = substituteParameterVisitor.Visit(customMapExpression) as BlockExpression; - - var assignResultExp = Expression.Assign(left, destExp); - var resultBlockExp = Expression.Block(new[] { srcExp, destExp }, assignSrcExp, assignDestExp, blockExpression, assignResultExp); - - var checkNullExp = - Expression.IfThenElse(Expression.Equal(right, Expression.Default(right.Type)), - Expression.Assign(left, Expression.Default(left.Type)), resultBlockExp); - - var releaseExp = Expression.Block(new ParameterExpression[] { }, (right.Type.GetInfo().IsPrimitive || right.Type.GetInfo().IsValueType ? resultBlockExp : (Expression)checkNullExp)); - - return releaseExp; - } - - var returnTypeDifferenceVisitor = new ReturnTypeDifferenceVisitor(right); - returnTypeDifferenceVisitor.Visit(right); - - // If right is custom member expression / func and return type matches left type - // just assign - if (left.Type == right.Type && returnTypeDifferenceVisitor.DifferentReturnTypes) - { - return Expression.Assign(left, right); - } - - if (destType.GetInfo().IsEnum && sourceType.GetInfo().IsEnum) - { - return Expression.Assign(left, Expression.Convert(Expression.Convert(right, _enumImplicitConversionType), destType)); - } - - if (typeof(IConvertible).GetInfo().IsAssignableFrom(destType) && - typeof(IConvertible).GetInfo().IsAssignableFrom(sourceType)) - { - var assignExp = CreateConvertibleAssignExpression(left, - right, - left.Type, - sourceType, - destNullableType); - - return assignExp; - } - var mapComplexResult = GetDifferentTypeMemberMappingExpression(right, left, newDest); - - // return nullCheckNestedMemberVisitor.CheckNullExpression != null - // ? Expression.Condition(nullCheckNestedMemberVisitor.CheckNullExpression, - // Expression.Assign(left, Expression.Default(left.Type)), - // mapComplexResult) - return mapComplexResult; - } - var binaryExpression = CreateAssignExpression(left, - right, - left.Type, - destNullableType, - sourceNullableType); - - var conditionalExpression = nullCheckNestedMemberVisitor.CheckNullExpression != null ? Expression.Condition(nullCheckNestedMemberVisitor.CheckNullExpression, Expression.Assign(left, Expression.Default(left.Type)), binaryExpression) : (Expression)binaryExpression; - - return conditionalExpression; - } - - private static Expression CreateAssignExpression(Expression setMethod, Expression getMethod, Type setType, Type setNullableType, Type getNullableType) - { - var left = setMethod; - var right = getMethod; - - if (setNullableType == null && getNullableType != null) - { - // Nullable to non nullable map - // Type.EmptyTypes is not being used - PCL support - right = Expression.Call(getMethod, "GetValueOrDefault", /*Type.EmptyTypes*/null); - } - else if (setNullableType != null && getNullableType == null) - { - // Non nullable to nullable map - right = Expression.Convert(getMethod, setType); - } - - return Expression.Condition(Expression.NotEqual(left, right), Expression.Assign(left, right), Expression.Default(setType)); - } - - private static Expression CreateConvertibleAssignExpression(Expression setMethod, Expression getMethod, Type setType, Type getType, Type setNullableType) - { - var left = setMethod; - var right = getMethod; - - if ((setNullableType ?? setType).GetInfo().IsEnum && (getType == typeof(string))) - { - return Expression.IfThen( - Expression.NotEqual(getMethod, StaticExpressions.NullConstant), - Expression.Assign(left, - Expression.Convert( - Expression.Call(typeof(Enum).GetInfo().GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }), Expression.Constant(setNullableType ?? setType), right, Expression.Constant(true)), - setType))); - } - return Expression.IfThen( - Expression.NotEqual(Expression.Convert(getMethod, typeof(object)), StaticExpressions.NullConstant), - Expression.Assign(left, - Expression.Convert( - Expression.Call(typeof(Convert).GetInfo().GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), Expression.Convert(right, typeof(object)), Expression.Constant(setNullableType ?? setType)), - setType))); - } - - public virtual BlockExpression MapCollection(Type srcColtype, Type destColType, Expression srcExpression, - Expression destExpression) - { - var sourceType = GetCollectionElementType(srcColtype); - var destType = GetCollectionElementType(destColType); - var sourceVariable = Expression.Variable(srcExpression.Type, - string.Format("{0}Src", Guid.NewGuid().ToString().Replace("-", "_"))); - var assignSourceFromProp = Expression.Assign(sourceVariable, srcExpression); - - var destList = typeof(List<>).MakeGenericType(destType); - var destColl = Expression.Variable(destList, string.Format("{0}Dst", Guid.NewGuid().ToString().Replace("-", "_"))); - - var newColl = Expression.New(destList); - var destAssign = Expression.Assign(destColl, newColl); - - var closedEnumeratorSourceType = typeof(IEnumerator<>).MakeGenericType(sourceType); - var closedEnumerableSourceType = GenericEnumerableType.MakeGenericType(sourceType); - var enumerator = Expression.Variable(closedEnumeratorSourceType, - string.Format("{0}Enum", Guid.NewGuid().ToString().Replace("-", "_"))); - var assignToEnum = Expression.Assign(enumerator, - Expression.Call(sourceVariable, closedEnumerableSourceType.GetInfo().GetMethod("GetEnumerator"))); - var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetInfo().GetMethod("MoveNext")); - - var current = Expression.Property(enumerator, "Current"); - var sourceColItmVariable = Expression.Variable(sourceType, - string.Format("{0}ItmSrc", Guid.NewGuid().ToString().Replace("-", "_"))); - var assignSourceItmFromProp = Expression.Assign(sourceColItmVariable, current); - - var destColItmVariable = Expression.Variable(destType, - string.Format("{0}ItmDst", Guid.NewGuid().ToString().Replace("-", "_"))); - - var loopExpression = CollectionLoopExpression(destColl, sourceColItmVariable, destColItmVariable, - assignSourceItmFromProp, doMoveNext); - - var resultCollection = ConvertCollection(destExpression.Type, destList, destType, destColl); - - var assignResult = Expression.Assign(destExpression, resultCollection); - - var parameters = new List { sourceVariable, destColl, enumerator }; - var expressions = new List - { - assignSourceFromProp, - destAssign, - assignToEnum, - loopExpression, - assignResult - }; - - var blockExpression = Expression.Block(parameters, expressions); - - var checkSrcForNullExp = - Expression.IfThenElse(Expression.Equal(srcExpression, StaticExpressions.NullConstant), - Expression.Assign(destExpression, Expression.Default(destExpression.Type)), blockExpression); - var blockResultExp = Expression.Block(new ParameterExpression[] { }, new Expression[] { checkSrcForNullExp }); - - return blockResultExp; - } - - public Expression GetDifferentTypeMemberMappingExpression(Expression srcExpression, Expression destExpression, bool newDest) - { - var sourceType = srcExpression.Type; - var destType = destExpression.Type; - - var tCol = GetEnumerableInterface(sourceType); - var tnCol = GetEnumerableInterface(destType); - - var blockExpression = (tCol != null && tnCol != null) - ? MapCollection(tCol, tnCol, srcExpression, destExpression) - : MapProperty(sourceType, destType, srcExpression, destExpression, newDest); - - var refSrcType = sourceType.GetInfo().IsClass; - var destPropType = destType; - - if (!refSrcType) return blockExpression; - - var resultExpression = - Expression.IfThenElse(Expression.Equal(srcExpression, StaticExpressions.NullConstant), - Expression.Assign(destExpression, Expression.Default(destPropType)), - blockExpression); - return resultExpression; - } - - public abstract BlockExpression MapProperty(Type srcType, Type destType, Expression srcExpression, Expression destExpression, bool newDest); - - protected BlockExpression CompileCollectionInternal(ParameterExpression sourceParameterExp, ParameterExpression destParameterExp = null) - { - var sourceType = GetCollectionElementType(typeof(T)); - var destType = GetCollectionElementType(typeof(TN)); - - var destList = typeof(List<>).MakeGenericType(destType); - var destColl = Expression.Variable(destList, "destColl"); - - var newColl = Expression.New(destList); - var destAssign = Expression.Assign(destColl, newColl); - - var closedEnumeratorSourceType = typeof(IEnumerator<>).MakeGenericType(sourceType); - var closedEnumerableSourceType = GenericEnumerableType.MakeGenericType(sourceType); - var enumerator = Expression.Variable(closedEnumeratorSourceType, "srcEnumerator"); - var assignToEnum = Expression.Assign(enumerator, - Expression.Call(sourceParameterExp, closedEnumerableSourceType.GetInfo().GetMethod("GetEnumerator"))); - var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetInfo().GetMethod("MoveNext")); - - var current = Expression.Property(enumerator, "Current"); - var sourceColItmVariable = Expression.Variable(sourceType, "ItmSrc"); - var assignSourceItmFromProp = Expression.Assign(sourceColItmVariable, current); - - var destColItmVariable = Expression.Variable(destType, "ItmDst"); - - var loopExpression = CollectionLoopExpression(destColl, sourceColItmVariable, destColItmVariable, - assignSourceItmFromProp, doMoveNext); - - var resultCollection = ConvertCollection(typeof(TN), destList, destType, destColl); - - var parameters = new List { destColl, enumerator }; - - var expressions = new List - { - destAssign, - assignToEnum, - loopExpression, - destParameterExp != null - ? Expression.Assign(destParameterExp, resultCollection) - : resultCollection - }; - - - var blockExpression = Expression.Block(parameters, expressions); - return blockExpression; - } - - internal static Type GetCollectionElementType(Type type) - { - return type.IsArray ? type.GetElementType() : GetEnumerableInterface(type).GetInfo().GetGenericArguments()[0]; - } - - internal LoopExpression CollectionLoopExpression( - ParameterExpression destColl, - ParameterExpression sourceColItmVariable, - ParameterExpression destColItmVariable, - BinaryExpression assignSourceItmFromProp, - MethodCallExpression doMoveNext) - { - var mapExprForType = GetMemberMappingExpression(destColItmVariable, sourceColItmVariable, true); - - var addToNewColl = Expression.Call(destColl, "Add", null, destColItmVariable); - - var ifTrueBlock = Expression.Block(new[] { sourceColItmVariable, destColItmVariable }, new[] { assignSourceItmFromProp, mapExprForType, addToNewColl }); - - var loopExpression = CreateLoopExpression(doMoveNext, ifTrueBlock); - - return loopExpression; - } - - protected LoopExpression CreateLoopExpression(Expression doMoveNextSrc, BlockExpression innerLoopBlock) - { - var brk = Expression.Label(); - var loopExpression = Expression.Loop( - Expression.IfThenElse(Expression.NotEqual(doMoveNextSrc, StaticExpressions.FalseConstant), - innerLoopBlock - , Expression.Break(brk)) - , brk); - return loopExpression; - } - - internal static Expression ConvertCollection(Type destPropType, - Type destList, - Type destType, - Expression destColl) - { - if (destPropType.IsArray) - { - return Expression.Call(destColl, destList.GetInfo().GetMethod("ToArray")); - } - - if (!destPropType.GetInfo().IsGenericType && GetEnumerableInterface(destPropType) == null) - { - return destColl; - } - - if (typeof(IQueryable).GetInfo().IsAssignableFrom(destPropType)) - { - return Expression.Call(typeof(Queryable), "AsQueryable", new[] { destType }, destColl); - } - - if (destPropType.GetInfo().IsInterface && destPropType.GetInfo().IsAssignableFrom(destColl.Type)) - { - // This will handle any destination interface implemented by List - return destColl; - } - - ConstructorInfo ctor = null; - - if (destPropType.GetInfo().IsInterface) - { - // We are targeting an interface type, we need to find a compatible collection type - // We could look for a loaded type that implements the target interface and has an appropriate - // constructor, but that is a bit too much magic for now. - throw new NotImplementedException( - $"Destination interface type {destPropType.FullName} is not supported yet"); - } - else - { - ctor = destPropType.GetInfo().GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault( - ci => { - var param = ci.GetParameters(); - return param.Length == 1 && param[0].ParameterType.GetInfo().IsAssignableFrom(destList); - }); - - if (ctor == null) - { - throw new Exception($"Could not find a constructor on {destPropType.Name} that accepts {destList}"); - } - } - - return Expression.New(ctor, destColl); - } - - public static Type GetEnumerableInterface(Type type) - { - return - type.GetInfo().GetInterfaces() - .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) - ?? (type.GetInfo().IsGenericType && type.GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? type : null); - } - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace ExpressMapper +{ + internal abstract class MappingServiceBase + { + private readonly Type _enumImplicitConversionType = typeof(int); + protected readonly Dictionary CustomTypeMapperExpCache = new Dictionary(); + public IDictionary TypeMappers { get; set; } + protected IMappingServiceProvider MappingServiceProvider { get; private set; } + internal MappingServiceBase(IMappingServiceProvider mappingServiceProvider) + { + MappingServiceProvider = mappingServiceProvider; + TypeMappers = new Dictionary(); + } + + protected static readonly Type GenericEnumerableType = typeof(IEnumerable<>); + public abstract IDictionary CollectionMappers { get; } + public abstract void PrecompileCollection(); + public abstract bool DestinationSupport { get; } + public abstract MulticastDelegate MapCollection(long cacheKey); + public void Reset() + { + CollectionMappers.Clear(); + TypeMappers.Clear(); + } + + public void Compile(CompilationTypes compilationType) + { + var typeMappers = new Dictionary(TypeMappers); + foreach (var typeMapper in typeMappers) + { + typeMapper.Value.Compile(compilationType); + } + } + + protected BlockExpression GetCustomMapExpression(Type src, Type dest) + { + var cacheKey = MappingServiceProvider.CalculateCacheKey(src, dest); + if (!MappingServiceProvider.CustomMappers.ContainsKey(cacheKey)) return null; + CompileGenericCustomTypeMapper(src, dest, MappingServiceProvider.CustomMappers[cacheKey](), cacheKey); + return CustomTypeMapperExpCache[cacheKey]; + } + + protected Tuple, ParameterExpression, ParameterExpression> GetMapExpressions(Type src, Type dest) + { + var cacheKey = MappingServiceProvider.CalculateCacheKey(src, dest); + if (TypeMappers.ContainsKey(cacheKey)) + { + return TypeMappers[cacheKey].GetMapExpressions(); + } + + dynamic srcInst = Activator.CreateInstance(src); + dynamic destInst = Activator.CreateInstance(dest); + RegisterDynamic(srcInst, destInst); + if (TypeMappers.ContainsKey(cacheKey)) + { + return TypeMappers[cacheKey].GetMapExpressions(); + } + + throw new MapNotImplementedException( + $"There is no mapping has been found. Source Type: {src.FullName}, Destination Type: {dest.FullName}"); + } + + private void RegisterDynamic(T src, TN dest) + { + MappingServiceProvider.Register(); + } + + protected void CompileGenericCustomTypeMapper(Type srcType, Type dstType, ICustomTypeMapper typeMapper, long cacheKey) + { + if (CustomTypeMapperExpCache.ContainsKey(cacheKey)) return; + + var srcTypedExp = Expression.Variable(srcType, "srcTyped"); + var dstTypedExp = Expression.Variable(dstType, "dstTyped"); + + var customGenericType = typeof(ICustomTypeMapper<,>).MakeGenericType(srcType, dstType); + var castToCustomGeneric = Expression.Convert( + Expression.Constant(typeMapper, typeof(ICustomTypeMapper)), + customGenericType); + var genVariable = Expression.Variable(customGenericType); + var assignExp = Expression.Assign(genVariable, castToCustomGeneric); + var methodInfo = customGenericType.GetInfo().GetMethod("Map"); + var genericMappingContext = typeof(DefaultMappingContext<,>).MakeGenericType(srcType, dstType); + var newMappingContextExp = Expression.New(genericMappingContext); + var contextVarExp = Expression.Variable(genericMappingContext, string.Format("context{0}", Guid.NewGuid())); + var assignContextExp = Expression.Assign(contextVarExp, newMappingContextExp); + + var sourceExp = Expression.Property(contextVarExp, "Source"); + var sourceAssignedExp = Expression.Assign(sourceExp, srcTypedExp); + var destExp = Expression.Property(contextVarExp, "Destination"); + var destAssignedExp = Expression.Assign(destExp, dstTypedExp); + + var mapCall = Expression.Call(genVariable, methodInfo, contextVarExp); + //var resultVarExp = Expression.Variable(dstType, "result"); + //var resultAssignExp = Expression.Assign(resultVarExp, mapCall); + var resultAssignExp = Expression.Assign(dstTypedExp, mapCall); + + var blockExpression = Expression.Block(new[] { genVariable, contextVarExp }, assignExp, assignContextExp, sourceAssignedExp, destAssignedExp, resultAssignExp); + + CustomTypeMapperExpCache[cacheKey] = Expression.Block(new ParameterExpression[] { }, blockExpression); + } + + protected virtual bool ComplexMapCondition(Type src, Type dst) + { + return src != dst; + } + + public Expression GetMemberMappingExpression(Expression left, Expression right, bool newDest) + { + var nullCheckNestedMemberVisitor = new NullCheckNestedMemberVisitor(false); + nullCheckNestedMemberVisitor.Visit(right); + + var destNullableType = Nullable.GetUnderlyingType(left.Type); + var sourceNullableType = Nullable.GetUnderlyingType(right.Type); + + var destType = destNullableType ?? left.Type; + var sourceType = sourceNullableType ?? right.Type; + + if (ComplexMapCondition(sourceType, destType)) + { + var customMapExpression = GetCustomMapExpression(right.Type, left.Type); + if (customMapExpression != null) + { + var srcExp = Expression.Variable(right.Type, + string.Format("{0}Src", Guid.NewGuid().ToString("N"))); + var assignSrcExp = Expression.Assign(srcExp, right); + + var destExp = Expression.Variable(left.Type, + string.Format("{0}Dst", Guid.NewGuid().ToString("N"))); + var assignDestExp = Expression.Assign(destExp, left); + + // try precise substitute visitor + var substituteParameterVisitor = + new PreciseSubstituteParameterVisitor( + new KeyValuePair( + Expression.Variable(right.Type, "srcTyped"), srcExp), + new KeyValuePair( + Expression.Variable(left.Type, "dstTyped"), destExp)); + + var blockExpression = substituteParameterVisitor.Visit(customMapExpression) as BlockExpression; + + var assignResultExp = Expression.Assign(left, destExp); + var resultBlockExp = Expression.Block(new[] { srcExp, destExp }, assignSrcExp, assignDestExp, blockExpression, assignResultExp); + + var checkNullExp = + Expression.IfThenElse(Expression.Equal(right, Expression.Default(right.Type)), + Expression.Assign(left, Expression.Default(left.Type)), resultBlockExp); + + var releaseExp = Expression.Block(new ParameterExpression[] { }, (right.Type.GetInfo().IsPrimitive || right.Type.GetInfo().IsValueType ? resultBlockExp : (Expression)checkNullExp)); + + return releaseExp; + } + + var returnTypeDifferenceVisitor = new ReturnTypeDifferenceVisitor(right); + returnTypeDifferenceVisitor.Visit(right); + + // If right is custom member expression / func and return type matches left type + // just assign + if (left.Type == right.Type && returnTypeDifferenceVisitor.DifferentReturnTypes) + { + return Expression.Assign(left, right); + } + + if (destType.GetInfo().IsEnum && sourceType.GetInfo().IsEnum) + { + return Expression.Assign(left, Expression.Convert(Expression.Convert(right, _enumImplicitConversionType), destType)); + } + + if (typeof(IConvertible).GetInfo().IsAssignableFrom(destType) && + typeof(IConvertible).GetInfo().IsAssignableFrom(sourceType)) + { + var assignExp = CreateConvertibleAssignExpression(left, + right, + left.Type, + sourceType, + destNullableType); + + return assignExp; + } + var mapComplexResult = GetDifferentTypeMemberMappingExpression(right, left, newDest); + + // return nullCheckNestedMemberVisitor.CheckNullExpression != null + // ? Expression.Condition(nullCheckNestedMemberVisitor.CheckNullExpression, + // Expression.Assign(left, Expression.Default(left.Type)), + // mapComplexResult) + return mapComplexResult; + } + var binaryExpression = CreateAssignExpression(left, + right, + left.Type, + destNullableType, + sourceNullableType); + + var conditionalExpression = nullCheckNestedMemberVisitor.CheckNullExpression != null ? Expression.Condition(nullCheckNestedMemberVisitor.CheckNullExpression, Expression.Assign(left, Expression.Default(left.Type)), binaryExpression) : (Expression)binaryExpression; + + return conditionalExpression; + } + + private static Expression CreateAssignExpression(Expression setMethod, Expression getMethod, Type setType, Type setNullableType, Type getNullableType) + { + var left = setMethod; + var right = getMethod; + + if (setNullableType == null && getNullableType != null) + { + // Nullable to non nullable map + // Type.EmptyTypes is not being used - PCL support + right = Expression.Call(getMethod, "GetValueOrDefault", /*Type.EmptyTypes*/null); + } + else if (setNullableType != null && getNullableType == null) + { + // Non nullable to nullable map + right = Expression.Convert(getMethod, setType); + } + + return Expression.Condition(Expression.NotEqual(left, right), Expression.Assign(left, right), Expression.Default(setType)); + } + + private static Expression CreateConvertibleAssignExpression(Expression setMethod, Expression getMethod, Type setType, Type getType, Type setNullableType) + { + var left = setMethod; + var right = getMethod; + + if ((setNullableType ?? setType).GetInfo().IsEnum && (getType == typeof(string))) + { + return Expression.IfThen( + Expression.NotEqual(getMethod, StaticExpressions.NullConstant), + Expression.Assign(left, + Expression.Convert( + Expression.Call(typeof(Enum).GetInfo().GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }), Expression.Constant(setNullableType ?? setType), right, Expression.Constant(true)), + setType))); + } + return Expression.IfThen( + Expression.NotEqual(Expression.Convert(getMethod, typeof(object)), StaticExpressions.NullConstant), + Expression.Assign(left, + Expression.Convert( + Expression.Call(typeof(Convert).GetInfo().GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), Expression.Convert(right, typeof(object)), Expression.Constant(setNullableType ?? setType)), + setType))); + } + + public virtual BlockExpression MapCollection(Type srcColtype, Type destColType, Expression srcExpression, + Expression destExpression) + { + var sourceType = GetCollectionElementType(srcColtype); + var destType = GetCollectionElementType(destColType); + var sourceVariable = Expression.Variable(srcExpression.Type, + string.Format("{0}Src", Guid.NewGuid().ToString().Replace("-", "_"))); + var assignSourceFromProp = Expression.Assign(sourceVariable, srcExpression); + + var destList = typeof(List<>).MakeGenericType(destType); + var destColl = Expression.Variable(destList, string.Format("{0}Dst", Guid.NewGuid().ToString().Replace("-", "_"))); + + var newColl = Expression.New(destList); + var destAssign = Expression.Assign(destColl, newColl); + + var closedEnumeratorSourceType = typeof(IEnumerator<>).MakeGenericType(sourceType); + var closedEnumerableSourceType = GenericEnumerableType.MakeGenericType(sourceType); + var enumerator = Expression.Variable(closedEnumeratorSourceType, + string.Format("{0}Enum", Guid.NewGuid().ToString().Replace("-", "_"))); + var assignToEnum = Expression.Assign(enumerator, + Expression.Call(sourceVariable, closedEnumerableSourceType.GetInfo().GetMethod("GetEnumerator"))); + var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetInfo().GetMethod("MoveNext")); + + var current = Expression.Property(enumerator, "Current"); + var sourceColItmVariable = Expression.Variable(sourceType, + string.Format("{0}ItmSrc", Guid.NewGuid().ToString().Replace("-", "_"))); + var assignSourceItmFromProp = Expression.Assign(sourceColItmVariable, current); + + var destColItmVariable = Expression.Variable(destType, + string.Format("{0}ItmDst", Guid.NewGuid().ToString().Replace("-", "_"))); + + var loopExpression = CollectionLoopExpression(destColl, sourceColItmVariable, destColItmVariable, + assignSourceItmFromProp, doMoveNext); + + var resultCollection = ConvertCollection(destExpression.Type, destList, destType, destColl); + + var assignResult = Expression.Assign(destExpression, resultCollection); + + var parameters = new List { sourceVariable, destColl, enumerator }; + var expressions = new List + { + assignSourceFromProp, + destAssign, + assignToEnum, + loopExpression, + assignResult + }; + + var blockExpression = Expression.Block(parameters, expressions); + + var checkSrcForNullExp = + Expression.IfThenElse(Expression.Equal(srcExpression, StaticExpressions.NullConstant), + Expression.Assign(destExpression, Expression.Default(destExpression.Type)), blockExpression); + var blockResultExp = Expression.Block(new ParameterExpression[] { }, new Expression[] { checkSrcForNullExp }); + + return blockResultExp; + } + + public Expression GetDifferentTypeMemberMappingExpression(Expression srcExpression, Expression destExpression, bool newDest) + { + var sourceType = srcExpression.Type; + var destType = destExpression.Type; + + var tCol = GetEnumerableInterface(sourceType); + var tnCol = GetEnumerableInterface(destType); + + var blockExpression = (tCol != null && tnCol != null) + ? MapCollection(tCol, tnCol, srcExpression, destExpression) + : MapProperty(sourceType, destType, srcExpression, destExpression, newDest); + + var refSrcType = sourceType.GetInfo().IsClass; + var destPropType = destType; + + if (!refSrcType) return blockExpression; + + var resultExpression = + Expression.IfThenElse(Expression.Equal(srcExpression, StaticExpressions.NullConstant), + Expression.Assign(destExpression, Expression.Default(destPropType)), + blockExpression); + return resultExpression; + } + + public abstract BlockExpression MapProperty(Type srcType, Type destType, Expression srcExpression, Expression destExpression, bool newDest); + + protected BlockExpression CompileCollectionInternal(ParameterExpression sourceParameterExp, ParameterExpression destParameterExp = null) + { + var sourceType = GetCollectionElementType(typeof(T)); + var destType = GetCollectionElementType(typeof(TN)); + + var destList = typeof(List<>).MakeGenericType(destType); + var destColl = Expression.Variable(destList, "destColl"); + + var newColl = Expression.New(destList); + var destAssign = Expression.Assign(destColl, newColl); + + var closedEnumeratorSourceType = typeof(IEnumerator<>).MakeGenericType(sourceType); + var closedEnumerableSourceType = GenericEnumerableType.MakeGenericType(sourceType); + var enumerator = Expression.Variable(closedEnumeratorSourceType, "srcEnumerator"); + var assignToEnum = Expression.Assign(enumerator, + Expression.Call(sourceParameterExp, closedEnumerableSourceType.GetInfo().GetMethod("GetEnumerator"))); + var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetInfo().GetMethod("MoveNext")); + + var current = Expression.Property(enumerator, "Current"); + var sourceColItmVariable = Expression.Variable(sourceType, "ItmSrc"); + var assignSourceItmFromProp = Expression.Assign(sourceColItmVariable, current); + + var destColItmVariable = Expression.Variable(destType, "ItmDst"); + + var loopExpression = CollectionLoopExpression(destColl, sourceColItmVariable, destColItmVariable, + assignSourceItmFromProp, doMoveNext); + + var resultCollection = ConvertCollection(typeof(TN), destList, destType, destColl); + + var parameters = new List { destColl, enumerator }; + + var expressions = new List + { + destAssign, + assignToEnum, + loopExpression, + destParameterExp != null + ? Expression.Assign(destParameterExp, resultCollection) + : resultCollection + }; + + + var blockExpression = Expression.Block(parameters, expressions); + return blockExpression; + } + + internal static Type GetCollectionElementType(Type type) + { + return type.IsArray ? type.GetElementType() : GetEnumerableInterface(type).GetInfo().GetGenericArguments()[0]; + } + + internal LoopExpression CollectionLoopExpression( + ParameterExpression destColl, + ParameterExpression sourceColItmVariable, + ParameterExpression destColItmVariable, + BinaryExpression assignSourceItmFromProp, + MethodCallExpression doMoveNext) + { + var mapExprForType = GetMemberMappingExpression(destColItmVariable, sourceColItmVariable, true); + + var addToNewColl = Expression.Call(destColl, "Add", null, destColItmVariable); + + var ifTrueBlock = Expression.Block(new[] { sourceColItmVariable, destColItmVariable }, new[] { assignSourceItmFromProp, mapExprForType, addToNewColl }); + + var loopExpression = CreateLoopExpression(doMoveNext, ifTrueBlock); + + return loopExpression; + } + + protected LoopExpression CreateLoopExpression(Expression doMoveNextSrc, BlockExpression innerLoopBlock) + { + var brk = Expression.Label(); + var loopExpression = Expression.Loop( + Expression.IfThenElse(Expression.NotEqual(doMoveNextSrc, StaticExpressions.FalseConstant), + innerLoopBlock + , Expression.Break(brk)) + , brk); + return loopExpression; + } + + internal static Expression ConvertCollection(Type destPropType, + Type destList, + Type destType, + Expression destColl) + { + if (destPropType.IsArray) + { + return Expression.Call(destColl, destList.GetInfo().GetMethod("ToArray")); + } + + if (!destPropType.GetInfo().IsGenericType && GetEnumerableInterface(destPropType) == null) + { + return destColl; + } + + if (typeof(IQueryable).GetInfo().IsAssignableFrom(destPropType)) + { + return Expression.Call(typeof(Queryable), "AsQueryable", new[] { destType }, destColl); + } + + if (destPropType.GetInfo().IsInterface && destPropType.GetInfo().IsAssignableFrom(destColl.Type)) + { + // This will handle any destination interface implemented by List + return destColl; + } + + ConstructorInfo ctor = null; + + if (destPropType.GetInfo().IsInterface) + { + // We are targeting an interface type, we need to find a compatible collection type + // We could look for a loaded type that implements the target interface and has an appropriate + // constructor, but that is a bit too much magic for now. + throw new NotImplementedException( + $"Destination interface type {destPropType.FullName} is not supported yet"); + } + else + { + ctor = destPropType.GetInfo().GetConstructors(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault( + ci => { + var param = ci.GetParameters(); + return param.Length == 1 && param[0].ParameterType.GetInfo().IsAssignableFrom(destList); + }); + + if (ctor == null) + { + throw new Exception($"Could not find a constructor on {destPropType.Name} that accepts {destList}"); + } + } + + return Expression.New(ctor, destColl); + } + + public static Type GetEnumerableInterface(Type type) + { + return + type.GetInfo().GetInterfaces() + .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) + ?? (type.GetInfo().IsGenericType && type.GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? type : null); + } + } +} diff --git a/ExpressMapper NET40/MappingServiceProvider.cs b/Expressmapper.Shared/MappingServiceProvider.cs similarity index 74% rename from ExpressMapper NET40/MappingServiceProvider.cs rename to Expressmapper.Shared/MappingServiceProvider.cs index cc61b13..c59d677 100644 --- a/ExpressMapper NET40/MappingServiceProvider.cs +++ b/Expressmapper.Shared/MappingServiceProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; namespace ExpressMapper { @@ -11,6 +12,7 @@ public sealed class MappingServiceProvider : IMappingServiceProvider private readonly object _lock = new object(); public Dictionary> CustomMappers { get; set; } + public Dictionary> CustomMappingsBySource { get; set; } private readonly Dictionary> _customTypeMapperCache = new Dictionary>(); private readonly List _nonGenericCollectionMappingCache = new List(); @@ -36,6 +38,7 @@ public MappingServiceProvider() new DestinationMappingService(this) }; CustomMappers = new Dictionary>(); + CustomMappingsBySource = new Dictionary>(); } public IQueryable Project(IQueryable source) @@ -62,6 +65,27 @@ public IMemberConfiguration Register() return RegisterInternal(); } + private IMemberConfiguration Register(T src, TN dest) + { + return RegisterInternal(); + } + + public IMemberConfiguration Register(IMemberConfigParameters baseType) + { + var memberConfiguration = Register(); + var src = typeof(T); + var dest = typeof(TN); + var cacheKey = CalculateCacheKey(src, dest); + + if (SourceService.TypeMappers.ContainsKey(cacheKey) && + DestinationService.TypeMappers.ContainsKey(cacheKey)) + { + var typeMapper = SourceService.TypeMappers[cacheKey] as ITypeMapper; + typeMapper?.ImportMemberConfigParameters(baseType); + } + return memberConfiguration; + } + public IMemberConfiguration Register(bool memberCaseInsensitive) { return RegisterInternal(memberCaseInsensitive); @@ -94,8 +118,8 @@ private IMemberConfiguration RegisterInternal(bool memberCaseInsen if (SourceService.TypeMappers.ContainsKey(cacheKey) && DestinationService.TypeMappers.ContainsKey(cacheKey)) { - throw new InvalidOperationException( - $"Mapping from {src.FullName} to {dest.FullName} is already registered"); + var typeMapper = SourceService.TypeMappers[cacheKey] as ITypeMapper; + return typeMapper?.MemberConfiguration; } if (src.GetInfo().GetInterfaces().Any(t => t.Name.Contains(typeof(IEnumerable).Name)) && @@ -110,8 +134,21 @@ private IMemberConfiguration RegisterInternal(bool memberCaseInsen SourceService.TypeMappers[cacheKey] = sourceClassMapper; DestinationService.TypeMappers[cacheKey] = destinationClassMapper; - return - new MemberConfiguration(new ITypeMapper[] { sourceClassMapper, destinationClassMapper }); + var memberConfiguration = new MemberConfiguration( + new ITypeMapper[] { sourceClassMapper, destinationClassMapper }, this); + sourceClassMapper.MemberConfiguration = memberConfiguration; + destinationClassMapper.MemberConfiguration = memberConfiguration; + + if (!CustomMappingsBySource.ContainsKey(src.GetHashCode())) + { + CustomMappingsBySource[src.GetHashCode()] = new List(); + } + if (!CustomMappingsBySource[src.GetHashCode()].Contains(cacheKey)) + { + CustomMappingsBySource[src.GetHashCode()].Add(cacheKey); + } + + return memberConfiguration; } } @@ -239,12 +276,20 @@ public void RegisterCustom() where TMapper : ICustomTypeMapper(T src) { - return MapInternal(src); + if (src.GetType() == typeof(T)) + { + return MapInternal(src); + } + return (TN)Map(typeof(T), typeof(TN), src); } public TN Map(T src, TN dest) { - return MapInternal(src, dest); + if (src.GetType() == typeof(T) && (dest.GetType() == typeof(TN))) + { + return MapInternal(src, dest); + } + return (TN)Map(typeof(T), typeof(TN), src, dest); } private TN MapInternal(T src, TN dest = default(TN), bool dynamicTrial = false) @@ -279,14 +324,14 @@ public TN Map(T src, TN dest) var tCol = typeof(T).GetInfo().GetInterfaces() .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) ?? - (typeof(T).GetInfo().IsGenericType - && typeof(T).GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? typeof(T) - : null); + (typeof(T).GetInfo().IsGenericType + && typeof(T).GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? typeof(T) + : null); var tnCol = typeof(TN).GetInfo().GetInterfaces() - .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) ?? - (typeof(TN).GetInfo().IsGenericType && typeof(TN).GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? typeof(TN) - : null); + .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) ?? + (typeof(TN).GetInfo().IsGenericType && typeof(TN).GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? typeof(TN) + : null); if ((tCol == null || tnCol == null)) { @@ -335,16 +380,18 @@ public object Map(Type srcType, Type dstType, object src, object dest) return MapNonGenericInternal(srcType, dstType, src, dest); } - private object MapNonGenericInternal(Type srcType, Type dstType, object src, object dest = null) + private object MapNonGenericInternal(Type srcType, Type dstType, object src, object dest = null, bool dynamicTrial = false) { - var cacheKey = CalculateCacheKey(srcType, dstType); + if (src == null) + { + return null; + } + var cacheKey = CalculateCacheKey(srcType, dstType); if (CustomMappers.ContainsKey(cacheKey)) { var customTypeMapper = CustomMappers[cacheKey]; - var typeMapper = customTypeMapper(); - if (!_customTypeMapperCache.ContainsKey(cacheKey)) { CompileNonGenericCustomTypeMapper(srcType, dstType, typeMapper, cacheKey); @@ -352,18 +399,66 @@ private object MapNonGenericInternal(Type srcType, Type dstType, object src, obj return _customTypeMapperCache[cacheKey](src, dest); } - var mappingService = dest == null ? SourceService : DestinationService; + ITypeMapper mapper = null; + var actualSrcType = src.GetType(); + if (srcType != actualSrcType && actualSrcType.GetInfo().IsAssignableFrom(srcType)) + throw new InvalidCastException($"Your source object instance type '{actualSrcType.FullName}' is not assignable from source type you specified '{srcType}'."); - if (mappingService.TypeMappers.ContainsKey(cacheKey)) + var srcHash = actualSrcType.GetHashCode(); + + if (dest != null) + { + var actualDstType = dest.GetType(); + if (dstType != actualDstType && actualDstType.GetInfo().IsAssignableFrom(dstType)) + throw new InvalidCastException($"Your destination object instance type '{actualSrcType.FullName}' is not assignable from destination type you specified '{srcType}'."); + + if (CustomMappingsBySource.ContainsKey(srcHash)) + { + var mappings = CustomMappingsBySource[srcHash]; + + mapper = + mappings.Where(m => DestinationService.TypeMappers.ContainsKey(m)) + .Select(m => DestinationService.TypeMappers[m]) + .FirstOrDefault(tm => tm.DestinationType == actualDstType); + } + } + else { - if (src == null) + if (CustomMappingsBySource.ContainsKey(srcHash)) { - return null; + var mappings = CustomMappingsBySource[srcHash]; + var typeMappers = + mappings.Where(m => SourceService.TypeMappers.ContainsKey(m)) + .Select(m => SourceService.TypeMappers[m]) + .Where(m => dstType.GetInfo().IsAssignableFrom(m.DestinationType)) + .ToList(); + if (typeMappers.Count > 1) + { + if (typeMappers.All(tm => tm.DestinationType != dstType)) + { + throw new AmbiguousMatchException( + $"Source '{actualSrcType.FullName}' has more than one destination types' mappings"); + } + mapper = typeMappers.FirstOrDefault(tm => tm.DestinationType == dstType); + } + else + { + mapper = typeMappers.First(); + } } + } - var mapper = mappingService.TypeMappers[cacheKey]; + var mappingService = dest == null ? SourceService : DestinationService; + if (mapper != null) + { var nonGenericMapFunc = mapper.GetNonGenericMapFunc(); + return nonGenericMapFunc(src, dest); + } + if (mappingService.TypeMappers.ContainsKey(cacheKey)) + { + mapper = mappingService.TypeMappers[cacheKey]; + var nonGenericMapFunc = mapper.GetNonGenericMapFunc(); return nonGenericMapFunc(src, dest); } @@ -376,7 +471,7 @@ private object MapNonGenericInternal(Type srcType, Type dstType, object src, obj : null); var tnCol = dstType.GetInfo().GetInterfaces() - .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) ?? + .FirstOrDefault(t => t.GetInfo().IsGenericType && t.GetGenericTypeDefinition() == GenericEnumerableType) ?? (dstType.GetInfo().IsGenericType && dstType.GetInfo().GetInterfaces().Any(t => t == typeof(IEnumerable)) ? dstType : null); @@ -390,8 +485,16 @@ private object MapNonGenericInternal(Type srcType, Type dstType, object src, obj : _mappingServices.First(m => m.DestinationSupport).MapCollection(cacheKey).DynamicInvoke(src, dest)); return result; } - throw new MapNotImplementedException( - $"There is no mapping has been found. Source Type: {srcType.FullName}, Destination Type: {dstType.FullName}"); + + if (dynamicTrial) + { + throw new MapNotImplementedException( + $"There is no mapping has been found. Source Type: {srcType.FullName}, Destination Type: {dstType.FullName}"); + } + dynamic source = src; + dynamic destination = dest; + Register(source, destination); + return MapNonGenericInternal(srcType, dstType, src, dest, true); } #region Helper methods diff --git a/ExpressMapper NET40/MemberConfiguration.cs b/Expressmapper.Shared/MemberConfiguration.cs similarity index 90% rename from ExpressMapper NET40/MemberConfiguration.cs rename to Expressmapper.Shared/MemberConfiguration.cs index f9e519a..ef86ca7 100644 --- a/ExpressMapper NET40/MemberConfiguration.cs +++ b/Expressmapper.Shared/MemberConfiguration.cs @@ -1,183 +1,197 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace ExpressMapper -{ - public class MemberConfiguration : IMemberConfiguration - { - private readonly ITypeMapper[] _typeMappers; - public MemberConfiguration(ITypeMapper[] typeMappers) - { - _typeMappers = typeMappers; - } - - public IMemberConfiguration InstantiateFunc(Func constructor) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.InstantiateFunc(constructor); - } - return this; - } - - public IMemberConfiguration Instantiate(Expression> constructor) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.Instantiate(constructor); - } - return this; - } - - public IMemberConfiguration Before(Action beforeHandler) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.BeforeMap(beforeHandler); - } - return this; - } - - public IMemberConfiguration After(Action afterHandler) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.AfterMap(afterHandler); - } - return this; - } - - public IMemberConfiguration Base(Expression> afterHandler) where TBase : class, new() - { - throw new NotImplementedException(); - } - - public IMemberConfiguration Member(Expression> dest, Expression> src) - { - if (dest == null) - { - throw new ArgumentNullException("dst"); - } - - var memberExpression = dest.Body as MemberExpression; - if (memberExpression == null) - { - throw new Exception("MemberExpression should return one of the properties of destination class"); - } - - var propertyInfo = memberExpression.Member as PropertyInfo; - - if (propertyInfo != null && !propertyInfo.CanWrite || (propertyInfo != null && propertyInfo.CanWrite && !propertyInfo.GetSetMethod(true).IsPublic)) - { - Ignore(dest); - } - else - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.MapMember(dest, src); - } - } - return this; - } - - public IMemberConfiguration Function(Expression> dest, Func src) - { - var memberExpression = dest.Body as MemberExpression; - if (memberExpression == null) - { - throw new Exception("MemberExpression should return one of the properties of destination class"); - } - - var propertyInfo = memberExpression.Member as PropertyInfo; - - if (propertyInfo != null && !propertyInfo.CanWrite || (propertyInfo != null && propertyInfo.CanWrite && !propertyInfo.GetSetMethod(true).IsPublic)) - { - Ignore(dest); - } - else - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.MapFunction(dest, src); - } - } - return this; - } - - public IMemberConfiguration Ignore(Expression> dest) - { - if (dest == null) - { - throw new ArgumentNullException("dst"); - } - - if (!(dest.Body is MemberExpression)) - { - throw new Exception("MemberExpression should return one of the properties of destination class"); - } - foreach (var typeMapper in _typeMappers) - { - typeMapper.Ignore(dest); - } - return this; - } - - public IMemberConfiguration Value(Expression> dest, TNMember value) - { - if (!(dest.Body is MemberExpression)) - { - throw new Exception("MemberExpression should return one of the properties of destination class"); - } - return Member(dest, x => value); - } - - public IMemberConfiguration CaseSensitive(bool caseSensitive) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.CaseSensetiveMemberMap(caseSensitive); - } - return this; - } - - public IMemberConfiguration CompileTo(CompilationTypes compilationType) - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.CompileTo(compilationType); - } - return this; - } - - #region flatten code - - /// - /// This will apply the flattening algorithm to the source. - /// This allow properties in nested source classes to be assigned to a top level destination property. - /// Matching is done by concatenated names, and also a few Linq commands - /// e.g. dest.NestedClassMyProperty would contain the property src.NestedClass.MyProperty (as long as types match) - /// and dest.MyCollectionCount would contain the count (int) of the Collection. - /// - public IMemberConfiguration Flatten() - { - var sourceMapperBase = - _typeMappers.Single(x => x.MapperType == CompilationTypes.Source) as TypeMapperBase; - - if (sourceMapperBase == null) - throw new Exception("Failed to find the source mapping."); - - foreach (var typeMapper in _typeMappers) - { - typeMapper.Flatten(); - } - - return this; - } - - #endregion - - } -} +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace ExpressMapper +{ + public class MemberConfiguration : IMemberConfiguration + { + private readonly IMappingServiceProvider _mappingServiceProvider; + private readonly ITypeMapper[] _typeMappers; + public MemberConfiguration(ITypeMapper[] typeMappers, IMappingServiceProvider mappingServiceProvider) + { + _typeMappers = typeMappers; + _mappingServiceProvider = mappingServiceProvider; + } + + public IMemberConfiguration InstantiateFunc(Func constructor) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.InstantiateFunc(constructor); + } + return this; + } + + public IMemberConfiguration Instantiate(Expression> constructor) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.Instantiate(constructor); + } + return this; + } + + public IMemberConfiguration Before(Action beforeHandler) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.BeforeMap(beforeHandler); + } + return this; + } + + public IMemberConfiguration After(Action afterHandler) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.AfterMap(afterHandler); + } + return this; + } + + public IMemberConfiguration Base(Expression> afterHandler) where TBase : class, new() + { + throw new NotImplementedException(); + } + + public IMemberConfiguration Member(Expression> dest, Expression> src) + { + if (dest == null) + { + throw new ArgumentNullException("dst"); + } + + var memberExpression = dest.Body as MemberExpression; + if (memberExpression == null) + { + throw new Exception("MemberExpression should return one of the properties of destination class"); + } + + var propertyInfo = memberExpression.Member as PropertyInfo; + + if (propertyInfo != null && !propertyInfo.CanWrite || (propertyInfo != null && propertyInfo.CanWrite && !propertyInfo.GetSetMethod(true).IsPublic)) + { + Ignore(dest); + } + else + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.MapMember(dest, src); + } + } + return this; + } + + public IMemberConfiguration Function(Expression> dest, Func src) + { + var memberExpression = dest.Body as MemberExpression; + if (memberExpression == null) + { + throw new Exception("MemberExpression should return one of the properties of destination class"); + } + + var propertyInfo = memberExpression.Member as PropertyInfo; + + if (propertyInfo != null && !propertyInfo.CanWrite || (propertyInfo != null && propertyInfo.CanWrite && !propertyInfo.GetSetMethod(true).IsPublic)) + { + Ignore(dest); + } + else + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.MapFunction(dest, src); + } + } + return this; + } + + public IMemberConfiguration Ignore(Expression> dest) + { + if (dest == null) + { + throw new ArgumentNullException("dst"); + } + + if (!(dest.Body is MemberExpression)) + { + throw new Exception("MemberExpression should return one of the properties of destination class"); + } + foreach (var typeMapper in _typeMappers) + { + typeMapper.Ignore(dest); + } + return this; + } + + public IMemberConfiguration Value(Expression> dest, TNMember value) + { + if (!(dest.Body is MemberExpression)) + { + throw new Exception("MemberExpression should return one of the properties of destination class"); + } + return Member(dest, x => value); + } + + public IMemberConfiguration CaseSensitive(bool caseSensitive) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.CaseSensetiveMemberMap(caseSensitive); + } + return this; + } + + public IMemberConfiguration CompileTo(CompilationTypes compilationType) + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.CompileTo(compilationType); + } + return this; + } + + public IMemberConfiguration Include() where TSub : T + where TNSub : TN + { + foreach (var typeMapper in _typeMappers) + { + typeMapper.BaseType = true; + } + + _mappingServiceProvider.Register(_typeMappers.First() as IMemberConfigParameters); + return this; + } + + #region flatten code + + /// + /// This will apply the flattening algorithm to the source. + /// This allow properties in nested source classes to be assigned to a top level destination property. + /// Matching is done by concatenated names, and also a few Linq commands + /// e.g. dest.NestedClassMyProperty would contain the property src.NestedClass.MyProperty (as long as types match) + /// and dest.MyCollectionCount would contain the count (int) of the Collection. + /// + public IMemberConfiguration Flatten() + { + var sourceMapperBase = + _typeMappers.Single(x => x.MapperType == CompilationTypes.Source) as TypeMapperBase; + + if (sourceMapperBase == null) + throw new Exception("Failed to find the source mapping."); + + foreach (var typeMapper in _typeMappers) + { + typeMapper.Flatten(); + } + + return this; + } + + #endregion + + } +} diff --git a/ExpressMapper NET40/NullCheckNestedMemberVisitor.cs b/Expressmapper.Shared/NullCheckNestedMemberVisitor.cs similarity index 100% rename from ExpressMapper NET40/NullCheckNestedMemberVisitor.cs rename to Expressmapper.Shared/NullCheckNestedMemberVisitor.cs diff --git a/ExpressMapper NET40/PreciseSubstituteParameterVisitor.cs b/Expressmapper.Shared/PreciseSubstituteParameterVisitor.cs similarity index 100% rename from ExpressMapper NET40/PreciseSubstituteParameterVisitor.cs rename to Expressmapper.Shared/PreciseSubstituteParameterVisitor.cs diff --git a/ExpressMapper NET40/ProjectionAccessMemberVisitor.cs b/Expressmapper.Shared/ProjectionAccessMemberVisitor.cs similarity index 100% rename from ExpressMapper NET40/ProjectionAccessMemberVisitor.cs rename to Expressmapper.Shared/ProjectionAccessMemberVisitor.cs diff --git a/ExpressMapper NET40/ReturnTypeDifferenceVisitor.cs b/Expressmapper.Shared/ReturnTypeDifferenceVisitor.cs similarity index 100% rename from ExpressMapper NET40/ReturnTypeDifferenceVisitor.cs rename to Expressmapper.Shared/ReturnTypeDifferenceVisitor.cs diff --git a/ExpressMapper NET40/SourceMappingService.cs b/Expressmapper.Shared/SourceMappingService.cs similarity index 100% rename from ExpressMapper NET40/SourceMappingService.cs rename to Expressmapper.Shared/SourceMappingService.cs diff --git a/ExpressMapper NET40/SourceTypeMapper.cs b/Expressmapper.Shared/SourceTypeMapper.cs similarity index 100% rename from ExpressMapper NET40/SourceTypeMapper.cs rename to Expressmapper.Shared/SourceTypeMapper.cs diff --git a/ExpressMapper NET40/StaticExpressions.cs b/Expressmapper.Shared/StaticExpressions.cs similarity index 96% rename from ExpressMapper NET40/StaticExpressions.cs rename to Expressmapper.Shared/StaticExpressions.cs index 8a5eb06..45cf0bd 100644 --- a/ExpressMapper NET40/StaticExpressions.cs +++ b/Expressmapper.Shared/StaticExpressions.cs @@ -1,10 +1,10 @@ -using System.Linq.Expressions; - -namespace ExpressMapper -{ - internal static class StaticExpressions - { - internal static readonly ConstantExpression NullConstant = Expression.Constant(null); - internal static readonly ConstantExpression FalseConstant = Expression.Constant(false); - } -} +using System.Linq.Expressions; + +namespace ExpressMapper +{ + internal static class StaticExpressions + { + internal static readonly ConstantExpression NullConstant = Expression.Constant(null); + internal static readonly ConstantExpression FalseConstant = Expression.Constant(false); + } +} diff --git a/ExpressMapper NET40/SubstituteParameterVisitor.cs b/Expressmapper.Shared/SubstituteParameterVisitor.cs similarity index 100% rename from ExpressMapper NET40/SubstituteParameterVisitor.cs rename to Expressmapper.Shared/SubstituteParameterVisitor.cs diff --git a/ExpressMapper NET40/TypeExtensions.cs b/Expressmapper.Shared/TypeExtensions.cs similarity index 100% rename from ExpressMapper NET40/TypeExtensions.cs rename to Expressmapper.Shared/TypeExtensions.cs diff --git a/ExpressMapper NET40/TypeMapperBase.cs b/Expressmapper.Shared/TypeMapperBase.cs similarity index 65% rename from ExpressMapper NET40/TypeMapperBase.cs rename to Expressmapper.Shared/TypeMapperBase.cs index 4159237..12013da 100644 --- a/ExpressMapper NET40/TypeMapperBase.cs +++ b/Expressmapper.Shared/TypeMapperBase.cs @@ -1,447 +1,537 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace ExpressMapper -{ - public abstract class TypeMapperBase - { - #region Constants - - private const string RightStr = "right"; - private const string DstString = "dst"; - private const string SrcString = "src"; - - #endregion - - private readonly object _lockObject = new object(); - private bool _compiling; - protected ParameterExpression DestFakeParameter = Expression.Parameter(typeof(TN), DstString); - protected IMappingService MappingService { get; set; } - protected IMappingServiceProvider MappingServiceProvider { get; set; } - protected Dictionary AutoMembers = new Dictionary(); - protected List> CustomMembers = new List>(); - protected List> FlattenMembers = new List>(); - protected List> CustomFunctionMembers = new List>(); - public Expression> QueryableExpression { get; protected set; } - public abstract CompilationTypes MapperType { get; } - protected bool Flattened { get; set; } - - public Expression QueryableGeneralExpression - { - get { return QueryableExpression; } - } - - #region Constructors - - protected TypeMapperBase(IMappingService service, IMappingServiceProvider serviceProvider) - { - ResultExpressionList = new List(); - RecursiveExpressionResult = new List(); - PropertyCache = new Dictionary(); - CustomPropertyCache = new Dictionary(); - IgnoreMemberList = new List(); - MappingService = service; - MappingServiceProvider = serviceProvider; - InitializeRecursiveMappings(serviceProvider); - } - - #endregion - - #region Properties - - protected ParameterExpression SourceParameter = Expression.Parameter(typeof(T), SrcString); - protected List RecursiveExpressionResult { get; private set; } - protected List ResultExpressionList { get; private set; } - protected Func ResultMapFunction { get; set; } - protected List IgnoreMemberList { get; private set; } - protected Dictionary PropertyCache { get; private set; } - protected Dictionary CustomPropertyCache { get; private set; } - protected Action BeforeMapHandler { get; set; } - protected Action AfterMapHandler { get; set; } - protected Func ConstructorFunc { get; set; } - protected Expression> ConstructorExp { get; set; } - protected Func NonGenericMapFunc { get; set; } - protected bool CaseSensetiveMember { get; set; } - protected bool CaseSensetiveOverride { get; set; } - - protected CompilationTypes CompilationTypeMember { get; set; } - protected bool CompilationTypeOverride { get; set; } - - #endregion - - protected abstract void InitializeRecursiveMappings(IMappingServiceProvider serviceProvider); - - public void Flatten() - { - Flattened = true; - } - - public void CaseSensetiveMemberMap(bool caseSensitive) - { - CaseSensetiveMember = caseSensitive; - CaseSensetiveOverride = true; - } - - public void CompileTo(CompilationTypes compileType) - { - CompilationTypeMember = compileType; - CompilationTypeOverride = true; - } - - public void Compile(CompilationTypes compilationType, bool forceByDemand = false) - { - if (!forceByDemand && ((CompilationTypeOverride && (MapperType & CompilationTypeMember) != MapperType) || (!CompilationTypeOverride && (MapperType & compilationType) != MapperType))) - { - return; - } - - if (_compiling) - { - return; - } - - try - { - _compiling = true; - try - { - CompileInternal(); - } - catch (Exception ex) - { - throw new ExpressmapperException( - string.Format( - "Error error occured trying to compile mapping for: source {0}, destination {1}. See the inner exception for details.", - typeof(T).FullName, typeof(TN).FullName), ex); - } - } - finally - { - _compiling = false; - } - } - - protected abstract void CompileInternal(); - - public void AfterMap(Action afterMap) - { - if (afterMap == null) - { - throw new ArgumentNullException("afterMap"); - } - - if (AfterMapHandler != null) - { - throw new InvalidOperationException(String.Format("AfterMap already registered for {0}", typeof(T).FullName)); - } - - AfterMapHandler = afterMap; - } - - public Tuple, ParameterExpression, ParameterExpression> GetMapExpressions() - { - if (_compiling) - { - return new Tuple, ParameterExpression, ParameterExpression>(new List(RecursiveExpressionResult), SourceParameter, DestFakeParameter); - } - - Compile(MapperType); - return new Tuple, ParameterExpression, ParameterExpression>(new List(ResultExpressionList), SourceParameter, DestFakeParameter); ; - } - - public Func GetNonGenericMapFunc() - { - if (NonGenericMapFunc != null) - { - return NonGenericMapFunc; - } - - var parameterExpression = Expression.Parameter(typeof(object), SrcString); - var srcConverted = Expression.Convert(parameterExpression, typeof(T)); - var srcTypedExp = Expression.Variable(typeof(T), "srcTyped"); - var srcAssigned = Expression.Assign(srcTypedExp, srcConverted); - - var destParameterExp = Expression.Parameter(typeof(object), DstString); - var dstConverted = Expression.Convert(destParameterExp, typeof(TN)); - var dstTypedExp = Expression.Variable(typeof(TN), "dstTyped"); - var dstAssigned = Expression.Assign(dstTypedExp, dstConverted); - - var customGenericType = typeof(ITypeMapper<,>).MakeGenericType(typeof(T), typeof(TN)); - var castToCustomGeneric = Expression.Convert(Expression.Constant((ITypeMapper)this), customGenericType); - var genVariable = Expression.Variable(customGenericType); - var assignExp = Expression.Assign(genVariable, castToCustomGeneric); - var methodInfo = customGenericType.GetInfo().GetMethod("MapTo", new[] { typeof(T), typeof(TN) }); - - var mapCall = Expression.Call(genVariable, methodInfo, srcTypedExp, dstTypedExp); - var resultVarExp = Expression.Variable(typeof(object), "result"); - var convertToObj = Expression.Convert(mapCall, typeof(object)); - var assignResult = Expression.Assign(resultVarExp, convertToObj); - - var blockExpression = Expression.Block(new[] { srcTypedExp, dstTypedExp, genVariable, resultVarExp }, new Expression[] { srcAssigned, dstAssigned, assignExp, assignResult, resultVarExp }); - var lambda = Expression.Lambda>(blockExpression, parameterExpression, destParameterExp); - NonGenericMapFunc = lambda.Compile(); - - return NonGenericMapFunc; - } - - protected void AutoMapProperty(MemberInfo propertyGet, MemberInfo propertySet) - { - var callSetPropMethod = Expression.PropertyOrField(DestFakeParameter, propertySet.Name); - var callGetPropMethod = Expression.PropertyOrField(SourceParameter, propertyGet.Name); - - MapMember(callSetPropMethod, callGetPropMethod); - } - - public void MapMember(Expression> left, Expression> right) - { - if (left == null) - { - throw new ArgumentNullException("left"); - } - - if (right == null) - { - throw new ArgumentNullException(RightStr); - } - - CustomMembers.Add(new KeyValuePair(left.Body as MemberExpression, right.Body)); - //MapMember(left.Body as MemberExpression, right.Body); - } - - #region flatten code - - public void MapMemberFlattened(MemberExpression left, Expression right) - { - FlattenMembers.Add(new KeyValuePair(left, right)); - } - - protected List NamesOfMembersAndIgnoredProperties() - { - var result = - CustomMembers.Select(x => x.Key.Member.Name) - .Union(CustomFunctionMembers.Select(x => x.Key.Member.Name)) - .ToList(); - result.AddRange(IgnoreMemberList); - return result; - } - - #endregion - - - protected void MapMember(MemberExpression left, Expression right) - { - var mappingExpression = MappingService.GetMemberMappingExpression(left, right, false); - CustomPropertyCache[left.Member.Name] = mappingExpression; - } - - protected BinaryExpression GetDestionationVariable() - { - if (ConstructorExp != null) - { - var substVisitorSrc = new SubstituteParameterVisitor(SourceParameter); - var constructorExp = substVisitorSrc.Visit(ConstructorExp.Body); - - return Expression.Assign(DestFakeParameter, constructorExp); - } - - if (ConstructorFunc != null) - { - Expression> customConstruct = t => ConstructorFunc(t); - var invocationExpression = Expression.Invoke(customConstruct, SourceParameter); - return Expression.Assign(DestFakeParameter, invocationExpression); - } - var createDestination = Expression.New(typeof(TN)); - return Expression.Assign(DestFakeParameter, createDestination); - } - - protected void ProcessAutoProperties() - { - var getFields = - typeof(T).GetInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); - var setFields = - typeof(TN).GetInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); - - var getProps = - typeof(T).GetInfo().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); - var setProps = - typeof(TN).GetInfo().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); - - var sourceMembers = getFields.Cast().Union(getProps); - var destMembers = setFields.Cast().Union(setProps); - - var stringComparison = GetStringCase(); - -// var comparer = CultureInfo.CurrentCulture.CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase); - var comparer = StringComparer.Create(CultureInfo.CurrentCulture, - stringComparison == StringComparison.OrdinalIgnoreCase); - - foreach (var prop in sourceMembers) - { - if (IgnoreMemberList.Contains(prop.Name, comparer) || CustomPropertyCache.Keys.Contains(prop.Name, comparer)) - { - continue; - } - var setprop = destMembers.FirstOrDefault(x => string.Equals(x.Name, prop.Name, stringComparison)); - - var propertyInfo = setprop as PropertyInfo; - if ((propertyInfo == null && setprop == null) || (propertyInfo != null && (!propertyInfo.CanWrite || !propertyInfo.GetSetMethod(true).IsPublic))) - { - IgnoreMemberList.Add(prop.Name); - continue; - } - AutoMembers[prop] = setprop; - AutoMapProperty(prop, setprop); - } - } - - internal StringComparison GetStringCase() - { - StringComparison stringComparison; - - if (MappingServiceProvider.CaseSensetiveMemberMap && !CaseSensetiveOverride) - { - stringComparison = MappingServiceProvider.CaseSensetiveMemberMap - ? StringComparison.CurrentCulture - : StringComparison.OrdinalIgnoreCase; - } - else - { - stringComparison = CaseSensetiveMember - ? StringComparison.CurrentCulture - : StringComparison.OrdinalIgnoreCase; - } - return stringComparison; - } - - public virtual void InstantiateFunc(Func constructor) - { - ConstructorFunc = constructor; - } - - public virtual void Instantiate(Expression> constructor) - { - ConstructorExp = constructor; - } - - public virtual void BeforeMap(Action beforeMap) - { - if (beforeMap == null) - { - throw new ArgumentNullException("beforeMap"); - } - - if (BeforeMapHandler != null) - { - throw new InvalidOperationException(String.Format("BeforeMap already registered for {0}", typeof(T).FullName)); - } - - BeforeMapHandler = beforeMap; - } - - public virtual void Ignore(Expression> left) - { - var memberExpression = left.Body as MemberExpression; - IgnoreMemberList.Add(memberExpression.Member.Name); - } - - public void Ignore(PropertyInfo left) - { - IgnoreMemberList.Add(left.Name); - } - - public TN MapTo(T src, TN dest) - { - if (ResultMapFunction == null) - { - lock (_lockObject) - { - // force compilation by client code demand - Compile(MapperType, forceByDemand: true); - } - } - return ResultMapFunction(src, dest); - } - - public void MapFunction(Expression> left, Func right) - { - var memberExpression = left.Body as MemberExpression; - Expression> expr = (t) => right(t); - - var parameterExpression = Expression.Parameter(typeof(T)); - var rightExpression = Expression.Invoke(expr, parameterExpression); - - CustomFunctionMembers.Add(new KeyValuePair(memberExpression, rightExpression)); - //MapFunction(left, rightExpression, memberExpression); - } - - protected void MapFunction(MemberExpression left, Expression rightExpression) - { - if (left.Member.DeclaringType != rightExpression.Type) - { - var mapComplexResult = MappingService.GetDifferentTypeMemberMappingExpression(rightExpression, left, false); - CustomPropertyCache[left.Member.Name] = mapComplexResult; - } - else - { - var binaryExpression = Expression.Assign(left, rightExpression); - CustomPropertyCache[left.Member.Name] = binaryExpression; - } - } - - protected void ProcessCustomMembers() - { - CustomMembers = TranslateExpression(CustomMembers); - foreach (var keyValue in CustomMembers) - { - MapMember(keyValue.Key, keyValue.Value); - } - } - - protected void ProcessCustomFunctionMembers() - { - CustomFunctionMembers = TranslateExpression(CustomFunctionMembers); - foreach (var keyValue in CustomFunctionMembers) - { - MapMember(keyValue.Key, keyValue.Value); - } - } - - protected void ProcessFlattenedMembers() - { - if (Flattened) - { - var flattenMapper = new FlattenMapper(NamesOfMembersAndIgnoredProperties(), GetStringCase()); - foreach (var flattenInfo in flattenMapper.BuildMemberMapping()) - { - MapMemberFlattened(flattenInfo.DestAsMemberExpression(), flattenInfo.SourceAsExpression()); - } - - FlattenMembers = TranslateExpression(FlattenMembers); - foreach (var keyValue in FlattenMembers) - { - MapMember(keyValue.Key, keyValue.Value); - } - } - } - - protected List> TranslateExpression(IEnumerable> expressions) - { - var result = new List>(expressions.Count()); - foreach (var customMember in expressions) - { - var substVisitorDest = new SubstituteParameterVisitor(DestFakeParameter); - var dest = substVisitorDest.Visit(customMember.Key) as MemberExpression; - - var substVisitorSrc = new SubstituteParameterVisitor(SourceParameter); - var src = substVisitorSrc.Visit(customMember.Value); - result.Add(new KeyValuePair(dest, src)); - } - return result; - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace ExpressMapper +{ + public abstract class TypeMapperBase : IMemberConfigParameters + { + #region Constants + + private const string RightStr = "right"; + private const string DstString = "dst"; + private const string SrcString = "src"; + + #endregion + + private readonly object _lockObject = new object(); + private bool _compiling; + protected ParameterExpression DestFakeParameter = Expression.Parameter(typeof(TN), DstString); + protected IMappingService MappingService { get; set; } + protected IMappingServiceProvider MappingServiceProvider { get; set; } + protected Dictionary AutoMembers = new Dictionary(); + public bool BaseType { get; set; } + + + public IMemberConfiguration MemberConfiguration { get; set; } + public List> CustomMembers { get; private set; } + public List> FlattenMembers { get; private set; } + public List> CustomFunctionMembers { get; private set; } + public Expression> QueryableExpression { get; protected set; } + public abstract CompilationTypes MapperType { get; } + public bool Flattened { get; set; } + public Type SourceType => typeof(T); + public Type DestinationType => typeof(TN); + + public Expression QueryableGeneralExpression => QueryableExpression; + + #region Constructors + + protected TypeMapperBase(IMappingService service, IMappingServiceProvider serviceProvider) + { + ResultExpressionList = new List(); + RecursiveExpressionResult = new List(); + PropertyCache = new Dictionary(); + CustomPropertyCache = new Dictionary(); + IgnoreMemberList = new List(); + MappingService = service; + MappingServiceProvider = serviceProvider; + InitializeRecursiveMappings(serviceProvider); + + CustomMembers = new List>(); + FlattenMembers = new List>(); + CustomFunctionMembers = new List>(); + } + + #endregion + + #region Properties + + protected ParameterExpression SourceParameter = Expression.Parameter(typeof(T), SrcString); + protected List RecursiveExpressionResult { get; private set; } + protected List ResultExpressionList { get; private set; } + protected Func ResultMapFunction { get; set; } + public List IgnoreMemberList { get; private set; } + protected Dictionary PropertyCache { get; private set; } + protected Dictionary CustomPropertyCache { get; private set; } + protected Action BeforeMapHandler { get; set; } + protected Action AfterMapHandler { get; set; } + protected Func ConstructorFunc { get; set; } + protected Expression> ConstructorExp { get; set; } + protected Func NonGenericMapFunc { get; set; } + public bool CaseSensetiveMember { get; set; } + public bool CaseSensetiveOverride { get; set; } + public CompilationTypes CompilationTypeMember { get; set; } + public bool CompilationTypeOverride { get; set; } + + #endregion + + protected abstract void InitializeRecursiveMappings(IMappingServiceProvider serviceProvider); + + public void Flatten() + { + Flattened = true; + } + + public void CaseSensetiveMemberMap(bool caseSensitive) + { + CaseSensetiveMember = caseSensitive; + CaseSensetiveOverride = true; + } + + public void CompileTo(CompilationTypes compileType) + { + CompilationTypeMember = compileType; + CompilationTypeOverride = true; + } + + public void Compile(CompilationTypes compilationType, bool forceByDemand = false) + { + if (!forceByDemand && ((CompilationTypeOverride && (MapperType & CompilationTypeMember) != MapperType) || + (!CompilationTypeOverride && (MapperType & compilationType) != MapperType))) + { + return; + } + + if (_compiling) + { + return; + } + + try + { + _compiling = true; + try + { + CompileInternal(); + } + catch (Exception ex) + { + throw new ExpressmapperException( + $"Error error occured trying to compile mapping for: source {typeof(T).FullName}, destination {typeof(TN).FullName}. See the inner exception for details.", + ex); + } + } + finally + { + _compiling = false; + } + } + + protected abstract void CompileInternal(); + + public void AfterMap(Action afterMap) + { + if (afterMap == null) + { + throw new ArgumentNullException(nameof(afterMap)); + } + + if (AfterMapHandler != null) + { + throw new InvalidOperationException($"AfterMap already registered for {typeof(T).FullName}"); + } + + AfterMapHandler = afterMap; + } + + public Tuple, ParameterExpression, ParameterExpression> GetMapExpressions() + { + if (_compiling || BaseType) + { + return new Tuple, ParameterExpression, ParameterExpression>( + new List(RecursiveExpressionResult), SourceParameter, DestFakeParameter); + } + + Compile(MapperType); + return new Tuple, ParameterExpression, ParameterExpression>( + new List(ResultExpressionList), SourceParameter, DestFakeParameter); + } + + public Func GetNonGenericMapFunc() + { + if (NonGenericMapFunc != null) + { + return NonGenericMapFunc; + } + + var parameterExpression = Expression.Parameter(typeof(object), SrcString); + var srcConverted = Expression.Convert(parameterExpression, typeof(T)); + var srcTypedExp = Expression.Variable(typeof(T), "srcTyped"); + var srcAssigned = Expression.Assign(srcTypedExp, srcConverted); + + var destParameterExp = Expression.Parameter(typeof(object), DstString); + var dstConverted = Expression.Convert(destParameterExp, typeof(TN)); + var dstTypedExp = Expression.Variable(typeof(TN), "dstTyped"); + var dstAssigned = Expression.Assign(dstTypedExp, dstConverted); + + var customGenericType = typeof(ITypeMapper<,>).MakeGenericType(typeof(T), typeof(TN)); + var castToCustomGeneric = Expression.Convert(Expression.Constant((ITypeMapper) this), customGenericType); + var genVariable = Expression.Variable(customGenericType); + var assignExp = Expression.Assign(genVariable, castToCustomGeneric); + var methodInfo = customGenericType.GetInfo().GetMethod("MapTo", new[] {typeof(T), typeof(TN)}); + + var mapCall = Expression.Call(genVariable, methodInfo, srcTypedExp, dstTypedExp); + var resultVarExp = Expression.Variable(typeof(object), "result"); + var convertToObj = Expression.Convert(mapCall, typeof(object)); + var assignResult = Expression.Assign(resultVarExp, convertToObj); + + var blockExpression = Expression.Block(new[] {srcTypedExp, dstTypedExp, genVariable, resultVarExp}, + new Expression[] {srcAssigned, dstAssigned, assignExp, assignResult, resultVarExp}); + var lambda = + Expression.Lambda>(blockExpression, parameterExpression, destParameterExp); + NonGenericMapFunc = lambda.Compile(); + + return NonGenericMapFunc; + } + + protected void AutoMapProperty(MemberInfo propertyGet, MemberInfo propertySet) + { + var callSetPropMethod = Expression.PropertyOrField(DestFakeParameter, propertySet.Name); + var callGetPropMethod = Expression.PropertyOrField(SourceParameter, propertyGet.Name); + + MapMember(callSetPropMethod, callGetPropMethod); + } + + public void MapMember(Expression> left, + Expression> right) + { + if (left == null) + { + throw new ArgumentNullException("left"); + } + + if (right == null) + { + throw new ArgumentNullException(RightStr); + } + + CustomMembers.Add( + new KeyValuePair(left.Body as MemberExpression, right.Body)); + //MapMember(left.Body as MemberExpression, right.Body); + } + + #region flatten code + + public void MapMemberFlattened(MemberExpression left, Expression right) + { + FlattenMembers.Add(new KeyValuePair(left, right)); + } + + protected List NamesOfMembersAndIgnoredProperties() + { + var result = + CustomMembers.Select(x => x.Key.Member.Name) + .Union(CustomFunctionMembers.Select(x => x.Key.Member.Name)) + .ToList(); + result.AddRange(IgnoreMemberList); + return result; + } + + #endregion + + + protected void MapMember(MemberExpression left, Expression right) + { + var mappingExpression = MappingService.GetMemberMappingExpression(left, right, false); + CustomPropertyCache[left.Member.Name] = mappingExpression; + } + + protected BinaryExpression GetDestionationVariable() + { + if (ConstructorExp != null) + { + var substVisitorSrc = new SubstituteParameterVisitor(SourceParameter); + var constructorExp = substVisitorSrc.Visit(ConstructorExp.Body); + + return Expression.Assign(DestFakeParameter, constructorExp); + } + + if (ConstructorFunc != null) + { + Expression> customConstruct = t => ConstructorFunc(t); + var invocationExpression = Expression.Invoke(customConstruct, SourceParameter); + return Expression.Assign(DestFakeParameter, invocationExpression); + } + var createDestination = Expression.New(typeof(TN)); + return Expression.Assign(DestFakeParameter, createDestination); + } + + protected void ProcessAutoProperties() + { + var getFields = + typeof(T).GetInfo() + .GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + var setFields = + typeof(TN).GetInfo() + .GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + + var getProps = + typeof(T).GetInfo() + .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + var setProps = + typeof(TN).GetInfo() + .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + + var sourceMembers = getFields.Cast().Union(getProps); + var destMembers = setFields.Cast().Union(setProps); + + var stringComparison = GetStringCase(); + +// var comparer = CultureInfo.CurrentCulture.CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase); + var comparer = StringComparer.Create(CultureInfo.CurrentCulture, + stringComparison == StringComparison.OrdinalIgnoreCase); + + foreach (var prop in sourceMembers) + { + if (IgnoreMemberList.Contains(prop.Name, comparer) || + CustomPropertyCache.Keys.Contains(prop.Name, comparer)) + { + continue; + } + var setprop = destMembers.FirstOrDefault(x => string.Equals(x.Name, prop.Name, stringComparison)); + + var propertyInfo = setprop as PropertyInfo; + if ((propertyInfo == null && setprop == null) || + (propertyInfo != null && (!propertyInfo.CanWrite || !propertyInfo.GetSetMethod(true).IsPublic))) + { + IgnoreMemberList.Add(prop.Name); + continue; + } + AutoMembers[prop] = setprop; + AutoMapProperty(prop, setprop); + } + } + + internal StringComparison GetStringCase() + { + StringComparison stringComparison; + + if (MappingServiceProvider.CaseSensetiveMemberMap && !CaseSensetiveOverride) + { + stringComparison = MappingServiceProvider.CaseSensetiveMemberMap + ? StringComparison.CurrentCulture + : StringComparison.OrdinalIgnoreCase; + } + else + { + stringComparison = CaseSensetiveMember + ? StringComparison.CurrentCulture + : StringComparison.OrdinalIgnoreCase; + } + return stringComparison; + } + + public virtual void InstantiateFunc(Func constructor) + { + ConstructorFunc = constructor; + } + + public virtual void Instantiate(Expression> constructor) + { + ConstructorExp = constructor; + } + + public virtual void BeforeMap(Action beforeMap) + { + if (beforeMap == null) + { + throw new ArgumentNullException(nameof(beforeMap)); + } + + if (BeforeMapHandler != null) + { + throw new InvalidOperationException($"BeforeMap already registered for {typeof(T).FullName}"); + } + + BeforeMapHandler = beforeMap; + } + + public virtual void Ignore(Expression> left) + { + var memberExpression = left.Body as MemberExpression; + IgnoreMemberList.Add(memberExpression.Member.Name); + } + + public void Ignore(PropertyInfo left) + { + IgnoreMemberList.Add(left.Name); + } + + public TN MapTo(T src, TN dest) + { + if (ResultMapFunction == null) + { + lock (_lockObject) + { + // force compilation by client code demand + Compile(MapperType, forceByDemand: true); + } + } + return ResultMapFunction(src, dest); + } + + public void MapFunction(Expression> left, Func right) + { + var memberExpression = left.Body as MemberExpression; + Expression> expr = (t) => right(t); + + var parameterExpression = Expression.Parameter(typeof(T)); + var rightExpression = Expression.Invoke(expr, parameterExpression); + + CustomFunctionMembers.Add( + new KeyValuePair(memberExpression, rightExpression)); + //MapFunction(left, rightExpression, memberExpression); + } + + protected void MapFunction(MemberExpression left, Expression rightExpression) + { + if (left.Member.DeclaringType != rightExpression.Type) + { + var mapComplexResult = + MappingService.GetDifferentTypeMemberMappingExpression(rightExpression, left, false); + CustomPropertyCache[left.Member.Name] = mapComplexResult; + } + else + { + var binaryExpression = Expression.Assign(left, rightExpression); + CustomPropertyCache[left.Member.Name] = binaryExpression; + } + } + + protected void ProcessCustomMembers() + { + CustomMembers = TranslateExpression(CustomMembers); + foreach (var keyValue in CustomMembers) + { + MapMember(keyValue.Key, keyValue.Value); + } + } + + protected void ProcessCustomFunctionMembers() + { + CustomFunctionMembers = TranslateExpression(CustomFunctionMembers); + foreach (var keyValue in CustomFunctionMembers) + { + MapMember(keyValue.Key, keyValue.Value); + } + } + + protected void ProcessFlattenedMembers() + { + if (Flattened) + { + var flattenMapper = new FlattenMapper(NamesOfMembersAndIgnoredProperties(), GetStringCase()); + foreach (var flattenInfo in flattenMapper.BuildMemberMapping()) + { + MapMemberFlattened(flattenInfo.DestAsMemberExpression(), flattenInfo.SourceAsExpression()); + } + + FlattenMembers = TranslateExpression(FlattenMembers); + foreach (var keyValue in FlattenMembers) + { + MapMember(keyValue.Key, keyValue.Value); + } + } + } + + protected List> TranslateExpression( + IEnumerable> expressions) + { + var result = new List>(expressions.Count()); + foreach (var customMember in expressions) + { + var substVisitorDest = new SubstituteParameterVisitor(DestFakeParameter); + var dest = substVisitorDest.Visit(customMember.Key) as MemberExpression; + + var substVisitorSrc = new SubstituteParameterVisitor(SourceParameter); + var src = substVisitorSrc.Visit(customMember.Value); + result.Add(new KeyValuePair(dest, src)); + } + return result; + } + + public void ImportMemberConfigParameters(IMemberConfigParameters baseClassConfiguration) + { + Flattened = baseClassConfiguration.Flattened; + CaseSensetiveMember = baseClassConfiguration.CaseSensetiveMember; + CaseSensetiveOverride = baseClassConfiguration.CaseSensetiveOverride; + CompilationTypeOverride = baseClassConfiguration.CompilationTypeOverride; + CompilationTypeMember = baseClassConfiguration.CompilationTypeMember; + + // todo : implement visitor to replace base type to the subclass' type + CustomFunctionMembers = + new List>(baseClassConfiguration.CustomFunctionMembers + .Count); + CustomMembers = + new List>(baseClassConfiguration.CustomMembers.Count); + FlattenMembers = + new List>(baseClassConfiguration.FlattenMembers.Count); + + IgnoreMemberList = + new List(baseClassConfiguration.IgnoreMemberList); + + var replaceDestMemberTypeVisitor = new ReplaceMemberTypeVisitor(DestinationType, DestFakeParameter); + var replaceSrcMemberTypeVisitor = new ReplaceMemberTypeVisitor(SourceType, SourceParameter); + + foreach (var customMember in baseClassConfiguration.CustomMembers) + { + var destExp = replaceDestMemberTypeVisitor.Visit(customMember.Key) as MemberExpression; + var srcExp = replaceSrcMemberTypeVisitor.Visit(customMember.Value); + CustomMembers.Add(new KeyValuePair(destExp, srcExp)); + } + + foreach (var customMember in baseClassConfiguration.CustomFunctionMembers) + { + var destExp = replaceDestMemberTypeVisitor.Visit(customMember.Key) as MemberExpression; + var srcExp = replaceSrcMemberTypeVisitor.Visit(customMember.Value); + CustomFunctionMembers.Add(new KeyValuePair(destExp, srcExp)); + } + + foreach (var customMember in baseClassConfiguration.FlattenMembers) + { + var destExp = replaceDestMemberTypeVisitor.Visit(customMember.Key) as MemberExpression; + var srcExp = replaceSrcMemberTypeVisitor.Visit(customMember.Value); + FlattenMembers.Add(new KeyValuePair(destExp, srcExp)); + } + } + } + + /// + /// ReplaceMemberTypeVisitor + /// + public class ReplaceMemberTypeVisitor : ExpressionVisitor + { + private readonly Type _replacementType; + private readonly Expression _instanceExp; + + /// + /// ReplaceMemberTypeVisitor constructor + /// + public ReplaceMemberTypeVisitor(Type replacementType, Expression instanceExp) + { + _replacementType = replacementType; + _instanceExp = instanceExp; + } + + protected override Expression VisitMember(MemberExpression node) + { + return node.Member.DeclaringType.GetInfo().IsAssignableFrom(_replacementType) + ? Expression.PropertyOrField(_instanceExp, node.Member.Name) + : base.VisitMember(node); + } + } +}