From f7feaa257071c0467b4ad1f7431dfd3bd3a3d769 Mon Sep 17 00:00:00 2001 From: Yuriy Anisimov Date: Fri, 13 Jan 2017 19:35:25 -0800 Subject: [PATCH 1/5] Inheritance support - NOT READY! --- .../ExpressMapper NET40.csproj | 42 +- .../ExpressMapper NET45.csproj | 97 +- .../ExpressMapper Net46.csproj | 100 +- .../ExpressMapper PCL NET45.csproj | 100 +- ExpressMapper.Tests NET40/BasicTests.cs | 21 + .../ExpressMapper.Tests.Model.csproj | 4 + .../Models/BaseControl.cs | 12 + .../Models/Organization.cs | 7 +- ExpressMapper.Tests.Model/Models/TextBox.cs | 7 + .../ViewModels/BaseControlViewModel.cs | 12 + .../ViewModels/TextBoxViewModel.cs | 7 + ExpressMapper.sln | 9 + .../CollectionTypes.cs | 0 .../CompilationTypes.cs | 0 .../Constants.cs | 0 .../DefaultMappingContext.cs | 0 .../DelegateCustomTypeMapper.cs | 46 +- .../DestinationMappingService.cs | 0 .../DestinationTypeMapper.cs | 0 .../Expressmapper.Shared.projitems | 51 + .../Expressmapper.Shared.shproj | 13 + .../ExpressmapperException.cs | 0 .../ExpressmapperExtensions.cs | 0 .../FlattenLinqMethod.cs | 0 .../FlattenMapper.cs | 0 .../FlattenMemberInfo.cs | 0 .../ICustomTypeMapper.cs | 0 .../IMappingContext.cs | 0 .../IMappingService.cs | 44 +- .../IMappingServiceProvider.cs | 2 + .../IMemberConfigParameters.cs | 18 + .../IMemberConfiguration.cs | 51 +- .../ITypeMapper.cs | 3 + .../MapNotImplemented.cs | 62 +- .../Mapper.cs | 194 ++-- .../MappingServiceBase.cs | 942 +++++++++--------- .../MappingServiceProvider.cs | 109 +- .../MemberConfiguration.cs | 373 +++---- .../NullCheckNestedMemberVisitor.cs | 0 .../PreciseSubstituteParameterVisitor.cs | 0 .../ProjectionAccessMemberVisitor.cs | 0 .../ReturnTypeDifferenceVisitor.cs | 0 .../SourceMappingService.cs | 0 .../SourceTypeMapper.cs | 0 .../StaticExpressions.cs | 20 +- .../SubstituteParameterVisitor.cs | 0 .../TypeExtensions.cs | 0 .../TypeMapperBase.cs | 909 ++++++++--------- 48 files changed, 1594 insertions(+), 1661 deletions(-) create mode 100644 ExpressMapper.Tests.Model/Models/BaseControl.cs create mode 100644 ExpressMapper.Tests.Model/Models/TextBox.cs create mode 100644 ExpressMapper.Tests.Model/ViewModels/BaseControlViewModel.cs create mode 100644 ExpressMapper.Tests.Model/ViewModels/TextBoxViewModel.cs rename {ExpressMapper NET40 => Expressmapper.Shared}/CollectionTypes.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/CompilationTypes.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/Constants.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/DefaultMappingContext.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/DelegateCustomTypeMapper.cs (95%) rename {ExpressMapper NET40 => Expressmapper.Shared}/DestinationMappingService.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/DestinationTypeMapper.cs (100%) create mode 100644 Expressmapper.Shared/Expressmapper.Shared.projitems create mode 100644 Expressmapper.Shared/Expressmapper.Shared.shproj rename {ExpressMapper NET40 => Expressmapper.Shared}/ExpressmapperException.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/ExpressmapperExtensions.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/FlattenLinqMethod.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/FlattenMapper.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/FlattenMemberInfo.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/ICustomTypeMapper.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/IMappingContext.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/IMappingService.cs (97%) rename {ExpressMapper NET40 => Expressmapper.Shared}/IMappingServiceProvider.cs (88%) create mode 100644 Expressmapper.Shared/IMemberConfigParameters.cs rename {ExpressMapper NET40 => Expressmapper.Shared}/IMemberConfiguration.cs (95%) rename {ExpressMapper NET40 => Expressmapper.Shared}/ITypeMapper.cs (90%) rename {ExpressMapper NET40 => Expressmapper.Shared}/MapNotImplemented.cs (96%) rename {ExpressMapper NET40 => Expressmapper.Shared}/Mapper.cs (96%) rename {ExpressMapper NET40 => Expressmapper.Shared}/MappingServiceBase.cs (98%) rename {ExpressMapper NET40 => Expressmapper.Shared}/MappingServiceProvider.cs (82%) rename {ExpressMapper NET40 => Expressmapper.Shared}/MemberConfiguration.cs (93%) rename {ExpressMapper NET40 => Expressmapper.Shared}/NullCheckNestedMemberVisitor.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/PreciseSubstituteParameterVisitor.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/ProjectionAccessMemberVisitor.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/ReturnTypeDifferenceVisitor.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/SourceMappingService.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/SourceTypeMapper.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/StaticExpressions.cs (96%) rename {ExpressMapper NET40 => Expressmapper.Shared}/SubstituteParameterVisitor.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/TypeExtensions.cs (100%) rename {ExpressMapper NET40 => Expressmapper.Shared}/TypeMapperBase.cs (85%) 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/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/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/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/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.Shared/Expressmapper.Shared.shproj b/Expressmapper.Shared/Expressmapper.Shared.shproj index 64b3157..a94bd46 100644 --- a/Expressmapper.Shared/Expressmapper.Shared.shproj +++ b/Expressmapper.Shared/Expressmapper.Shared.shproj @@ -10,4 +10,4 @@ - + \ No newline at end of file diff --git a/Expressmapper.Shared/IMemberConfiguration.cs b/Expressmapper.Shared/IMemberConfiguration.cs index cd74b41..3beb2b8 100644 --- a/Expressmapper.Shared/IMemberConfiguration.cs +++ b/Expressmapper.Shared/IMemberConfiguration.cs @@ -21,6 +21,6 @@ public interface IMemberConfiguration IMemberConfiguration CaseSensitive(bool caseSensitive); IMemberConfiguration CompileTo(CompilationTypes compilationType); IMemberConfiguration Flatten(); - IMemberConfiguration Include(); + IMemberConfiguration Include(); } } diff --git a/Expressmapper.Shared/ITypeMapper.cs b/Expressmapper.Shared/ITypeMapper.cs index 67a84b7..c2768fe 100644 --- a/Expressmapper.Shared/ITypeMapper.cs +++ b/Expressmapper.Shared/ITypeMapper.cs @@ -7,6 +7,7 @@ namespace ExpressMapper { public interface ITypeMapper { + bool BaseType { get; set; } Type SourceType { get; } Type DestinationType { get; } Expression QueryableGeneralExpression { get; } @@ -22,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); diff --git a/Expressmapper.Shared/MappingServiceBase.cs b/Expressmapper.Shared/MappingServiceBase.cs index 942ff11..ba2ac10 100644 --- a/Expressmapper.Shared/MappingServiceBase.cs +++ b/Expressmapper.Shared/MappingServiceBase.cs @@ -63,7 +63,8 @@ protected Tuple, ParameterExpression, ParameterExpression> GetM 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)); + 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) diff --git a/Expressmapper.Shared/MappingServiceProvider.cs b/Expressmapper.Shared/MappingServiceProvider.cs index 5ea7edf..6766acf 100644 --- a/Expressmapper.Shared/MappingServiceProvider.cs +++ b/Expressmapper.Shared/MappingServiceProvider.cs @@ -113,8 +113,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)) && @@ -138,8 +138,11 @@ private IMemberConfiguration RegisterInternal(bool memberCaseInsen SourceService.TypeMappers[cacheKey] = sourceClassMapper; DestinationService.TypeMappers[cacheKey] = destinationClassMapper; - return - new MemberConfiguration(new ITypeMapper[] { sourceClassMapper, destinationClassMapper }, this); + var memberConfiguration = new MemberConfiguration( + new ITypeMapper[] {sourceClassMapper, destinationClassMapper}, this); + sourceClassMapper.MemberConfiguration = memberConfiguration; + destinationClassMapper.MemberConfiguration = memberConfiguration; + return memberConfiguration; } } diff --git a/Expressmapper.Shared/MemberConfiguration.cs b/Expressmapper.Shared/MemberConfiguration.cs index 247c338..a62ef70 100644 --- a/Expressmapper.Shared/MemberConfiguration.cs +++ b/Expressmapper.Shared/MemberConfiguration.cs @@ -179,9 +179,15 @@ public IMemberConfiguration Flatten() return this; } - public IMemberConfiguration Include() + public IMemberConfiguration Include() { - return _mappingServiceProvider.Register(_typeMappers.First() as IMemberConfigParameters); + foreach (var typeMapper in _typeMappers) + { + typeMapper.BaseType = true; + } + + _mappingServiceProvider.Register(_typeMappers.First() as IMemberConfigParameters); + return this; } #endregion diff --git a/Expressmapper.Shared/TypeMapperBase.cs b/Expressmapper.Shared/TypeMapperBase.cs index 0d7c211..d98d597 100644 --- a/Expressmapper.Shared/TypeMapperBase.cs +++ b/Expressmapper.Shared/TypeMapperBase.cs @@ -23,6 +23,10 @@ public abstract class TypeMapperBase : IMemberConfigParameters 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; } @@ -96,7 +100,8 @@ public void CompileTo(CompilationTypes compileType) public void Compile(CompilationTypes compilationType, bool forceByDemand = false) { - if (!forceByDemand && ((CompilationTypeOverride && (MapperType & CompilationTypeMember) != MapperType) || (!CompilationTypeOverride && (MapperType & compilationType) != MapperType))) + if (!forceByDemand && ((CompilationTypeOverride && (MapperType & CompilationTypeMember) != MapperType) || + (!CompilationTypeOverride && (MapperType & compilationType) != MapperType))) { return; } @@ -116,7 +121,8 @@ public void Compile(CompilationTypes compilationType, bool forceByDemand = false 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); + $"Error error occured trying to compile mapping for: source {typeof(T).FullName}, destination {typeof(TN).FullName}. See the inner exception for details.", + ex); } } finally @@ -144,13 +150,15 @@ public void AfterMap(Action afterMap) public Tuple, ParameterExpression, ParameterExpression> GetMapExpressions() { - if (_compiling) + if (_compiling || BaseType) { - return new Tuple, ParameterExpression, ParameterExpression>(new List(RecursiveExpressionResult), SourceParameter, DestFakeParameter); + return new Tuple, ParameterExpression, ParameterExpression>( + new List(RecursiveExpressionResult), SourceParameter, DestFakeParameter); } Compile(MapperType); - return new Tuple, ParameterExpression, ParameterExpression>(new List(ResultExpressionList), SourceParameter, DestFakeParameter); + return new Tuple, ParameterExpression, ParameterExpression>( + new List(ResultExpressionList), SourceParameter, DestFakeParameter); } public Func GetNonGenericMapFunc() @@ -171,18 +179,20 @@ public Func GetNonGenericMapFunc() 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 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 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); + 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; @@ -196,7 +206,8 @@ protected void AutoMapProperty(MemberInfo propertyGet, MemberInfo propertySet) MapMember(callSetPropMethod, callGetPropMethod); } - public void MapMember(Expression> left, Expression> right) + public void MapMember(Expression> left, + Expression> right) { if (left == null) { @@ -208,7 +219,8 @@ public void MapMember(Expression(left.Body as MemberExpression, right.Body)); + CustomMembers.Add( + new KeyValuePair(left.Body as MemberExpression, right.Body)); //MapMember(left.Body as MemberExpression, right.Body); } @@ -261,14 +273,18 @@ protected BinaryExpression GetDestionationVariable() protected void ProcessAutoProperties() { var getFields = - typeof(T).GetInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + typeof(T).GetInfo() + .GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); var setFields = - typeof(TN).GetInfo().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + typeof(TN).GetInfo() + .GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); var getProps = - typeof(T).GetInfo().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + typeof(T).GetInfo() + .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); var setProps = - typeof(TN).GetInfo().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + typeof(TN).GetInfo() + .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); var sourceMembers = getFields.Cast().Union(getProps); var destMembers = setFields.Cast().Union(setProps); @@ -281,14 +297,16 @@ protected void ProcessAutoProperties() foreach (var prop in sourceMembers) { - if (IgnoreMemberList.Contains(prop.Name, comparer) || CustomPropertyCache.Keys.Contains(prop.Name, comparer)) + 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))) + if ((propertyInfo == null && setprop == null) || + (propertyInfo != null && (!propertyInfo.CanWrite || !propertyInfo.GetSetMethod(true).IsPublic))) { IgnoreMemberList.Add(prop.Name); continue; @@ -374,7 +392,8 @@ public void MapFunction(Expression> left, var parameterExpression = Expression.Parameter(typeof(T)); var rightExpression = Expression.Invoke(expr, parameterExpression); - CustomFunctionMembers.Add(new KeyValuePair(memberExpression, rightExpression)); + CustomFunctionMembers.Add( + new KeyValuePair(memberExpression, rightExpression)); //MapFunction(left, rightExpression, memberExpression); } @@ -382,7 +401,8 @@ protected void MapFunction(MemberExpression left, Expression rightExpression) { if (left.Member.DeclaringType != rightExpression.Type) { - var mapComplexResult = MappingService.GetDifferentTypeMemberMappingExpression(rightExpression, left, false); + var mapComplexResult = + MappingService.GetDifferentTypeMemberMappingExpression(rightExpression, left, false); CustomPropertyCache[left.Member.Name] = mapComplexResult; } else @@ -428,7 +448,8 @@ protected void ProcessFlattenedMembers() } } - protected List> TranslateExpression(IEnumerable> expressions) + protected List> TranslateExpression( + IEnumerable> expressions) { var result = new List>(expressions.Count()); foreach (var customMember in expressions) @@ -452,11 +473,63 @@ public void ImportMemberConfigParameters(IMemberConfigParameters baseClassConfig CompilationTypeMember = baseClassConfiguration.CompilationTypeMember; // todo : implement visitor to replace base type to the subclass' type - CustomFunctionMembers = new List>(baseClassConfiguration.CustomFunctionMembers); - CustomMembers = new List>(baseClassConfiguration.CustomMembers); - FlattenMembers = new List>(baseClassConfiguration.FlattenMembers); + 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)); + } - IgnoreMemberList = new List(baseClassConfiguration.IgnoreMemberList); + 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.IsAssignableFrom(_replacementType) + ? Expression.PropertyOrField(_instanceExp, node.Member.Name) + : base.VisitMember(node); + } } } -} From 8e1a608b0b8814e08ad0a8cde278d79c7622579b Mon Sep 17 00:00:00 2001 From: Yuriy Anisimov Date: Tue, 17 Jan 2017 11:44:03 -0800 Subject: [PATCH 3/5] Inheritance final commit --- ExpressMapper.Tests NET40/BasicTests.cs | 6 ++- ExpressMapper.Tests NET40/CollectionTests.cs | 43 +++++++++++++++++++ ExpressMapper.Tests NET40/ExceptionTests.cs | 22 +++++----- Expressmapper.Shared/IMemberConfiguration.cs | 4 +- .../MappingServiceProvider.cs | 21 +++++++-- Expressmapper.Shared/MemberConfiguration.cs | 23 +++++----- 6 files changed, 90 insertions(+), 29 deletions(-) diff --git a/ExpressMapper.Tests NET40/BasicTests.cs b/ExpressMapper.Tests NET40/BasicTests.cs index 20b4619..30dcc31 100644 --- a/ExpressMapper.Tests NET40/BasicTests.cs +++ b/ExpressMapper.Tests NET40/BasicTests.cs @@ -774,7 +774,8 @@ public void CustomMapNonGeneric() public void AccessSourceNestedProperty() { Mapper.Register() - .Member(dest => dest.Name, src => string.Format("Test - {0} - and date: {1} plus {2}", src.Country.Name, DateTime.Now, src.Country.Code)); + .Member(dest => dest.Name, src => + $"Test - {src.Country.Name} - and date: {DateTime.Now} plus {src.Country.Code}"); Mapper.Register(); Mapper.Register(); @@ -790,7 +791,8 @@ public void AccessSourceNestedProperty() public void AccessSourceManyNestedProperties() { Mapper.Register() - .Member(dest => dest.Name, src => string.Format("Type: {0}, Catalog: {1}, Category: {2}", src.Category.Catalog.TripType.Name, src.Category.Catalog.Name, src.Category.Name)) + .Member(dest => dest.Name, src => + $"Type: {src.Category.Catalog.TripType.Name}, Catalog: {src.Category.Catalog.Name}, Category: {src.Category.Name}") .Ignore(dest => dest.Category); Mapper.Compile(); diff --git a/ExpressMapper.Tests NET40/CollectionTests.cs b/ExpressMapper.Tests NET40/CollectionTests.cs index c958f14..0fa9332 100644 --- a/ExpressMapper.Tests NET40/CollectionTests.cs +++ b/ExpressMapper.Tests NET40/CollectionTests.cs @@ -306,6 +306,49 @@ public void CollectionWithDestinationMap() Assert.AreEqual("ObjectA2", result[1].Childs[0].Code); } + [Test] + public void InheritanceIncludeTest() + { + Mapper.Register() + .Member(dst => dst.id_ctrl, src => src.Id) + .Member(dst => dst.name_ctrl, src => src.Name) + .Include() + .Include(); + + Mapper.Register() + .Member(dest => dest.AmountOfElements, src => src.NumberOfElements); + + Mapper.Compile(); + + var textBox = new TextBox + { + Id = Guid.NewGuid(), + Name = "Just a text box", + Description = "Just a text box - very simple description", + Text = "Hello World!" + }; + + var comboBox = new ComboBox + { + Id = Guid.NewGuid(), + Name = "Just a combo box", + Description = "Just a combo box - very simple description", + GeneralName = "Super Combo mombo", + NumberOfElements = 103 + }; + + var controls = new List + { + textBox, comboBox + }; + + var controlVms = Mapper.Map, IEnumerable>(controls); + Assert.NotNull(controlVms); + Assert.True(controlVms.Any()); + Assert.True(controlVms.Any(c => c is TextBoxViewModel)); + Assert.True(controlVms.Any(c => c is ComboBoxViewModel)); + } + [Test] public void NestedInheritanceIncludeTest() { diff --git a/ExpressMapper.Tests NET40/ExceptionTests.cs b/ExpressMapper.Tests NET40/ExceptionTests.cs index 57400f0..d8e8469 100644 --- a/ExpressMapper.Tests NET40/ExceptionTests.cs +++ b/ExpressMapper.Tests NET40/ExceptionTests.cs @@ -10,19 +10,19 @@ namespace ExpressMapper.Tests [TestFixture] public class ExceptionTests : BaseTestClass { - [Test] - public void MappingRegisteredMoreThanOnceTest() - { - Mapper.Register(); + //[Test] + //public void MappingRegisteredMoreThanOnceTest() + //{ + // Mapper.Register(); - var exceptionMessage = string.Format("Mapping from {0} to {1} is already registered", - typeof (Size).FullName, typeof (SizeViewModel).FullName); - Assert.Throws(() => - { - Mapper.Register(); + // var exceptionMessage = string.Format("Mapping from {0} to {1} is already registered", + // typeof (Size).FullName, typeof (SizeViewModel).FullName); + // Assert.Throws(() => + // { + // Mapper.Register(); - }, exceptionMessage); - } + // }, exceptionMessage); + //} [Test] public void RegisteringCollectionTypesTest() diff --git a/Expressmapper.Shared/IMemberConfiguration.cs b/Expressmapper.Shared/IMemberConfiguration.cs index 3beb2b8..0fed812 100644 --- a/Expressmapper.Shared/IMemberConfiguration.cs +++ b/Expressmapper.Shared/IMemberConfiguration.cs @@ -21,6 +21,8 @@ public interface IMemberConfiguration IMemberConfiguration CaseSensitive(bool caseSensitive); IMemberConfiguration CompileTo(CompilationTypes compilationType); IMemberConfiguration Flatten(); - IMemberConfiguration Include(); + + IMemberConfiguration Include() where TSub : T + where TNSub : TN; } } diff --git a/Expressmapper.Shared/MappingServiceProvider.cs b/Expressmapper.Shared/MappingServiceProvider.cs index 6766acf..b5c8dd7 100644 --- a/Expressmapper.Shared/MappingServiceProvider.cs +++ b/Expressmapper.Shared/MappingServiceProvider.cs @@ -65,6 +65,11 @@ public IMemberConfiguration Register() return RegisterInternal(); } + private IMemberConfiguration Register(T src, TN dest) + { + return RegisterInternal(); + } + public IMemberConfiguration Register(IMemberConfigParameters baseType) { var memberConfiguration = Register(); @@ -279,7 +284,7 @@ public TN Map(T src) public TN Map(T src, TN dest) { - if (src.GetType() == typeof(T) && (dest.GetType() == typeof(TN)/* || dest == default(TN)*/)) + if (src.GetType() == typeof(T) && (dest.GetType() == typeof(TN))) { return MapInternal(src, dest); } @@ -374,7 +379,7 @@ 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) { if (src == null) { @@ -476,8 +481,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.Shared/MemberConfiguration.cs b/Expressmapper.Shared/MemberConfiguration.cs index a62ef70..ef86ca7 100644 --- a/Expressmapper.Shared/MemberConfiguration.cs +++ b/Expressmapper.Shared/MemberConfiguration.cs @@ -154,6 +154,18 @@ public IMemberConfiguration CompileTo(CompilationTypes 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 /// @@ -179,17 +191,6 @@ public IMemberConfiguration Flatten() return this; } - public IMemberConfiguration Include() - { - foreach (var typeMapper in _typeMappers) - { - typeMapper.BaseType = true; - } - - _mappingServiceProvider.Register(_typeMappers.First() as IMemberConfigParameters); - return this; - } - #endregion } From 3f418158ecde0537e60dc46752680a3ce10df8ad Mon Sep 17 00:00:00 2001 From: Yuriy Anisimov Date: Tue, 17 Jan 2017 14:44:37 -0800 Subject: [PATCH 4/5] Applying inheritance to DotNetCore --- .idea/.idea.ExpressMapper/.idea/workspace.xml | 3092 +++++++---------- .../DestinationMappingService.cs | 10 +- .../DestinationTypeMapper.cs | 2 +- .../IMappingServiceProvider.cs | 2 + .../IMemberConfigParameters.cs | 18 + ExpressMapper NETCORE/IMemberConfiguration.cs | 3 + ExpressMapper NETCORE/ITypeMapper.cs | 5 + .../MappingServiceProvider.cs | 151 +- ExpressMapper NETCORE/MemberConfiguration.cs | 22 +- ExpressMapper NETCORE/SourceMappingService.cs | 10 +- ExpressMapper NETCORE/SourceTypeMapper.cs | 12 +- ExpressMapper NETCORE/TypeMapperBase.cs | 180 +- ExpressMapper.Tests NETCORE/BasicTests.cs | 91 +- .../CollectionTests.cs | 158 +- ExpressMapper.Tests NETCORE/ExceptionTests.cs | 22 +- .../Models/BaseControl.cs | 12 + .../Models/ComboBox.cs | 8 + .../Models/Gui.cs | 10 + .../Models/TextBox.cs | 7 + .../Models/UserInterface.cs | 7 + .../ViewModels/BaseControlViewModel.cs | 12 + .../ViewModels/ComboBoxViewModel.cs | 8 + .../ViewModels/GuiViewModel.cs | 9 + .../ViewModels/TextBoxViewModel.cs | 7 + .../ViewModels/UserInterfaceViewModel.cs | 7 + .../DestinationMappingService.cs | 2 +- .../MappingServiceProvider.cs | 14 +- Expressmapper.Shared/TypeMapperBase.cs | 44 +- 28 files changed, 1977 insertions(+), 1948 deletions(-) create mode 100644 ExpressMapper NETCORE/IMemberConfigParameters.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/Models/BaseControl.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/Models/ComboBox.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/Models/Gui.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/Models/TextBox.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/Models/UserInterface.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/ViewModels/BaseControlViewModel.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/ViewModels/ComboBoxViewModel.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/ViewModels/GuiViewModel.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/ViewModels/TextBoxViewModel.cs create mode 100644 ExpressMapper.Tests.Model NETCORE/ViewModels/UserInterfaceViewModel.cs diff --git a/.idea/.idea.ExpressMapper/.idea/workspace.xml b/.idea/.idea.ExpressMapper/.idea/workspace.xml index 362995a..b4a3b92 100644 --- a/.idea/.idea.ExpressMapper/.idea/workspace.xml +++ b/.idea/.idea.ExpressMapper/.idea/workspace.xml @@ -2,24 +2,33 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + -