diff --git a/src/Mapster.Tests/WhenMappingObjectRegression.cs b/src/Mapster.Tests/WhenMappingObjectRegression.cs new file mode 100644 index 00000000..d6cf7776 --- /dev/null +++ b/src/Mapster.Tests/WhenMappingObjectRegression.cs @@ -0,0 +1,156 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; + +namespace Mapster.Tests +{ + [TestClass] + public class WhenMappingObjectRegression + { + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + [TestMethod] + public void TSourceIsObjectUpdate() + { + var source = new Source524 { X1 = 123 }; + var _result = Somemap(source); + + _result.X1.ShouldBe(123); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + [TestMethod] + public void TSourceIsObjectUpdateUseDynamicCast() + { + var source = new Source524 { X1 = 123 }; + var _result = SomemapWithDynamic(source); + + _result.X1.ShouldBe(123); + } + + [TestMethod] + public void UpdateManyDest() + { + var source = new Source524 { X1 = 123 }; + var _result = SomemapManyDest(source); + + _result.X1.ShouldBe(123); + _result.X2.ShouldBe(127); + } + + [TestMethod] + public void UpdateToRealObject() + { + var source = new Source524 { X1 = 123 }; + var RealObject = new Object(); + + var _result = source.Adapt(RealObject); + + _result.ShouldBeOfType(); + ((Source524)_result).X1.ShouldBe(source.X1); + + } + + [TestMethod] + public void RealObjectCastToDestination() /// Warning potential Infinity Loop in ObjectAdapter!!! + { + var source = new Source524 { X1 = 123 }; + var RealObject = new Object(); + + var _result = RealObject.Adapt(source); + + _result.ShouldBeOfType(); + ((Source524)_result).X1.ShouldBe(source.X1); + } + + [TestMethod] + public void UpdateObjectInsaider() + { + var _source = new InsaderObject() { X1 = 1 }; + var _Destination = new InsaderObject() { X1 = 2 }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + [TestMethod] + public void UpdateObjectInsaiderToObject() + { + var _source = new InsaderObject() { X1 = 1 }; + var _Destination = new InsaderObject() { X1 = new Object() }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + [TestMethod] + public void UpdateObjectInsaiderWhenObjectinTSource() + { + var _source = new InsaderObject() { X1 = new Object() }; + var _Destination = new InsaderObject() { X1 = 3 }; + + var _result = _source.Adapt(_Destination); + + _result.X1.ShouldBe(_source.X1); + } + + + #region TestFunctions + + Dest524 Somemap(object source) + { + var dest = new Dest524 { X1 = 321 }; + var dest1 = source.Adapt(dest); + + return dest; + } + + ManyDest524 SomemapManyDest(object source) + { + var dest = new ManyDest524 { X1 = 321, X2 = 127 }; + var dest1 = source.Adapt(dest); + + return dest; + } + + Dest524 SomemapWithDynamic(object source) + { + var dest = new Dest524 { X1 = 321 }; + var dest1 = source.Adapt(dest, source.GetType(), dest.GetType()); + + return dest; + } + + #endregion TestFunctions + + #region TestClasses + class Source524 + { + public int X1 { get; set; } + } + class Dest524 + { + public int X1 { get; set; } + } + + class ManyDest524 + { + public int X1 { get; set;} + + public int X2 { get; set;} + } + + class InsaderObject + { + public Object X1 { get; set;} + } + + + #endregion TestClasses + } +} diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index f5ef6add..37bde9c4 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -41,7 +41,7 @@ public void AdaptRecordStructToRecordStruct() var _structResult = _sourceStruct.Adapt(_destinationStruct); _structResult.X.ShouldBe(1000); - object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); + _structResult.X.ShouldNotBe(_destinationStruct.X); } [TestMethod] @@ -194,26 +194,6 @@ public void UpdateNullable() } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// - [TestMethod] - public void TSousreIsObjectUpdateUseDynamicCast() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = SomemapWithDynamic(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr SomemapWithDynamic(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest,source.GetType(),dest.GetType()); - - return dest; - } - /// /// https://github.com/MapsterMapper/Mapster/issues/569 /// @@ -268,29 +248,6 @@ public void CollectionUpdate() destination.Count.ShouldBe(_result.Count); } - /// - /// https://github.com/MapsterMapper/Mapster/issues/524 - /// Not work. Already has a special overload: - /// .Adapt(this object source, object destination, Type sourceType, Type destinationType) - /// - [Ignore] - [TestMethod] - public void TSousreIsObjectUpdate() - { - var source = new TestClassPublicCtr { X = 123 }; - var _result = Somemap(source); - - _result.X.ShouldBe(123); - } - - TestClassPublicCtr Somemap(object source) - { - var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest); // typeof(TSource) always return Type as Object. Need use dynamic or Cast to Runtime Type before Adapt - - return dest; - } - #endregion NowNotWorking } diff --git a/src/Mapster/TypeAdapter.cs b/src/Mapster/TypeAdapter.cs index 2e1db2f1..0b92359b 100644 --- a/src/Mapster/TypeAdapter.cs +++ b/src/Mapster/TypeAdapter.cs @@ -93,10 +93,37 @@ public static TDestination Adapt(this TSource source, TDe /// Adapted destination type. public static TDestination Adapt(this TSource source, TDestination destination, TypeAdapterConfig config) { + var sourceType = source.GetType(); + var destinationType = destination.GetType(); + + if (sourceType == typeof(object)) // Infinity loop in ObjectAdapter if Runtime Type of source is Object + return destination; + + if (typeof(TSource) == typeof(object) || typeof(TDestination) == typeof(object)) + return UpdateFuncFromPackedinObject(source, destination, config, sourceType, destinationType); + var fn = config.GetMapToTargetFunction(); return fn(source, destination); } + private static TDestination UpdateFuncFromPackedinObject(TSource source, TDestination destination, TypeAdapterConfig config, Type sourceType, Type destinationType) + { + dynamic del = config.GetMapToTargetFunction(sourceType, destinationType); + + + if (sourceType.GetTypeInfo().IsVisible && destinationType.GetTypeInfo().IsVisible) + { + dynamic objfn = del; + return objfn((dynamic)source, (dynamic)destination); + } + else + { + //NOTE: if type is non-public, we cannot use dynamic + //DynamicInvoke is slow, but works with non-public + return (TDestination)del.DynamicInvoke(source, destination); + } + } + /// /// Adapt the source object to the destination type. ///