diff --git a/ExpressMapper NET40/Properties/AssemblyInfo.cs b/ExpressMapper NET40/Properties/AssemblyInfo.cs index 784753a..7e01778 100644 --- a/ExpressMapper NET40/Properties/AssemblyInfo.cs +++ b/ExpressMapper NET40/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.9.0.0")] -[assembly: AssemblyFileVersion("1.9.0.0")] -[assembly: AssemblyInformationalVersion("1.9.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.9.1.0")] +[assembly: AssemblyFileVersion("1.9.1.0")] +[assembly: AssemblyInformationalVersion("1.9.1")] \ No newline at end of file diff --git a/ExpressMapper NETCORE/Properties/AssemblyInfo.cs b/ExpressMapper NETCORE/Properties/AssemblyInfo.cs index 971e74c..0518aba 100644 --- a/ExpressMapper NETCORE/Properties/AssemblyInfo.cs +++ b/ExpressMapper NETCORE/Properties/AssemblyInfo.cs @@ -20,6 +20,6 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("969e6614-0489-4174-a735-cc66f51d8746")] -[assembly: AssemblyVersion("1.9.0.0")] -[assembly: AssemblyFileVersion("1.9.0.0")] -[assembly: AssemblyInformationalVersion("1.9.0")] +[assembly: AssemblyVersion("1.9.1.0")] +[assembly: AssemblyFileVersion("1.9.1.0")] +[assembly: AssemblyInformationalVersion("1.9.1")] diff --git a/ExpressMapper NETCORE/TypeMapperBase.cs b/ExpressMapper NETCORE/TypeMapperBase.cs index e1b7b83..b2a764a 100644 --- a/ExpressMapper NETCORE/TypeMapperBase.cs +++ b/ExpressMapper NETCORE/TypeMapperBase.cs @@ -121,7 +121,7 @@ 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.", + $"Error error occured trying to compile mapping for: source {typeof(T).FullName}, destination {typeof(TN).FullName}. See the inner exception for details.", ex); } } @@ -179,18 +179,18 @@ 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 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(); @@ -200,8 +200,8 @@ public Func GetNonGenericMapFunc() protected void AutoMapProperty(MemberInfo propertyGet, MemberInfo propertySet) { - var callSetPropMethod = Expression.PropertyOrField(DestFakeParameter, propertySet.Name); - var callGetPropMethod = Expression.PropertyOrField(SourceParameter, propertyGet.Name); + var callSetPropMethod = propertySet.MemberType == MemberTypes.Field ? Expression.Field(DestFakeParameter, propertySet as FieldInfo) : Expression.Property(DestFakeParameter, propertySet as PropertyInfo); + var callGetPropMethod = propertyGet.MemberType == MemberTypes.Field ? Expression.Field(SourceParameter, propertyGet as FieldInfo) : Expression.Property(SourceParameter, propertyGet as PropertyInfo); MapMember(callSetPropMethod, callGetPropMethod); } @@ -282,6 +282,7 @@ protected void ProcessAutoProperties() var getProps = typeof(T).GetInfo() .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + var setProps = typeof(TN).GetInfo() .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); @@ -293,7 +294,7 @@ protected void ProcessAutoProperties() var comparer = CultureInfo.CurrentCulture.CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase); //var comparer = StringComparer.Create(CultureInfo.CurrentCulture, -// stringComparison == StringComparison.OrdinalIgnoreCase); + // stringComparison == StringComparison.OrdinalIgnoreCase); foreach (var prop in sourceMembers) { @@ -302,18 +303,48 @@ protected void ProcessAutoProperties() { continue; } - var setprop = destMembers.FirstOrDefault(x => string.Equals(x.Name, prop.Name, stringComparison)); + + var notUniqueDestMembers = destMembers.Where(x => string.Equals(x.Name, prop.Name, stringComparison)); + var notUniqueSrcMembers = sourceMembers.Where(x => string.Equals(x.Name, prop.Name, stringComparison)); + + var getprop = GetTopMostMemberOfHierarchy(notUniqueSrcMembers); + if (AutoMembers.ContainsKey(getprop)) + { + continue; + } + + var setprop = GetTopMostMemberOfHierarchy(notUniqueDestMembers); var propertyInfo = setprop as PropertyInfo; if ((propertyInfo == null && setprop == null) || (propertyInfo != null && (!propertyInfo.CanWrite || !propertyInfo.GetSetMethod(true).IsPublic))) { - IgnoreMemberList.Add(prop.Name); + IgnoreMemberList.Add(getprop.Name); continue; } - AutoMembers[prop] = setprop; - AutoMapProperty(prop, setprop); + AutoMembers[getprop] = setprop; + AutoMapProperty(getprop, setprop); + } + } + + private static MemberInfo GetTopMostMemberOfHierarchy(IEnumerable notUniqueMembers) + { + MemberInfo chosen = null; + + foreach (var notUniqueMember in notUniqueMembers) + { + if (chosen == null) + { + chosen = notUniqueMember; + } + else + { + chosen = chosen.DeclaringType.GetInfo().IsAssignableFrom(notUniqueMember.DeclaringType) + ? notUniqueMember + : chosen; + } } + return chosen; } internal StringComparison GetStringCase() @@ -474,8 +505,10 @@ public void ImportMemberConfigParameters(IMemberConfigParameters baseClassConfig // todo : implement visitor to replace base type to the subclass' type CustomFunctionMembers = - new List>(baseClassConfiguration.CustomFunctionMembers.Count); - CustomMembers = new List>(baseClassConfiguration.CustomMembers.Count); + new List>(baseClassConfiguration.CustomFunctionMembers + .Count); + CustomMembers = + new List>(baseClassConfiguration.CustomMembers.Count); FlattenMembers = new List>(baseClassConfiguration.FlattenMembers.Count); diff --git a/ExpressMapper NETCORE/project.json b/ExpressMapper NETCORE/project.json index 4ce29b4..fc67bfc 100644 --- a/ExpressMapper NETCORE/project.json +++ b/ExpressMapper NETCORE/project.json @@ -1,5 +1,5 @@ { - "version": "1.0.0-*", + "version": "1.9.1.0", "buildOptions": { "outputName": "ExpressMapper", "xmlDoc": true diff --git a/ExpressMapper.Tests NET40/BasicTests.cs b/ExpressMapper.Tests NET40/BasicTests.cs index f381efb..ae20a47 100644 --- a/ExpressMapper.Tests NET40/BasicTests.cs +++ b/ExpressMapper.Tests NET40/BasicTests.cs @@ -31,7 +31,7 @@ public void EnumToAnotherEnumMapTest() }; var unitOfWorkViewModel = unitOfWork.Map(); - Assert.AreEqual((int)unitOfWork.State, (int)unitOfWorkViewModel.State); + Assert.AreEqual((int) unitOfWork.State, (int) unitOfWorkViewModel.State); Assert.AreEqual(unitOfWork.Id, unitOfWorkViewModel.Id); } @@ -60,7 +60,7 @@ public void DefaultPrimitiveTypePropertyToStringTest() .Member(dest => dest.TestString, src => src.TestDecimal); Mapper.RegisterCustom(src => src.ToString("#0.00", CultureInfo.InvariantCulture)); Mapper.Compile(); - var test = new TestDefaultDecimal() { TestDecimal = default(decimal) }; + var test = new TestDefaultDecimal() {TestDecimal = default(decimal)}; test.TestDecimal = default(decimal); var result = Mapper.Map(test); //This is where the mapping fails @@ -74,9 +74,9 @@ public void MemberMappingsHasHigherPriorityThanCaseSensetiveTest() .Member(t => t.Enabled, s => s.enabled == "Y"); Mapper.Compile(); - var source = new Source { enabled = "N" }; - var result = Mapper.Map(source); - Assert.AreEqual(result.Enabled, false); + var source = new Source {enabled = "N"}; + var result = Mapper.Map(source); + Assert.AreEqual(result.Enabled, false); } [Test] @@ -293,7 +293,7 @@ public void HiddenInheritedMemberMap() Assert.AreEqual(result, srcDst.Value); } - private void MapBaseMember(IMemberConfiguration mapConfig) + private void MapBaseMember(IMemberConfiguration mapConfig) where T : Gift where TN : GiftViewModel { @@ -421,7 +421,8 @@ public void BeforeMapDuplicateTest() .Before((src, dest) => dest.Name = src.Name) .Before((src, dest) => dest.Name = src.Name) .Ignore(dest => dest.Name)); - Assert.That(exception.Message, Is.EqualTo("BeforeMap already registered for ExpressMapper.Tests.Model.Models.Size")); + Assert.That(exception.Message, + Is.EqualTo("BeforeMap already registered for ExpressMapper.Tests.Model.Models.Size")); var sizeResult = Functional.BeforeMap(); var result = Mapper.Map(sizeResult.Key); @@ -445,7 +446,8 @@ public void AfterMapDuplicateTest() var exception = Assert.Throws(() => Mapper.Register() .After((src, dest) => dest.Name = "OVERRIDE BY AFTER MAP") .After((src, dest) => dest.Name = "Duplicate map")); - Assert.That(exception.Message, Is.EqualTo("AfterMap already registered for ExpressMapper.Tests.Model.Models.Size")); + Assert.That(exception.Message, + Is.EqualTo("AfterMap already registered for ExpressMapper.Tests.Model.Models.Size")); var sizeResult = Functional.AfterMap(); var result = Mapper.Map(sizeResult.Key); Assert.AreEqual(result, sizeResult.Value); @@ -754,7 +756,8 @@ public void NonGenericSimpleWithDestinationMap() var test = Functional.AutoMemberMap(); var resultInstanceHash = test.Value.GetHashCode(); - var testViewModel = Mapper.Map(test.Key, test.Value, typeof(TestModel), typeof(TestViewModel)) as TestViewModel; + var testViewModel = + Mapper.Map(test.Key, test.Value, typeof(TestModel), typeof(TestViewModel)) as TestViewModel; Assert.AreEqual(testViewModel.GetHashCode(), resultInstanceHash); Assert.AreEqual(testViewModel, test.Value); @@ -873,7 +876,7 @@ public void ExistingDestCollEqualsWithNullElement() var testItemHash = testResult.Item2.GetHashCode(); var arrayHash = testResult.Item2.Array.GetHashCode(); var testArr = new List(testResult.Item2.Array.Length); - testArr.AddRange(testResult.Item2.Array.Select(tc => tc == null ? (int?)null : tc.GetHashCode())); + testArr.AddRange(testResult.Item2.Array.Select(tc => tc == null ? (int?) null : tc.GetHashCode())); var result = Mapper.Map(testResult.Item1, testResult.Item2); Assert.AreEqual(result, testResult.Item2); @@ -950,7 +953,7 @@ public void ExistingSrcCollGreater() Assert.AreEqual(result.Collection.ElementAt(i), testResult.Item3.Collection.ElementAt(i)); } } - + [Test] public void ExistingDestDestCollGreater() { @@ -1086,15 +1089,20 @@ public void ExistingDestinationComplex() for (var i = 0; i < result.SubItems.Length; i++) { Assert.AreEqual(result.SubItems[i].GetHashCode(), subItemsHashes[i]); - Assert.AreEqual(result.SubItems[i].Units.GetHashCode(), subItemUnitsCollHashes[result.SubItems[i].GetHashCode()]); + Assert.AreEqual(result.SubItems[i].Units.GetHashCode(), + subItemUnitsCollHashes[result.SubItems[i].GetHashCode()]); for (var j = 0; j < 4; j++) { - Assert.AreEqual(result.SubItems[i].Units[j].GetHashCode(), subItemUnitsHashes[result.SubItems[i].GetHashCode()][j]); - Assert.AreEqual(result.SubItems[i].Units[j].SubUnits.GetHashCode(), subItemUnitSubUnitCollHashes[result.SubItems[i].GetHashCode()][j]); + Assert.AreEqual(result.SubItems[i].Units[j].GetHashCode(), + subItemUnitsHashes[result.SubItems[i].GetHashCode()][j]); + Assert.AreEqual(result.SubItems[i].Units[j].SubUnits.GetHashCode(), + subItemUnitSubUnitCollHashes[result.SubItems[i].GetHashCode()][j]); for (var k = 0; k < 3; k++) { - Assert.AreEqual(result.SubItems[i].Units[j].SubUnits[k].GetHashCode(), subItemUnitSubUnitsHashes[result.SubItems[i].GetHashCode()][result.SubItems[i].Units[j].GetHashCode()][k]); + Assert.AreEqual(result.SubItems[i].Units[j].SubUnits[k].GetHashCode(), + subItemUnitSubUnitsHashes[result.SubItems[i].GetHashCode()][ + result.SubItems[i].Units[j].GetHashCode()][k]); } } } @@ -1144,7 +1152,7 @@ public void EnumMap() Assert.AreEqual(GenderTypes.Men, testViewModel.Gender); Assert.AreEqual(GenderTypes.Women.ToString(), testViewModel.NullableGender); - Assert.AreEqual((int)GenderTypes.Women, testViewModel.GenderIndex); + Assert.AreEqual((int) GenderTypes.Women, testViewModel.GenderIndex); } [Test] @@ -1203,7 +1211,7 @@ public void MemberCaseSensitivityGlobalMapTest() }; var typoCaseViewModel = Mapper.Map(typoCase); - + Assert.AreEqual(typoCaseViewModel.Id, Guid.Empty); Assert.AreEqual(typoCaseViewModel.Name, null); Assert.AreEqual(typoCase.TestId, typoCaseViewModel.TestId); @@ -1416,7 +1424,7 @@ public void NestedInheritanceIncludeTest() Assert.AreEqual(uiViewModel.ControlViewModel.Description, textBox.Description); Assert.AreEqual(uiViewModel.ControlViewModel.id_ctrl, textBox.Id); Assert.AreEqual(uiViewModel.ControlViewModel.name_ctrl, textBox.Name); - Assert.AreEqual(((TextBoxViewModel)uiViewModel.ControlViewModel).Text, textBox.Text); + Assert.AreEqual(((TextBoxViewModel) uiViewModel.ControlViewModel).Text, textBox.Text); } [Test] @@ -1426,7 +1434,70 @@ public void MapNullSourceReturnNullDest() Mapper.Compile(); Assert.IsNull(Mapper.Map(null)); - Assert.IsNull(Mapper.Map(null, (object)null)); + Assert.IsNull(Mapper.Map(null, (object) null)); + } + + #region Duplicate property names in the class hierarchy + + [Test] + public void MapDuplicatePropertyNamesInHierarchyTest() + { + Mapper.Register(); + Mapper.Register(); + + Mapper.Register(); + Mapper.Register(); + Mapper.Compile(); + + + var tnt = new TNT + { + Foo = new AN + { + Id = 4 + } + }; + + var t = new T + { + Foo = new A + { + Id = 5 + } + }; + + var tntResult = t.Map(); + var tResult = tnt.Map(); + + Assert.AreEqual(tntResult.Foo.Id, 5); + Assert.AreEqual(tResult.Foo.Id, 4); + } + + class A + { + public int Id { get; set; } } + + class AN : A + { + public new int Id { get; set; } + } + + class T + { + public A Foo { get; set; } + } + + class TN : T + { + public new AN Foo { get; set; } + } + + class TNT : TN + { + public new AN Foo { get; set; } + } + + #endregion } } diff --git a/ExpressMapper.Tests NETCORE/BasicTests.cs b/ExpressMapper.Tests NETCORE/BasicTests.cs index 497ce7d..e16cabe 100644 --- a/ExpressMapper.Tests NETCORE/BasicTests.cs +++ b/ExpressMapper.Tests NETCORE/BasicTests.cs @@ -11,6 +11,7 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; +//using Moq; namespace ExpressMapper.Tests { @@ -292,7 +293,7 @@ public void HiddenInheritedMemberMap() Assert.AreEqual(result, srcDst.Value); } - private void MapBaseMember(IMemberConfiguration mapConfig) + private void MapBaseMember(IMemberConfiguration mapConfig) where T : Gift where TN : GiftViewModel { @@ -420,7 +421,8 @@ public void BeforeMapDuplicateTest() .Before((src, dest) => dest.Name = src.Name) .Before((src, dest) => dest.Name = src.Name) .Ignore(dest => dest.Name)); - Assert.That(exception.Message, Is.EqualTo("BeforeMap already registered for ExpressMapper.Tests.Model.Models.Size")); + Assert.That(exception.Message, + Is.EqualTo("BeforeMap already registered for ExpressMapper.Tests.Model.Models.Size")); var sizeResult = Functional.BeforeMap(); var result = Mapper.Map(sizeResult.Key); @@ -444,7 +446,8 @@ public void AfterMapDuplicateTest() var exception = Assert.Throws(() => Mapper.Register() .After((src, dest) => dest.Name = "OVERRIDE BY AFTER MAP") .After((src, dest) => dest.Name = "Duplicate map")); - Assert.That(exception.Message, Is.EqualTo("AfterMap already registered for ExpressMapper.Tests.Model.Models.Size")); + Assert.That(exception.Message, + Is.EqualTo("AfterMap already registered for ExpressMapper.Tests.Model.Models.Size")); var sizeResult = Functional.AfterMap(); var result = Mapper.Map(sizeResult.Key); Assert.AreEqual(result, sizeResult.Value); @@ -753,7 +756,8 @@ public void NonGenericSimpleWithDestinationMap() var test = Functional.AutoMemberMap(); var resultInstanceHash = test.Value.GetHashCode(); - var testViewModel = Mapper.Map(test.Key, test.Value, typeof(TestModel), typeof(TestViewModel)) as TestViewModel; + var testViewModel = + Mapper.Map(test.Key, test.Value, typeof(TestModel), typeof(TestViewModel)) as TestViewModel; Assert.AreEqual(testViewModel.GetHashCode(), resultInstanceHash); Assert.AreEqual(testViewModel, test.Value); @@ -774,7 +778,7 @@ public void AccessSourceNestedProperty() { Mapper.Register() .Member(dest => dest.Name, src => - $"Test - {src.Country.Name} - and date: {DateTime.Now} plus {src.Country.Code}"); + $"Test - {src.Country.Name} - and date: {DateTime.Now} plus {src.Country.Code}"); Mapper.Register(); Mapper.Register(); @@ -791,7 +795,7 @@ public void AccessSourceManyNestedProperties() { Mapper.Register() .Member(dest => dest.Name, src => - $"Type: {src.Category.Catalog.TripType.Name}, Catalog: {src.Category.Catalog.Name}, Category: {src.Category.Name}") + $"Type: {src.Category.Catalog.TripType.Name}, Catalog: {src.Category.Catalog.Name}, Category: {src.Category.Name}") .Ignore(dest => dest.Category); Mapper.Compile(); @@ -1085,15 +1089,20 @@ public void ExistingDestinationComplex() for (var i = 0; i < result.SubItems.Length; i++) { Assert.AreEqual(result.SubItems[i].GetHashCode(), subItemsHashes[i]); - Assert.AreEqual(result.SubItems[i].Units.GetHashCode(), subItemUnitsCollHashes[result.SubItems[i].GetHashCode()]); + Assert.AreEqual(result.SubItems[i].Units.GetHashCode(), + subItemUnitsCollHashes[result.SubItems[i].GetHashCode()]); for (var j = 0; j < 4; j++) { - Assert.AreEqual(result.SubItems[i].Units[j].GetHashCode(), subItemUnitsHashes[result.SubItems[i].GetHashCode()][j]); - Assert.AreEqual(result.SubItems[i].Units[j].SubUnits.GetHashCode(), subItemUnitSubUnitCollHashes[result.SubItems[i].GetHashCode()][j]); + Assert.AreEqual(result.SubItems[i].Units[j].GetHashCode(), + subItemUnitsHashes[result.SubItems[i].GetHashCode()][j]); + Assert.AreEqual(result.SubItems[i].Units[j].SubUnits.GetHashCode(), + subItemUnitSubUnitCollHashes[result.SubItems[i].GetHashCode()][j]); for (var k = 0; k < 3; k++) { - Assert.AreEqual(result.SubItems[i].Units[j].SubUnits[k].GetHashCode(), subItemUnitSubUnitsHashes[result.SubItems[i].GetHashCode()][result.SubItems[i].Units[j].GetHashCode()][k]); + Assert.AreEqual(result.SubItems[i].Units[j].SubUnits[k].GetHashCode(), + subItemUnitSubUnitsHashes[result.SubItems[i].GetHashCode()][ + result.SubItems[i].Units[j].GetHashCode()][k]); } } } @@ -1342,6 +1351,26 @@ public void MapExistsTest() Assert.False(Mapper.MapExists(typeof(FlattenFatherSonGrandsonDto), typeof(Father))); } + //[Test] + //public void DoNotUpdateUnchangedPropertyValuesTest() + //{ + // var srcBrand = new Brand + // { + // Id = Guid.NewGuid(), + // Name = "brand" + // }; + + // var existingBrandMock = new Mock().SetupAllProperties(); + // existingBrandMock.Object.Name = "brand"; + + // var destBrand = Mapper.Map(srcBrand, existingBrandMock.Object); + // existingBrandMock.VerifySet(x => x.Name = It.IsAny(), Times.Once()); + + // Assert.AreEqual(destBrand.Id, srcBrand.Id); + // Assert.AreEqual(destBrand.Name, srcBrand.Name); + //} + + [Test] public void InheritanceIncludeTest() { @@ -1407,5 +1436,68 @@ public void MapNullSourceReturnNullDest() Assert.IsNull(Mapper.Map(null)); Assert.IsNull(Mapper.Map(null, (object)null)); } + + #region Duplicate property names in the class hierarchy + + [Test] + public void MapDuplicatePropertyNamesInHierarchyTest() + { + Mapper.Register(); + Mapper.Register(); + + Mapper.Register(); + Mapper.Register(); + Mapper.Compile(); + + + var tnt = new TNT + { + Foo = new AN + { + Id = 4 + } + }; + + var t = new T + { + Foo = new A + { + Id = 5 + } + }; + + var tntResult = t.Map(); + var tResult = tnt.Map(); + + Assert.AreEqual(tntResult.Foo.Id, 5); + Assert.AreEqual(tResult.Foo.Id, 4); + } + + class A + { + public int Id { get; set; } + } + + class AN : A + { + public new int Id { get; set; } + } + + class T + { + public A Foo { get; set; } + } + + class TN : T + { + public new AN Foo { get; set; } + } + + class TNT : TN + { + public new AN Foo { get; set; } + } + + #endregion } } diff --git a/ExpressMapper.Tests NETCORE/project.json b/ExpressMapper.Tests NETCORE/project.json index 9e738ed..51a8655 100644 --- a/ExpressMapper.Tests NETCORE/project.json +++ b/ExpressMapper.Tests NETCORE/project.json @@ -4,17 +4,17 @@ "emitEntryPoint": true }, - "dependencies": { - "NUnit": "3.4.1", - "NUnitLite": "3.4.1", - "FluentAssertions": "4.14.0", - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.0" - }, - "ExpressMapper NETCORE": "1.0.0-*", - "ExpressMapper.Tests.Model NETCORE": "1.0.0-*" + "dependencies": { + "ExpressMapper NETCORE": "1.0.0-*", + "ExpressMapper.Tests.Model NETCORE": "1.0.0-*", + "FluentAssertions": "4.14.0", + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" }, + "NUnit": "3.4.1", + "NUnitLite": "3.4.1" + }, "frameworks": { "netcoreapp1.6": { diff --git a/Expressmapper.Shared/TypeMapperBase.cs b/Expressmapper.Shared/TypeMapperBase.cs index 12013da..d735c5f 100644 --- a/Expressmapper.Shared/TypeMapperBase.cs +++ b/Expressmapper.Shared/TypeMapperBase.cs @@ -179,18 +179,18 @@ 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 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(); @@ -200,8 +200,8 @@ public Func GetNonGenericMapFunc() protected void AutoMapProperty(MemberInfo propertyGet, MemberInfo propertySet) { - var callSetPropMethod = Expression.PropertyOrField(DestFakeParameter, propertySet.Name); - var callGetPropMethod = Expression.PropertyOrField(SourceParameter, propertyGet.Name); + var callSetPropMethod = propertySet.MemberType == MemberTypes.Field ? Expression.Field(DestFakeParameter, propertySet as FieldInfo) : Expression.Property(DestFakeParameter, propertySet as PropertyInfo); + var callGetPropMethod = propertyGet.MemberType == MemberTypes.Field ? Expression.Field(SourceParameter, propertyGet as FieldInfo) : Expression.Property(SourceParameter, propertyGet as PropertyInfo); MapMember(callSetPropMethod, callGetPropMethod); } @@ -282,6 +282,7 @@ protected void ProcessAutoProperties() var getProps = typeof(T).GetInfo() .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); + var setProps = typeof(TN).GetInfo() .GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public); @@ -291,7 +292,7 @@ protected void ProcessAutoProperties() var stringComparison = GetStringCase(); -// var comparer = CultureInfo.CurrentCulture.CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase); + //var comparer = CultureInfo.CurrentCulture.CompareInfo.GetStringComparer(CompareOptions.OrdinalIgnoreCase); var comparer = StringComparer.Create(CultureInfo.CurrentCulture, stringComparison == StringComparison.OrdinalIgnoreCase); @@ -302,18 +303,48 @@ protected void ProcessAutoProperties() { continue; } - var setprop = destMembers.FirstOrDefault(x => string.Equals(x.Name, prop.Name, stringComparison)); + + var notUniqueDestMembers = destMembers.Where(x => string.Equals(x.Name, prop.Name, stringComparison)); + var notUniqueSrcMembers = sourceMembers.Where(x => string.Equals(x.Name, prop.Name, stringComparison)); + + var getprop = GetTopMostMemberOfHierarchy(notUniqueSrcMembers); + if (AutoMembers.ContainsKey(getprop)) + { + continue; + } + + var setprop = GetTopMostMemberOfHierarchy(notUniqueDestMembers); var propertyInfo = setprop as PropertyInfo; if ((propertyInfo == null && setprop == null) || (propertyInfo != null && (!propertyInfo.CanWrite || !propertyInfo.GetSetMethod(true).IsPublic))) { - IgnoreMemberList.Add(prop.Name); + IgnoreMemberList.Add(getprop.Name); continue; } - AutoMembers[prop] = setprop; - AutoMapProperty(prop, setprop); + AutoMembers[getprop] = setprop; + AutoMapProperty(getprop, setprop); + } + } + + private static MemberInfo GetTopMostMemberOfHierarchy(IEnumerable notUniqueMembers) + { + MemberInfo chosen = null; + + foreach (var notUniqueMember in notUniqueMembers) + { + if (chosen == null) + { + chosen = notUniqueMember; + } + else + { + chosen = chosen.DeclaringType.GetInfo().IsAssignableFrom(notUniqueMember.DeclaringType) + ? notUniqueMember + : chosen; + } } + return chosen; } internal StringComparison GetStringCase()