Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #524. Adding support for working with Types packed into an object to the standard adapter #645

Open
wants to merge 1 commit into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions src/Mapster.Tests/WhenMappingObjectRegression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;

namespace Mapster.Tests
{
[TestClass]
public class WhenMappingObjectRegression
{
/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/524
/// </summary>
[TestMethod]
public void TSourceIsObjectUpdate()
{
var source = new Source524 { X1 = 123 };
var _result = Somemap(source);

_result.X1.ShouldBe(123);
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/524
/// </summary>
[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>();
((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>();
((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
}
}
45 changes: 1 addition & 44 deletions src/Mapster.Tests/WhenMappingRecordRegression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -194,26 +194,6 @@ public void UpdateNullable()

}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/524
/// </summary>
[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;
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/569
/// </summary>
Expand Down Expand Up @@ -268,29 +248,6 @@ public void CollectionUpdate()
destination.Count.ShouldBe(_result.Count);
}

/// <summary>
/// https://github.com/MapsterMapper/Mapster/issues/524
/// Not work. Already has a special overload:
/// .Adapt(this object source, object destination, Type sourceType, Type destinationType)
/// </summary>
[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

}
Expand Down
27 changes: 27 additions & 0 deletions src/Mapster/TypeAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,37 @@ public static TDestination Adapt<TDestination>(this object? source, TypeAdapterC
/// <returns>Adapted destination type.</returns>
public static TDestination Adapt<TSource, TDestination>(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<TSource, TDestination>();
return fn(source, destination);
}

private static TDestination UpdateFuncFromPackedinObject<TSource, TDestination>(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);
}
}

/// <summary>
/// Adapt the source object to the destination type.
/// </summary>
Expand Down
Loading