From 070126b579c204062024b6cd8fbcaf96bceb2bc5 Mon Sep 17 00:00:00 2001 From: Aderibigbe Adeola Date: Thu, 5 Sep 2024 10:15:20 +0100 Subject: [PATCH 1/4] Set up ignore configuration --- Automapify/Automapify.csproj | 1 + Automapify/Models/MapifyTuple.cs | 14 ++++++- .../Services/MapifyConfigurationBuilder.cs | 35 ++++++++++++++-- Automapify/Services/Mapper.cs | 41 ++++++++++++++++--- 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/Automapify/Automapify.csproj b/Automapify/Automapify.csproj index ed57043..221defa 100644 --- a/Automapify/Automapify.csproj +++ b/Automapify/Automapify.csproj @@ -9,5 +9,6 @@ https://github.com/Adeola-Aderibigbe/AutoMapify_Lib git .net 6.0, mapping + README.md diff --git a/Automapify/Models/MapifyTuple.cs b/Automapify/Models/MapifyTuple.cs index 5e70c2a..e0baf08 100644 --- a/Automapify/Models/MapifyTuple.cs +++ b/Automapify/Models/MapifyTuple.cs @@ -17,10 +17,22 @@ public MapifyTuple(string destinationMember, string sourceName, LambdaExpression SourceExpression = sourceExpression.Compile(); } + public MapifyTuple(List destinationMembers) + { + DestinationMemberNames = destinationMembers; + + IsIgnored = true; + } + + public void Ignore() + { + IsIgnored = true; + } public List DestinationMemberNames { get; set; } = new List(); - public string SourceMemberName { get; set; } + public string SourceMemberName { get; set; } = string.Empty; public Delegate SourceExpression { get; set; } + public bool IsIgnored { get; set; } = false; } } diff --git a/Automapify/Services/MapifyConfigurationBuilder.cs b/Automapify/Services/MapifyConfigurationBuilder.cs index 0a78a74..3c1e1bb 100644 --- a/Automapify/Services/MapifyConfigurationBuilder.cs +++ b/Automapify/Services/MapifyConfigurationBuilder.cs @@ -23,6 +23,25 @@ public MapifyConfigurationBuilder Map + /// Ignores property + /// + /// destination member type + /// Destination expression + /// + public MapifyConfigurationBuilder Ignore + (Expression> destinationPredicate) + { + var members = GetMemberExpressionNames(destinationPredicate); + + mapifyTuples.Add(new MapifyTuple(members)); + + return this; + } + + /// /// Get expression names from member /// @@ -31,7 +50,7 @@ public MapifyConfigurationBuilder MapExpression /// Names of the member private void CreateTuple - (Expression> destinationPredicate, Expression> sourcePredicate) + (Expression> destinationPredicate, Expression> sourcePredicate, bool isIgnored = false) { MemberExpression memberExpression = destinationPredicate.Body as MemberExpression; @@ -41,13 +60,15 @@ private void CreateTuple var sourceName = GetSourceMemberExpressionName(sourcePredicate); + MapifyTuple mapifyTuple = null; + if (currentMemberExpression != null) { if (sourcePredicate.Body is MethodCallExpression sourceExpression) { newSourceExpression = sourcePredicate.Compile().Invoke(Activator.CreateInstance()) as LambdaExpression; } - mapifyTuples.Add(new MapifyTuple(currentMemberExpression?.Member?.Name, sourceName, newSourceExpression ?? sourcePredicate)); + mapifyTuple = new MapifyTuple(currentMemberExpression?.Member?.Name, sourceName, newSourceExpression ?? sourcePredicate); } else { @@ -55,10 +76,16 @@ private void CreateTuple newSourceExpression = sourcePredicate.Compile().Invoke(Activator.CreateInstance()) as LambdaExpression; - mapifyTuples.Add(new MapifyTuple(destinationName, sourceName, newSourceExpression)); + mapifyTuple = new MapifyTuple(destinationName, sourceName, newSourceExpression); } - } + if (isIgnored) + { + mapifyTuple.Ignore(); + } + + mapifyTuples.Add(mapifyTuple); + } /// /// Get expression names from member diff --git a/Automapify/Services/Mapper.cs b/Automapify/Services/Mapper.cs index 94b6d8d..4aafe08 100644 --- a/Automapify/Services/Mapper.cs +++ b/Automapify/Services/Mapper.cs @@ -113,6 +113,15 @@ public static TDestination Map(this TSource sourceObj, Ma } } + /// + /// Handles mapping for collection + /// + /// Type of the source + /// Source object + /// Destination object + /// Destination type + /// Source Type + /// Mapify configuration private static void MapCollection(TSource sourceObj, IList destinationObj ,Type destinationType, Type sourceType, MapifyConfiguration mapifyConfiguration = null) @@ -156,17 +165,26 @@ private static void MapCollection(TSource sourceObj, IList destinationO } } + /// + /// Maps system collection + /// + /// Source object + /// Destination object + /// Destination name + /// Mapify configuration private static void MapCollection(object? sourceObj, IList destination,string destinationName, MapifyConfiguration mapifyConfiguration = null) { - var propertyExpression = GetSourceExpression(mapifyConfiguration?.MapifyTuples, destinationName); if( sourceObj is ICollection sourceCollection) { - foreach(var source in sourceCollection) + var propertyExpression = GetSourceExpression(mapifyConfiguration?.MapifyTuples, destinationName); + foreach (var source in sourceCollection) { var result = propertyExpression?.DynamicInvoke(source); - destination.Add(result); + + if (result != null) + destination.Add(result); } } } @@ -179,6 +197,16 @@ private static void MapProperties(PropertyInfo[] destinati { object propertyValue = null; + if (destinationProperty.GetCustomAttribute() != null) + continue; + + var isIgnored = mapifyConfiguration?.MapifyTuples.Where(s => s.IsIgnored && s.DestinationMemberNames.Contains(destinationProperty.Name)).Select(s => s.IsIgnored).FirstOrDefault(); + if (isIgnored.GetValueOrDefault()) + { + continue; + } + + if (destinationProperty.PropertyType.GetGenericArguments().FirstOrDefault() is Type type && mapifyConfiguration != null) { Type openType = typeof(List<>); @@ -212,7 +240,6 @@ private static void MapProperties(PropertyInfo[] destinati else if (GetSourceExpression(mapifyConfiguration?.MapifyTuples, destinationProperty.Name) is Delegate propertyExpression) { propertyValue = propertyExpression?.DynamicInvoke(sourceObj); - } else { @@ -254,8 +281,10 @@ private static PropertyInfo GetProperyInfo(Dictionary mapifyTuples, string propertyName) { if (mapifyTuples == null) + { return default; - + } + MapifyTuple mapifyTuple = mapifyTuples.Where(s => s.DestinationMemberNames.Contains(propertyName)).FirstOrDefault(); if(mapifyTuple != null) { @@ -267,6 +296,7 @@ private static Delegate GetSourceExpression(IList mapifyTuples, str private static MapifyConfiguration GetSourceConfiguration(IList mapifyTuples,string sourcePropertyName, PropertyInfo[] properties) { + if (mapifyTuples == null) return default; @@ -275,7 +305,6 @@ private static MapifyConfiguration GetSourceConfiguration(IList map { return new MapifyConfiguration(newMapifyTuples); } - return default; } From a6308fc89ff0dfd116c462b1cc3921d0b86c4ca1 Mon Sep 17 00:00:00 2001 From: Aderibigbe Adeola Date: Thu, 5 Sep 2024 10:15:42 +0100 Subject: [PATCH 2/4] Add ignore configuration --- Automapify.Client/MappingService.cs | 2 +- Automapify.Client/Models/Dtos/StudentDto.cs | 2 ++ Automapify.Client/Models/Student.cs | 1 + Automapify.Client/Program.cs | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Automapify.Client/MappingService.cs b/Automapify.Client/MappingService.cs index d21f0ca..127ef95 100644 --- a/Automapify.Client/MappingService.cs +++ b/Automapify.Client/MappingService.cs @@ -18,7 +18,7 @@ public static MapifyConfiguration StudentConfig() .Map(d => d.DOB, s => s.DateOfBirth) .Map(d => d.IsDeleted, s => false) .Map(d => d.Teachers.MapTo(s => s.Name), s => s.Teachers.MapFrom(p => $"{p.FirstName} {p.LastName}")) - .Map(d => d.TeacherNames, s => s.Teachers.MapFrom(p => $"{p.FirstName} {p.LastName}")) + .Ignore(d=>d.TeacherNames) .Map(d => d.Classrooms, s => s.Classrooms) .CreateConfig(); } diff --git a/Automapify.Client/Models/Dtos/StudentDto.cs b/Automapify.Client/Models/Dtos/StudentDto.cs index 2cfaaf6..7f38d7a 100644 --- a/Automapify.Client/Models/Dtos/StudentDto.cs +++ b/Automapify.Client/Models/Dtos/StudentDto.cs @@ -5,9 +5,11 @@ namespace Automapify.Client.Models.Dtos { public class StudentDto { + [Ignore] public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } + public DateTime DOB { get; set; } public bool IsDeleted { get; set; } diff --git a/Automapify.Client/Models/Student.cs b/Automapify.Client/Models/Student.cs index a3f6d9b..9b06790 100644 --- a/Automapify.Client/Models/Student.cs +++ b/Automapify.Client/Models/Student.cs @@ -1,4 +1,5 @@ using Automapify.Client.Models.Dtos; +using Automapify.Services.Attributes; using System; using System.Collections.Generic; using System.Linq; diff --git a/Automapify.Client/Program.cs b/Automapify.Client/Program.cs index 6dff458..5eb07d0 100644 --- a/Automapify.Client/Program.cs +++ b/Automapify.Client/Program.cs @@ -68,12 +68,12 @@ item.DisplayAge(); } -/* + var config = DefaultConfig.Instance .AddJob(Job .MediumRun .WithLaunchCount(1) .WithToolchain(InProcessEmitToolchain.Instance)); -var summary = BenchmarkRunner.Run(typeof(BenchmarkTest), config);*/ +var summary = BenchmarkRunner.Run(typeof(BenchmarkTest), config); From 207b5174a2e7040aa5205c2028ad854428d21c1d Mon Sep 17 00:00:00 2001 From: Aderibigbe Adeola Date: Thu, 5 Sep 2024 10:15:57 +0100 Subject: [PATCH 3/4] Add unit tests --- Automapify.Test/MapperTests.cs | 40 +++++++++++++++++++ Automapify.Test/Models/StudentClassroomDto.cs | 29 ++++++++++++++ Automapify.Test/TestHelper.cs | 13 ++++++ 3 files changed, 82 insertions(+) create mode 100644 Automapify.Test/Models/StudentClassroomDto.cs diff --git a/Automapify.Test/MapperTests.cs b/Automapify.Test/MapperTests.cs index 18a558d..a566458 100644 --- a/Automapify.Test/MapperTests.cs +++ b/Automapify.Test/MapperTests.cs @@ -1,3 +1,4 @@ +using Automapify.Services; using Automapify.Test.Models; using Automappify.Services; @@ -203,5 +204,44 @@ public void ShouldMapFromSourceObjectToExistingObjectUsingConfiguration() Assert.That(TestClassroom.Courses.Select(s => s.LeadLecturer.Name).Single(), Is.EqualTo(classroomDto.LeadLecturers.Single())); }); } + + [Test] + public void ShouldMapFromSourceObjectToExistingObjectWithIgnoreAttributes() + { + var studentClassroomDto = new StudentClassroomDto(); + + studentClassroomDto.Map(TestClassroom); + + Assert.Multiple(() => + { + Assert.That(string.IsNullOrEmpty(studentClassroomDto.Name)); + Assert.That(TestClassroom.NumberOfStudents, Is.EqualTo(studentClassroomDto.NoOfStudents)); + Assert.That(TestClassroom.NumberOfTeachers, Is.EqualTo(studentClassroomDto.NoOfLecturers)); + Assert.IsTrue(string.IsNullOrEmpty(studentClassroomDto.Code)); + }); + } + + [Test] + public void ShouldMapFromSourceObjectToExistingObjectWithIgnoreConfiguration() + { + var studentClassroomDto = new StudentClassroomDto(); + + var testHelper = new TestHelper(); + + studentClassroomDto.Map(TestClassroom,testHelper.SetupIgnoreConfiguration()); + + Assert.Multiple(() => + { + Assert.That(string.IsNullOrEmpty(studentClassroomDto.Name)); + Assert.That(TestClassroom.NumberOfStudents, Is.EqualTo(studentClassroomDto.NoOfStudents)); + Assert.That(TestClassroom.NumberOfTeachers, Is.EqualTo(studentClassroomDto.NoOfLecturers)); + Assert.IsTrue(string.IsNullOrEmpty(studentClassroomDto.Code)); + Assert.That(studentClassroomDto.IsActive, Is.True); + Assert.That(TestClassroom.Courses.Select(s => s.LeadLecturer.Name).Single(), Is.EqualTo(studentClassroomDto.LeadLecturers.Single())); + }); + } + + + } } \ No newline at end of file diff --git a/Automapify.Test/Models/StudentClassroomDto.cs b/Automapify.Test/Models/StudentClassroomDto.cs new file mode 100644 index 0000000..bca0cce --- /dev/null +++ b/Automapify.Test/Models/StudentClassroomDto.cs @@ -0,0 +1,29 @@ +using Automapify.Services.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IgnoreAttribute = Automapify.Services.Attributes.IgnoreAttribute; + +namespace Automapify.Test.Models +{ + public class StudentClassroomDto + { + [Ignore] + public string Name { get; set; } + + [MapProperty("NumberOfStudents")] + public int NoOfStudents { get; set; } + + public bool IsActive { get; set; } + + [MapProperty("NumberOfTeachers")] + public int NoOfLecturers { get; set; } + + [Ignore] + public string Code { get; set; } + + public List LeadLecturers { get; set; } + } +} diff --git a/Automapify.Test/TestHelper.cs b/Automapify.Test/TestHelper.cs index f53a5a7..c290c81 100644 --- a/Automapify.Test/TestHelper.cs +++ b/Automapify.Test/TestHelper.cs @@ -41,6 +41,19 @@ private MapifyConfiguration SetupConfiguration() .CreateConfig(); return MapifyConfiguration; } + + public MapifyConfiguration SetupIgnoreConfiguration() + { + MapifyConfiguration = new MapifyConfigurationBuilder() + .Ignore(d=>d.Name) + .Map(d => d.NoOfLecturers, s => s.NumberOfTeachers) + .Map(d => d.NoOfStudents, s => s.NumberOfStudents) + .Map(d => d.LeadLecturers, s => s.Courses.MapFrom(s => s.LeadLecturer.Name)) + .Map(d => d.IsActive, s => s.NumberOfTeachers > 0 && s.NumberOfStudents > 0) + .Ignore(d=>d.Code) + .CreateConfig(); + return MapifyConfiguration; + } protected MapifyConfiguration MapifyConfiguration { get; set; } protected Classroom TestClassroom { get; set; } From 4fe6c1e7d9bb9cb0dbec716b12046a67dba7d95b Mon Sep 17 00:00:00 2001 From: Aderibigbe Adeola Date: Thu, 5 Sep 2024 10:16:25 +0100 Subject: [PATCH 4/4] Remove benchmark --- Automapify.Client/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Automapify.Client/Program.cs b/Automapify.Client/Program.cs index 5eb07d0..63a5efb 100644 --- a/Automapify.Client/Program.cs +++ b/Automapify.Client/Program.cs @@ -69,11 +69,11 @@ item.DisplayAge(); } -var config = DefaultConfig.Instance +/*var config = DefaultConfig.Instance .AddJob(Job .MediumRun .WithLaunchCount(1) .WithToolchain(InProcessEmitToolchain.Instance)); -var summary = BenchmarkRunner.Run(typeof(BenchmarkTest), config); +var summary = BenchmarkRunner.Run(typeof(BenchmarkTest), config);*/