From db8aa28d0bc286fa6379f5cc56ab769dcbdf320c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 24 Sep 2023 22:11:57 +0500 Subject: [PATCH 01/11] partial fix issue #537 not fix - collection issue #430 #546 - object to ToDistination #524 --- .../WhenIgnoringConditionally.cs | 3 +- src/Mapster.Tests/WhenRecordRegration.cs | 415 ++++++++++++++++++ src/Mapster/Adapters/ClassAdapter.cs | 23 +- src/Mapster/Adapters/RecordTypeAdapter.cs | 6 +- src/Mapster/Utils/ReflectionUtils.cs | 61 ++- 5 files changed, 488 insertions(+), 20 deletions(-) create mode 100644 src/Mapster.Tests/WhenRecordRegration.cs diff --git a/src/Mapster.Tests/WhenIgnoringConditionally.cs b/src/Mapster.Tests/WhenIgnoringConditionally.cs index a92106f0..d7377a37 100644 --- a/src/Mapster.Tests/WhenIgnoringConditionally.cs +++ b/src/Mapster.Tests/WhenIgnoringConditionally.cs @@ -160,6 +160,7 @@ public void IgnoreIf_Can_Be_Combined() public void IgnoreIf_Apply_To_RecordType() { TypeAdapterConfig.NewConfig() + .EnableNonPublicMembers(true) // add or .IgnoreIf((src, dest) => src.Name == "TestName", dest => dest.Name) .Compile(); @@ -187,7 +188,7 @@ public class SimpleDto public string Name { get; set; } } - public class SimpleRecord + public class SimpleRecord // or Replace on record { public int Id { get; } public string Name { get; } diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs new file mode 100644 index 00000000..c6d99b51 --- /dev/null +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -0,0 +1,415 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; +using System.Collections.Generic; + +namespace Mapster.Tests +{ + /// + /// Tests for https://github.com/MapsterMapper/Mapster/issues/537 + /// + [TestClass] + + public class WhenRecordRegress + { + + + [TestMethod] + public void AdaptRecord() + { + var _sourse = new TestREcord() { X = 700 }; + + var _destination = new TestREcord() { X = 500 }; + + var _result = _sourse.Adapt(_destination); + + object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + + + var _sourcePositional = new TestRecordPositional(600); + + var _destinationPositional = new TestRecordPositional(900); + + var _positionalResult = _sourcePositional.Adapt(_destinationPositional); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + + + + + var _sourceStruct = new TestRecordStruct() { X = 1000 }; + + var _destinationStruct = new TestRecordStruct() { X = 800 }; + + var _structResult = _sourceStruct.Adapt(_destinationStruct); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + + + _result.X.ShouldBe(700); + + _positionalResult.X.ShouldBe(600); + + _structResult.X.ShouldBe(1000); + } + + + [TestMethod] + public void AdaptRecordToClass() + { + var _sourse = new TestRecordPositional(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + } + + [TestMethod] + public void AdaptClassToRecord() + { + var _sourse = new TestClassProtectedCtr(200); + + var _destination = new TestRecordPositional(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeFalse(); + + _destination.ShouldBeOfType(); + + _result.X.ShouldBe(200); + } + + + + + + [TestMethod] + public void AdaptClassIsNotNewInstanse() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/615 + /// + [TestMethod] + public void AdaptClassIncludeStruct() + { + TypeAdapterConfig + .ForType() + .Map(x => x.TestStruct, x => x.SourceWithStruct.TestStruct); + + var source = new SourceWithClass + { + SourceWithStruct = new SourceWithStruct + { + TestStruct = new TestStruct("A") + } + }; + + var destination = source.Adapt(); + + destination.TestStruct.Property.ShouldBe("A"); + } + + /// + /// https://github.com/MapsterMapper/Mapster/issues/482 + /// + [TestMethod] + public void AdaptClassIsPrivateProperty() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); + + var _result = _sourse.Adapt(_destination); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + + _destination.Name.ShouldBe("Me"); + } + + + /// + /// https://github.com/MapsterMapper/Mapster/issues/427 + /// + [TestMethod] + public void UpdateNullable() + { + var _sourse = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + + var _update = new UpdateUser + { + Id = "123", + + }; + + var configDate = new TypeAdapterConfig(); + configDate.ForType() + .Map(dest => dest.Modified, src => new DateTime(2025, 9, 24)) + .IgnoreNullValues(true); + + + _update.Adapt(_sourse, configDate); + + + + var _sourseEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + + var _updateEmail = new UpdateUser + { + Email = "245@gmail.com", + }; + + + var config = new TypeAdapterConfig(); + config.ForType() + + .IgnoreNullValues(true); + + + var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); + + + _sourse.Id.ShouldBe("123"); + _sourse.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourse.Modified.ShouldBe(new DateTime(2025, 9, 24)); + _sourse.Email.ShouldBe("123@gmail.com"); + + + _sourseEmailUpdate.Id.ShouldBe("123"); + _sourseEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourseEmailUpdate.Modified.ShouldBe(null); + _sourseEmailUpdate.Email.ShouldBe("245@gmail.com"); + + + + } + + } + + + + + + + #region NowNotWorking + + + + + /// + /// https://github.com/MapsterMapper/Mapster/issues/430 + /// + // [TestMethod] + // public void CollectionUpdate() + // { + // List sources = new() + // { + // new(541), + // new(234) + + // }; + + + // var destination = new List(); + + + // var _result = sources.Adapt(destination); + + // destination.Count.ShouldBe(_result.Count); + + // } + + // /// + // /// https://github.com/MapsterMapper/Mapster/issues/524 + // /// + // [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); + + // return dest; + // } + + + + + //} + + + + + + #endregion NowNotWorking + + + + + + #region TestClases + + + class UserAccount + { + public UserAccount(string id, string email, DateTime created) + { + Id = id; + Email = email; + Created = created; + + } + + protected UserAccount() { } + + public string Id { get; set; } + public string? Email { get; set; } + public DateTime Created { get; set; } + public DateTime? Modified { get; set; } + } + + class UpdateUser + { + public string? Id { get; set; } + public string? Email { get; set; } + + public DateTime? Created { get; set; } + public DateTime? Modified { get; set; } + + } + + + + + + + class DestinationWithStruct + { + public TestStruct TestStruct { get; set; } + } + + class SourceWithClass + { + public SourceWithStruct SourceWithStruct { get; set; } + } + + class SourceWithStruct + { + public TestStruct TestStruct { get; set; } + } + + struct TestStruct + { + public string Property { get; } + + public TestStruct(string property) : this() + { + Property = property; + } + } + + + class TestClassPublicCtr + { + public TestClassPublicCtr() + { + + } + + public TestClassPublicCtr(int x) + { + X = x; + } + + public int X { get; set; } + } + + + class TestClassProtectedCtr + { + protected TestClassProtectedCtr() + { + + } + + public TestClassProtectedCtr(int x) + { + X = x; + } + + public int X { get; set; } + } + + + class TestClassProtectedCtrPrivateProperty + { + protected TestClassProtectedCtrPrivateProperty() + { + + } + + public TestClassProtectedCtrPrivateProperty(int x, string name) + { + X = x; + Name = name; + } + + public int X { get; private set; } + + public string Name { get; private set; } + } + + + record TestREcord() + { + + + public int X { set; get; } + } + + record TestRecordPositional(int X); + + record struct TestRecordStruct + { + public int X { set; get; } + } + + + #endregion TestClases +} diff --git a/src/Mapster/Adapters/ClassAdapter.cs b/src/Mapster/Adapters/ClassAdapter.cs index cc803d8d..49027a49 100644 --- a/src/Mapster/Adapters/ClassAdapter.cs +++ b/src/Mapster/Adapters/ClassAdapter.cs @@ -54,7 +54,28 @@ protected override Expression CreateInstantiationExpression(Expression source, E { //new TDestination(src.Prop1, src.Prop2) - if (arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) + /// + bool IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(CompileArgument arg) + { + if (arg.Settings.EnableNonPublicMembers == null) + return false; + if (arg.Settings.EnableNonPublicMembers == false) + return false; + else + { + if (arg.DestinationType.GetConstructors().Any(x => x.GetParameters() != null)) + { + return true; + } + } + + + return false; + } + + + + if ((arg.GetConstructUsing() != null || arg.Settings.MapToConstructor == null) && !IsEnableNonPublicMembersAndNotPublicCtorWithoutParams(arg)) return base.CreateInstantiationExpression(source, destination, arg); ClassMapping? classConverter; diff --git a/src/Mapster/Adapters/RecordTypeAdapter.cs b/src/Mapster/Adapters/RecordTypeAdapter.cs index e5812e16..a0034829 100644 --- a/src/Mapster/Adapters/RecordTypeAdapter.cs +++ b/src/Mapster/Adapters/RecordTypeAdapter.cs @@ -4,7 +4,7 @@ namespace Mapster.Adapters { - internal class RecordTypeAdapter : BaseClassAdapter + internal class RecordTypeAdapter : ClassAdapter { protected override int Score => -149; protected override bool UseTargetValue => false; @@ -34,12 +34,12 @@ protected override Expression CreateInstantiationExpression(Expression source, E protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg) { - return Expression.Empty(); + return base.CreateBlockExpression(source, destination, arg); } protected override Expression CreateInlineExpression(Expression source, CompileArgument arg) { - return CreateInstantiationExpression(source, arg); + return base.CreateInstantiationExpression(source, arg); } } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 5959daa5..4d591666 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -170,27 +170,58 @@ public static bool IsRecordType(this Type type) var props = type.GetFieldsAndProperties().ToList(); + /// + /// + /// + #region SupportingСurrentBehavior for Config Clone and Fork + + if (type == typeof(MulticastDelegate)) + return true; + + if (type == typeof(TypeAdapterSetter)) + return true; + + // if (type == typeof(TypeAdapterRule)) + // return true; + + if (type == typeof(TypeAdapterSettings)) + return true; + + if (type.IsValueType && type?.GetConstructors().Length != 0) + { + var test = type.GetConstructors()[0].GetParameters(); + var param = type.GetConstructors()[0].GetParameters().ToArray(); + + if (param[0]?.ParameterType == typeof(TypeTuple) && param[1]?.ParameterType == typeof(TypeAdapterRule)) + return true; + } + + if (type == typeof(TypeTuple)) + return true; + + #endregion SupportingСurrentBehavior for Config Clone and Fork + + //interface with readonly props - if (type.GetTypeInfo().IsInterface && + if (type.GetTypeInfo().IsInterface && props.Any(p => p.SetterModifier != AccessModifier.Public)) return true; - //1 constructor - var ctors = type.GetConstructors().ToList(); - if (ctors.Count != 1) - return false; + //constructor + var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); - //ctor must not empty - var ctorParams = ctors[0].GetParameters(); - if (ctorParams.Length == 0) - return false; + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Where(x => x.IsFamily == true) + .Any(x => x.GetParameters() + .Any(y => y.ParameterType == type)); - //all parameters should match getter - return props.All(prop => - { - var name = prop.Name.ToPascalCase(); - return ctorParams.Any(p => p.ParameterType == prop.Type && p.Name?.ToPascalCase() == name); - }); + + if (ctors.Count >= 2 && isRecordTypeCtor) + return true; + + + + return false; } public static bool IsConvertible(this Type type) From 88fb01843f1349e3c5a1d91ed978b87a7518d1c5 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 25 Sep 2023 06:24:18 +0500 Subject: [PATCH 02/11] add FakeRecord --- src/Mapster.Tests/WhenRecordRegration.cs | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs index c6d99b51..b4078a30 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -211,7 +211,7 @@ public void UpdateNullable() } - } + @@ -220,7 +220,15 @@ public void UpdateNullable() #region NowNotWorking + [TestMethod] + public void DetectFakeRecord() + { + var _sourse = new TestClassPublicCtr(200); + var _destination = new FakeRecord { X = 300 }; + + var _result = _sourse.Adapt(_destination); + } /// @@ -268,16 +276,16 @@ public void UpdateNullable() // return dest; // } + #endregion NowNotWorking + } - //} - - #endregion NowNotWorking + @@ -285,6 +293,23 @@ public void UpdateNullable() #region TestClases + public class FakeRecord + { + protected FakeRecord(FakeRecord fake) + { + + } + + public FakeRecord() + { + + } + + public int X { get; set; } + } + + + class UserAccount { From 14a674e1cc12d12c8fb212c8cadd1d044ed94023 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Mon, 25 Sep 2023 17:25:52 +0500 Subject: [PATCH 03/11] refactoring test --- src/Mapster.Tests/WhenRecordRegration.cs | 102 +++++++++++++++++------ 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenRecordRegration.cs index b4078a30..8d3fc51b 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenRecordRegration.cs @@ -15,7 +15,7 @@ public class WhenRecordRegress [TestMethod] - public void AdaptRecord() + public void AdaptRecordToRecord() { var _sourse = new TestREcord() { X = 700 }; @@ -23,37 +23,52 @@ public void AdaptRecord() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + _result.X.ShouldBe(700); + + object.ReferenceEquals(_result, _destination).ShouldBeFalse(); + } + + [TestMethod] + public void AdaptPositionalRecordToPositionalRecord() + { var _sourcePositional = new TestRecordPositional(600); var _destinationPositional = new TestRecordPositional(900); var _positionalResult = _sourcePositional.Adapt(_destinationPositional); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + _positionalResult.X.ShouldBe(600); + + object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); + } + [TestMethod] + public void AdaptRecordStructToRecordStruct() + { var _sourceStruct = new TestRecordStruct() { X = 1000 }; var _destinationStruct = new TestRecordStruct() { X = 800 }; var _structResult = _sourceStruct.Adapt(_destinationStruct); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); - _result.X.ShouldBe(700); + _structResult.X.ShouldBe(1000); + + object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); - _positionalResult.X.ShouldBe(600); - _structResult.X.ShouldBe(1000); } + + + [TestMethod] public void AdaptRecordToClass() { @@ -64,11 +79,14 @@ public void AdaptRecordToClass() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } [TestMethod] @@ -81,11 +99,14 @@ public void AdaptClassToRecord() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeFalse(); + _destination.ShouldBeOfType(); _result.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } @@ -93,22 +114,54 @@ public void AdaptClassToRecord() [TestMethod] - public void AdaptClassIsNotNewInstanse() + public void AdaptClassToClassPublicCtr_IsNotInstanse() { var _sourse = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtr(400); + var _destination = new TestClassPublicCtr(400); var _result = _sourse.Adapt(_destination); + + + _destination.ShouldBeOfType(); + + _destination.X.ShouldBe(200); + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + } + + + [TestMethod] + public void AdaptClassToClassProtectdCtr_IsNotInstanse() + { + var _sourse = new TestClassPublicCtr(200); + + var _destination = new TestClassProtectedCtr(400); + + var _result = _sourse.Adapt(_destination); + + + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + + + + + + + + /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -136,7 +189,7 @@ public void AdaptClassIncludeStruct() /// https://github.com/MapsterMapper/Mapster/issues/482 /// [TestMethod] - public void AdaptClassIsPrivateProperty() + public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() { var _sourse = new TestClassPublicCtr(200); @@ -145,13 +198,16 @@ public void AdaptClassIsPrivateProperty() var _result = _sourse.Adapt(_destination); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + _destination.ShouldBeOfType(); _destination.X.ShouldBe(200); _destination.Name.ShouldBe("Me"); + + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } @@ -214,21 +270,17 @@ public void UpdateNullable() + #region NowNotWorking + // [TestMethod] + //public void DetectFakeRecord() + //{ + // var _sourse = new TestClassPublicCtr(200); + // var _destination = new FakeRecord { X = 300 }; - - #region NowNotWorking - - [TestMethod] - public void DetectFakeRecord() - { - var _sourse = new TestClassPublicCtr(200); - - var _destination = new FakeRecord { X = 300 }; - - var _result = _sourse.Adapt(_destination); - } + // var _result = _sourse.Adapt(_destination); + //} /// From 9b91b85fa5bf2fe866f42a6d72e6e9a1b6c00877 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 14:29:51 +0500 Subject: [PATCH 04/11] Fix From Rewiew --- ...tion.cs => WhenMappingRecordRegression.cs} | 127 +++++++++--------- 1 file changed, 65 insertions(+), 62 deletions(-) rename src/Mapster.Tests/{WhenRecordRegration.cs => WhenMappingRecordRegression.cs} (75%) diff --git a/src/Mapster.Tests/WhenRecordRegration.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs similarity index 75% rename from src/Mapster.Tests/WhenRecordRegration.cs rename to src/Mapster.Tests/WhenMappingRecordRegression.cs index 8d3fc51b..efc48053 100644 --- a/src/Mapster.Tests/WhenRecordRegration.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -10,18 +10,18 @@ namespace Mapster.Tests /// [TestClass] - public class WhenRecordRegress + public class WhenMappingRecordRegression { [TestMethod] public void AdaptRecordToRecord() { - var _sourse = new TestREcord() { X = 700 }; + var _source = new TestRecord() { X = 700 }; - var _destination = new TestREcord() { X = 500 }; + var _destination = new TestRecord() { X = 500 }; - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -72,11 +72,11 @@ public void AdaptRecordStructToRecordStruct() [TestMethod] public void AdaptRecordToClass() { - var _sourse = new TestRecordPositional(200); + var _sourсe = new TestRecordPositional(200); var _destination = new TestClassProtectedCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _sourсe.Adapt(_destination); @@ -92,11 +92,11 @@ public void AdaptRecordToClass() [TestMethod] public void AdaptClassToRecord() { - var _sourse = new TestClassProtectedCtr(200); + var _sourсe = new TestClassProtectedCtr(200); var _destination = new TestRecordPositional(400); - var _result = _sourse.Adapt(_destination); + var _result = _sourсe.Adapt(_destination); @@ -116,11 +116,11 @@ public void AdaptClassToRecord() [TestMethod] public void AdaptClassToClassPublicCtr_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassPublicCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -137,11 +137,11 @@ public void AdaptClassToClassPublicCtr_IsNotInstanse() [TestMethod] public void AdaptClassToClassProtectdCtr_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassProtectedCtr(400); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -191,11 +191,11 @@ public void AdaptClassIncludeStruct() [TestMethod] public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() { - var _sourse = new TestClassPublicCtr(200); + var _source = new TestClassPublicCtr(200); var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); - var _result = _sourse.Adapt(_destination); + var _result = _source.Adapt(_destination); @@ -217,7 +217,7 @@ public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() [TestMethod] public void UpdateNullable() { - var _sourse = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); + var _source = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); var _update = new UpdateUser { @@ -231,7 +231,7 @@ public void UpdateNullable() .IgnoreNullValues(true); - _update.Adapt(_sourse, configDate); + _update.Adapt(_source, configDate); @@ -252,10 +252,10 @@ public void UpdateNullable() var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); - _sourse.Id.ShouldBe("123"); - _sourse.Created.ShouldBe(new DateTime(2023, 9, 24)); - _sourse.Modified.ShouldBe(new DateTime(2025, 9, 24)); - _sourse.Email.ShouldBe("123@gmail.com"); + _source.Id.ShouldBe("123"); + _source.Created.ShouldBe(new DateTime(2023, 9, 24)); + _source.Modified.ShouldBe(new DateTime(2025, 9, 24)); + _source.Email.ShouldBe("123@gmail.com"); _sourseEmailUpdate.Id.ShouldBe("123"); @@ -272,61 +272,64 @@ public void UpdateNullable() #region NowNotWorking - // [TestMethod] - //public void DetectFakeRecord() - //{ - // var _sourse = new TestClassPublicCtr(200); + [TestMethod] + public void DetectFakeRecord() + { + var _source = new TestClassPublicCtr(200); - // var _destination = new FakeRecord { X = 300 }; + var _destination = new FakeRecord { X = 300 }; - // var _result = _sourse.Adapt(_destination); - //} + var _result = _source.Adapt(_destination); - /// - /// https://github.com/MapsterMapper/Mapster/issues/430 - /// - // [TestMethod] - // public void CollectionUpdate() - // { - // List sources = new() - // { - // new(541), - // new(234) + } + - // }; + /// + /// https://github.com/MapsterMapper/Mapster/issues/430 + /// + [TestMethod] + public void CollectionUpdate() + { + List sources = new() + { + new(541), + new(234) + + }; - // var destination = new List(); + var destination = new List(); - // var _result = sources.Adapt(destination); + var _result = sources.Adapt(destination); - // destination.Count.ShouldBe(_result.Count); + destination.Count.ShouldBe(_result.Count); - // } + } - // /// - // /// https://github.com/MapsterMapper/Mapster/issues/524 - // /// - // [TestMethod] - // public void TSousreIsObjectUpdate() - // { - // var source = new TestClassPublicCtr { X = 123 }; + /// + /// https://github.com/MapsterMapper/Mapster/issues/524 + /// + + [TestMethod] + public void TSousreIsObjectUpdate() + { + var source = new TestClassPublicCtr { X = 123 }; - // var _result = Somemap(source); + var _result = Somemap(source); - // _result.X.ShouldBe(123); + _result.X.ShouldBe(123); - // } + } - // TestClassPublicCtr Somemap(object source) - // { - // var dest = new TestClassPublicCtr { X = 321 }; - // var dest1 = source.Adapt(dest); + TestClassPublicCtr Somemap(object source) + { + var dest = new TestClassPublicCtr { X = 321 }; + var dest1 = source.Adapt(dest); - // return dest; - // } + return dest; + } #endregion NowNotWorking @@ -337,13 +340,13 @@ public void UpdateNullable() - - #region TestClases + + #region TestClasses public class FakeRecord { @@ -473,7 +476,7 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) } - record TestREcord() + record TestRecord() { @@ -488,5 +491,5 @@ record struct TestRecordStruct } - #endregion TestClases + #endregion TestClasses } From 68380023ef4c10c35ee00a63c01e20e901c905c8 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 14:38:20 +0500 Subject: [PATCH 05/11] fix deleting forgotten comments --- .../WhenMappingRecordRegression.cs | 64 +++++-------------- src/Mapster/Utils/ReflectionUtils.cs | 4 +- 2 files changed, 16 insertions(+), 52 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index efc48053..375f7686 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -12,8 +12,6 @@ namespace Mapster.Tests public class WhenMappingRecordRegression { - - [TestMethod] public void AdaptRecordToRecord() { @@ -66,9 +64,6 @@ public void AdaptRecordStructToRecordStruct() } - - - [TestMethod] public void AdaptRecordToClass() { @@ -109,10 +104,6 @@ public void AdaptClassToRecord() object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } - - - - [TestMethod] public void AdaptClassToClassPublicCtr_IsNotInstanse() { @@ -155,13 +146,6 @@ public void AdaptClassToClassProtectdCtr_IsNotInstanse() } - - - - - - - /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -268,21 +252,19 @@ public void UpdateNullable() } - - #region NowNotWorking [TestMethod] - public void DetectFakeRecord() - { - var _source = new TestClassPublicCtr(200); + public void DetectFakeRecord() + { + var _source = new TestClassPublicCtr(200); - var _destination = new FakeRecord { X = 300 }; + var _destination = new FakeRecord { X = 300 }; - var _result = _source.Adapt(_destination); + var _result = _source.Adapt(_destination); - } + } /// @@ -290,8 +272,8 @@ public void DetectFakeRecord() /// [TestMethod] public void CollectionUpdate() - { - List sources = new() + { + List sources = new() { new(541), new(234) @@ -302,16 +284,16 @@ public void CollectionUpdate() var destination = new List(); - var _result = sources.Adapt(destination); + var _result = sources.Adapt(destination); - destination.Count.ShouldBe(_result.Count); + destination.Count.ShouldBe(_result.Count); } /// /// https://github.com/MapsterMapper/Mapster/issues/524 /// - + [TestMethod] public void TSousreIsObjectUpdate() { @@ -323,7 +305,7 @@ public void TSousreIsObjectUpdate() } - TestClassPublicCtr Somemap(object source) + TestClassPublicCtr Somemap(object source) { var dest = new TestClassPublicCtr { X = 321 }; var dest1 = source.Adapt(dest); @@ -331,19 +313,10 @@ TestClassPublicCtr Somemap(object source) return dest; } - #endregion NowNotWorking - - - } - - - - - - - + #endregion NowNotWorking + } #region TestClasses @@ -364,8 +337,6 @@ public FakeRecord() } - - class UserAccount { public UserAccount(string id, string email, DateTime created) @@ -395,10 +366,6 @@ class UpdateUser } - - - - class DestinationWithStruct { public TestStruct TestStruct { get; set; } @@ -478,7 +445,7 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) record TestRecord() { - + public int X { set; get; } } @@ -490,6 +457,5 @@ record struct TestRecordStruct public int X { set; get; } } - #endregion TestClasses } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 4d591666..28e30c12 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -170,9 +170,7 @@ public static bool IsRecordType(this Type type) var props = type.GetFieldsAndProperties().ToList(); - /// - /// - /// + #region SupportingСurrentBehavior for Config Clone and Fork if (type == typeof(MulticastDelegate)) From cf5515716d72547f293baa743843f655f44bc90c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 15:33:07 +0500 Subject: [PATCH 06/11] Fix Naming_ add [Ignore] from not working test add new test from Adapt Object to Type --- .../WhenMappingRecordRegression.cs | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 375f7686..15588bd5 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -105,7 +105,7 @@ public void AdaptClassToRecord() } [TestMethod] - public void AdaptClassToClassPublicCtr_IsNotInstanse() + public void AdaptClassToClassPublicCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -126,7 +126,7 @@ public void AdaptClassToClassPublicCtr_IsNotInstanse() [TestMethod] - public void AdaptClassToClassProtectdCtr_IsNotInstanse() + public void AdaptClassToClassProtectdCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -173,7 +173,7 @@ public void AdaptClassIncludeStruct() /// https://github.com/MapsterMapper/Mapster/issues/482 /// [TestMethod] - public void AdaptClassToClassFromPrivateProperty_IsNotInstanse() + public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() { var _source = new TestClassPublicCtr(200); @@ -252,9 +252,36 @@ 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; + } + + + + + #region NowNotWorking [TestMethod] + [Ignore] public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); @@ -264,12 +291,18 @@ public void DetectFakeRecord() var _result = _source.Adapt(_destination); + _destination.X.ShouldBe(200); + + object.ReferenceEquals(_destination, _result).ShouldBeTrue(); + + } /// - /// https://github.com/MapsterMapper/Mapster/issues/430 + /// https://github.com/MapsterMapper/Mapster/issues/430 /// + [Ignore] [TestMethod] public void CollectionUpdate() { @@ -291,9 +324,11 @@ public void CollectionUpdate() } /// - /// https://github.com/MapsterMapper/Mapster/issues/524 + /// 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() { @@ -308,7 +343,7 @@ public void TSousreIsObjectUpdate() TestClassPublicCtr Somemap(object source) { var dest = new TestClassPublicCtr { X = 321 }; - var dest1 = source.Adapt(dest); + var dest1 = source.Adapt(dest); // typeof(TSource) always return Type as Object. Need use dynamic or Cast to Runtime Type before Adapt return dest; } From 1ab9810613230ed303d32e9ea84f9d50adf99fe2 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 27 Sep 2023 22:39:51 +0500 Subject: [PATCH 07/11] remove whitespace and misprint _sourceEmailUpdate --- .../WhenMappingRecordRegression.cs | 161 ++---------------- 1 file changed, 15 insertions(+), 146 deletions(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 15588bd5..917e9683 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -9,22 +9,16 @@ namespace Mapster.Tests /// Tests for https://github.com/MapsterMapper/Mapster/issues/537 /// [TestClass] - public class WhenMappingRecordRegression { [TestMethod] public void AdaptRecordToRecord() { var _source = new TestRecord() { X = 700 }; - var _destination = new TestRecord() { X = 500 }; - var _result = _source.Adapt(_destination); - - _result.X.ShouldBe(700); - object.ReferenceEquals(_result, _destination).ShouldBeFalse(); } @@ -32,55 +26,33 @@ public void AdaptRecordToRecord() public void AdaptPositionalRecordToPositionalRecord() { var _sourcePositional = new TestRecordPositional(600); - var _destinationPositional = new TestRecordPositional(900); - var _positionalResult = _sourcePositional.Adapt(_destinationPositional); - - _positionalResult.X.ShouldBe(600); - object.ReferenceEquals(_destinationPositional, _positionalResult).ShouldBeFalse(); - } [TestMethod] public void AdaptRecordStructToRecordStruct() { var _sourceStruct = new TestRecordStruct() { X = 1000 }; - var _destinationStruct = new TestRecordStruct() { X = 800 }; - var _structResult = _sourceStruct.Adapt(_destinationStruct); - - _structResult.X.ShouldBe(1000); - object.ReferenceEquals(_destinationStruct, _structResult).ShouldBeFalse(); - - } - [TestMethod] public void AdaptRecordToClass() { var _sourсe = new TestRecordPositional(200); - var _destination = new TestClassProtectedCtr(400); - var _result = _sourсe.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } @@ -88,19 +60,11 @@ public void AdaptRecordToClass() public void AdaptClassToRecord() { var _sourсe = new TestClassProtectedCtr(200); - var _destination = new TestRecordPositional(400); - var _result = _sourсe.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _result.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } @@ -108,44 +72,26 @@ public void AdaptClassToRecord() public void AdaptClassToClassPublicCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassPublicCtr(400); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - [TestMethod] public void AdaptClassToClassProtectdCtrIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtr(400); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - /// /// https://github.com/MapsterMapper/Mapster/issues/615 /// @@ -165,7 +111,6 @@ public void AdaptClassIncludeStruct() }; var destination = source.Adapt(); - destination.TestStruct.Property.ShouldBe("A"); } @@ -176,25 +121,15 @@ public void AdaptClassIncludeStruct() public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() { var _source = new TestClassPublicCtr(200); - var _destination = new TestClassProtectedCtrPrivateProperty(400, "Me"); - var _result = _source.Adapt(_destination); - - - _destination.ShouldBeOfType(); - _destination.X.ShouldBe(200); - _destination.Name.ShouldBe("Me"); - - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } - /// /// https://github.com/MapsterMapper/Mapster/issues/427 /// @@ -202,56 +137,41 @@ public void AdaptClassToClassFromPrivatePropertyIsNotInstanse() public void UpdateNullable() { var _source = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); - var _update = new UpdateUser { Id = "123", - }; - var configDate = new TypeAdapterConfig(); + configDate.ForType() .Map(dest => dest.Modified, src => new DateTime(2025, 9, 24)) .IgnoreNullValues(true); - _update.Adapt(_source, configDate); - - - var _sourseEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); - + var _sourceEmailUpdate = new UserAccount("123", "123@gmail.com", new DateTime(2023, 9, 24)); var _updateEmail = new UpdateUser { Email = "245@gmail.com", }; - var config = new TypeAdapterConfig(); config.ForType() - .IgnoreNullValues(true); - - var _resultEmail = _updateEmail.Adapt(_sourseEmailUpdate, config); - + var _resultEmail = _updateEmail.Adapt(_sourceEmailUpdate, config); _source.Id.ShouldBe("123"); _source.Created.ShouldBe(new DateTime(2023, 9, 24)); _source.Modified.ShouldBe(new DateTime(2025, 9, 24)); _source.Email.ShouldBe("123@gmail.com"); - - - _sourseEmailUpdate.Id.ShouldBe("123"); - _sourseEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); - _sourseEmailUpdate.Modified.ShouldBe(null); - _sourseEmailUpdate.Email.ShouldBe("245@gmail.com"); - - + _sourceEmailUpdate.Id.ShouldBe("123"); + _sourceEmailUpdate.Created.ShouldBe(new DateTime(2023, 9, 24)); + _sourceEmailUpdate.Modified.ShouldBe(null); + _sourceEmailUpdate.Email.ShouldBe("245@gmail.com"); } - /// /// https://github.com/MapsterMapper/Mapster/issues/524 /// @@ -259,25 +179,20 @@ public void UpdateNullable() 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; } - - - #region NowNotWorking [TestMethod] @@ -285,20 +200,12 @@ TestClassPublicCtr SomemapWithDynamic(object source) public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); - var _destination = new FakeRecord { X = 300 }; - var _result = _source.Adapt(_destination); - - _destination.X.ShouldBe(200); - object.ReferenceEquals(_destination, _result).ShouldBeTrue(); - - } - /// /// https://github.com/MapsterMapper/Mapster/issues/430 /// @@ -307,20 +214,14 @@ public void DetectFakeRecord() public void CollectionUpdate() { List sources = new() - { + { new(541), new(234) - }; - - var destination = new List(); - - var _result = sources.Adapt(destination); destination.Count.ShouldBe(_result.Count); - } /// @@ -333,11 +234,9 @@ public void CollectionUpdate() public void TSousreIsObjectUpdate() { var source = new TestClassPublicCtr { X = 123 }; - var _result = Somemap(source); _result.X.ShouldBe(123); - } TestClassPublicCtr Somemap(object source) @@ -350,7 +249,6 @@ TestClassPublicCtr Somemap(object source) #endregion NowNotWorking - } @@ -358,20 +256,12 @@ TestClassPublicCtr Somemap(object source) public class FakeRecord { - protected FakeRecord(FakeRecord fake) - { - - } - - public FakeRecord() - { - - } + protected FakeRecord(FakeRecord fake) { } + public FakeRecord() { } public int X { get; set; } } - class UserAccount { public UserAccount(string id, string email, DateTime created) @@ -379,9 +269,7 @@ public UserAccount(string id, string email, DateTime created) Id = id; Email = email; Created = created; - } - protected UserAccount() { } public string Id { get; set; } @@ -394,13 +282,10 @@ class UpdateUser { public string? Id { get; set; } public string? Email { get; set; } - public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - } - class DestinationWithStruct { public TestStruct TestStruct { get; set; } @@ -419,20 +304,15 @@ class SourceWithStruct struct TestStruct { public string Property { get; } - public TestStruct(string property) : this() { Property = property; } } - class TestClassPublicCtr { - public TestClassPublicCtr() - { - - } + public TestClassPublicCtr() { } public TestClassPublicCtr(int x) { @@ -442,13 +322,9 @@ public TestClassPublicCtr(int x) public int X { get; set; } } - class TestClassProtectedCtr { - protected TestClassProtectedCtr() - { - - } + protected TestClassProtectedCtr() { } public TestClassProtectedCtr(int x) { @@ -458,13 +334,9 @@ public TestClassProtectedCtr(int x) public int X { get; set; } } - class TestClassProtectedCtrPrivateProperty { - protected TestClassProtectedCtrPrivateProperty() - { - - } + protected TestClassProtectedCtrPrivateProperty() { } public TestClassProtectedCtrPrivateProperty(int x, string name) { @@ -477,11 +349,8 @@ public TestClassProtectedCtrPrivateProperty(int x, string name) public string Name { get; private set; } } - record TestRecord() { - - public int X { set; get; } } From 636eca0479bf57fcb2906ffde8928f3a9eeeae9b Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 14:54:19 +0500 Subject: [PATCH 08/11] test check Current Work From ImplicitOperator to Class --- .../WhenMappingRecordRegression.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index 917e9683..cf123a44 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -192,6 +192,31 @@ TestClassPublicCtr SomemapWithDynamic(object source) return dest; } + /// + /// https://github.com/MapsterMapper/Mapster/issues/569 + /// + [TestMethod] + public void ImplicitOperatorCurrentWorkFromClass() + { + var guid = Guid.NewGuid(); + + var pocoWithGuid1 = new PocoWithGuid { Id = guid }; + var pocoWithId2 = new PocoWithId { Id = new Id(guid) }; + + var pocoWithId1 = pocoWithGuid1.Adapt(); + var pocoWithGuid2 = pocoWithId2.Adapt(); + + pocoWithId1.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); + pocoWithGuid2.Id.Equals(guid).ShouldBeTrue(); + + + var _result = pocoWithId1.Adapt(pocoWithGuid2); + + _result.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); // Guid value transmitted + object.ReferenceEquals(_result, pocoWithGuid2).ShouldBeTrue(); // Not created new instanse from class pocoWithGuid2 + _result.ShouldBeOfType(); + + } #region NowNotWorking @@ -254,6 +279,28 @@ TestClassPublicCtr Somemap(object source) #region TestClasses + class PocoWithGuid + { + public Guid Id { get; init; } + } + + class PocoWithId + { + public Id Id { get; init; } + } + + class Id + { + private readonly Guid _guid; + + public Id(Guid id) => _guid = id; + + public static implicit operator Id(Guid value) => new(value); + public static implicit operator Guid(Id value) => value._guid; + + public override string ToString() => _guid.ToString(); + } + public class FakeRecord { protected FakeRecord(FakeRecord fake) { } From 62ab7d6a399bc90a3392f2ddad6db447a434605c Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 15:28:51 +0500 Subject: [PATCH 09/11] add Sealed record Detection and Test --- .../WhenMappingRecordRegression.cs | 33 +++++++++++++++++++ src/Mapster/Utils/ReflectionUtils.cs | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index cf123a44..e872e1b9 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -68,6 +68,28 @@ public void AdaptClassToRecord() object.ReferenceEquals(_destination, _result).ShouldBeFalse(); } + [TestMethod] + public void AdaptToSealtedRecord() + { + var _sourceRecord = new TestRecord() { X = 2000 }; + var _destinationSealtedRecord = new TestSealedRecord() { X = 3000 }; + var _RecordResult = _sourceRecord.Adapt(_destinationSealtedRecord); + + _RecordResult.X.ShouldBe(2000); + object.ReferenceEquals(_destinationSealtedRecord, _RecordResult).ShouldBeFalse(); + } + + [TestMethod] + public void AdaptToSealtedPositionalRecord() + { + var _sourceRecord = new TestRecord() { X = 2000 }; + var _destinationSealtedPositionalRecord = new TestSealedRecordPositional(4000); + var _RecordResult = _sourceRecord.Adapt(_destinationSealtedPositionalRecord); + + _RecordResult.X.ShouldBe(2000); + object.ReferenceEquals(_destinationSealtedPositionalRecord, _RecordResult).ShouldBeFalse(); + } + [TestMethod] public void AdaptClassToClassPublicCtrIsNotInstanse() { @@ -408,5 +430,16 @@ record struct TestRecordStruct public int X { set; get; } } + /// + /// Different Checked Constructor Attribute From Spec + /// https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/proposals/csharp-9.0/records#copy-and-clone-members + /// + sealed record TestSealedRecord() + { + public int X { get; set; } + } + + sealed record TestSealedRecordPositional(int X); + #endregion TestClasses } diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index 28e30c12..a0e1d3ac 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -209,7 +209,7 @@ public static bool IsRecordType(this Type type) var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(x => x.IsFamily == true) + .Where(x => x.IsFamily == true || (type.Attributes.HasFlag(TypeAttributes.Sealed) && x.IsPrivate == true)) // add target from Sealed record .Any(x => x.GetParameters() .Any(y => y.ParameterType == type)); From 531f0ed5939ef3b7682ef57d5e35e34710f95598 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Tue, 10 Oct 2023 16:44:02 +0500 Subject: [PATCH 10/11] add RecordType identity helper add targeting to RecordType Clone Method --- .../Utils/RecordTypeIdentityHelper.cs | 52 +++++++++++++++++++ .../WhenMappingRecordRegression.cs | 1 - src/Mapster/Utils/ReflectionUtils.cs | 12 +---- 3 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs new file mode 100644 index 00000000..b473802b --- /dev/null +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Mapster.Utils +{ + /// + /// CheckTools from Distinctive features of RecordType according to specification: + /// https://github.com/dotnet/docs/blob/main/docs/csharp/language-reference/builtin-types/record.md + /// + public static class RecordTypeIdentityHelper + { + private static bool IsRecordСonstructor(Type type) + { + var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); + + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) + .Where(x => x.IsFamily == true || (type.IsSealed && x.IsPrivate == true)) // add target from Sealed record + .Any(x => x.GetParameters() + .Any(y => y.ParameterType == type)); + + + if (ctors.Count >= 2 && isRecordTypeCtor) + return true; + + + return false; + + } + + private static bool IsIncludedRecordCloneMethod(Type type) + { + if( type.GetMethod("$")?.MethodImplementationFlags.HasFlag(MethodImplAttributes.IL) == true) + return true; + + return false; + } + + public static bool IsRecordType(Type type) + { + if (IsRecordСonstructor(type) && IsIncludedRecordCloneMethod(type)) + return true; + + + return false; + } + + } +} diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e872e1b9..e22f85b1 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -243,7 +243,6 @@ public void ImplicitOperatorCurrentWorkFromClass() #region NowNotWorking [TestMethod] - [Ignore] public void DetectFakeRecord() { var _source = new TestClassPublicCtr(200); diff --git a/src/Mapster/Utils/ReflectionUtils.cs b/src/Mapster/Utils/ReflectionUtils.cs index a0e1d3ac..fe44e790 100644 --- a/src/Mapster/Utils/ReflectionUtils.cs +++ b/src/Mapster/Utils/ReflectionUtils.cs @@ -205,20 +205,10 @@ public static bool IsRecordType(this Type type) props.Any(p => p.SetterModifier != AccessModifier.Public)) return true; - //constructor - var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); - - var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) - .Where(x => x.IsFamily == true || (type.Attributes.HasFlag(TypeAttributes.Sealed) && x.IsPrivate == true)) // add target from Sealed record - .Any(x => x.GetParameters() - .Any(y => y.ParameterType == type)); - - - if (ctors.Count >= 2 && isRecordTypeCtor) + if(RecordTypeIdentityHelper.IsRecordType(type)) return true; - return false; } From f678d12e3ed87b180af17abe8158ab6f7f4f0a25 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 15 Oct 2023 14:47:11 +0500 Subject: [PATCH 11/11] refactoring and del empty lines --- src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs | 13 ++++--------- src/Mapster.Tests/WhenMappingRecordRegression.cs | 7 ++----- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs index b473802b..4e5aa197 100644 --- a/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs +++ b/src/Mapster.Core/Utils/RecordTypeIdentityHelper.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Mapster.Utils { @@ -17,18 +14,18 @@ private static bool IsRecordСonstructor(Type type) { var ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).ToList(); + if (ctors.Count < 2) + return false; + var isRecordTypeCtor = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) .Where(x => x.IsFamily == true || (type.IsSealed && x.IsPrivate == true)) // add target from Sealed record .Any(x => x.GetParameters() .Any(y => y.ParameterType == type)); - - if (ctors.Count >= 2 && isRecordTypeCtor) + if (isRecordTypeCtor) return true; - return false; - } private static bool IsIncludedRecordCloneMethod(Type type) @@ -44,9 +41,7 @@ public static bool IsRecordType(Type type) if (IsRecordСonstructor(type) && IsIncludedRecordCloneMethod(type)) return true; - return false; } - } } diff --git a/src/Mapster.Tests/WhenMappingRecordRegression.cs b/src/Mapster.Tests/WhenMappingRecordRegression.cs index e22f85b1..f5ef6add 100644 --- a/src/Mapster.Tests/WhenMappingRecordRegression.cs +++ b/src/Mapster.Tests/WhenMappingRecordRegression.cs @@ -221,7 +221,6 @@ TestClassPublicCtr SomemapWithDynamic(object source) public void ImplicitOperatorCurrentWorkFromClass() { var guid = Guid.NewGuid(); - var pocoWithGuid1 = new PocoWithGuid { Id = guid }; var pocoWithId2 = new PocoWithId { Id = new Id(guid) }; @@ -231,7 +230,6 @@ public void ImplicitOperatorCurrentWorkFromClass() pocoWithId1.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); pocoWithGuid2.Id.Equals(guid).ShouldBeTrue(); - var _result = pocoWithId1.Adapt(pocoWithGuid2); _result.Id.ToString().Equals(guid.ToString()).ShouldBeTrue(); // Guid value transmitted @@ -240,8 +238,6 @@ public void ImplicitOperatorCurrentWorkFromClass() } - #region NowNotWorking - [TestMethod] public void DetectFakeRecord() { @@ -252,6 +248,8 @@ public void DetectFakeRecord() object.ReferenceEquals(_destination, _result).ShouldBeTrue(); } + #region NowNotWorking + /// /// https://github.com/MapsterMapper/Mapster/issues/430 /// @@ -313,7 +311,6 @@ class PocoWithId class Id { private readonly Guid _guid; - public Id(Guid id) => _guid = id; public static implicit operator Id(Guid value) => new(value);