Skip to content

Commit

Permalink
Merge pull request #17 from Adexandria/feature/add-ignore-configuration
Browse files Browse the repository at this point in the history
Feature/add ignore configuration
  • Loading branch information
Adexandria committed Sep 5, 2024
2 parents 76e605c + 4fe6c1e commit fd12a54
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Automapify.Client/MappingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
2 changes: 2 additions & 0 deletions Automapify.Client/Models/Dtos/StudentDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
1 change: 1 addition & 0 deletions Automapify.Client/Models/Student.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Automapify.Client.Models.Dtos;
using Automapify.Services.Attributes;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down
4 changes: 2 additions & 2 deletions Automapify.Client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@

item.DisplayAge();
}
/*
var config = DefaultConfig.Instance

/*var config = DefaultConfig.Instance
.AddJob(Job
.MediumRun
.WithLaunchCount(1)
Expand Down
40 changes: 40 additions & 0 deletions Automapify.Test/MapperTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Automapify.Services;
using Automapify.Test.Models;
using Automappify.Services;

Expand Down Expand Up @@ -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()));
});
}



}
}
29 changes: 29 additions & 0 deletions Automapify.Test/Models/StudentClassroomDto.cs
Original file line number Diff line number Diff line change
@@ -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<string> LeadLecturers { get; set; }
}
}
13 changes: 13 additions & 0 deletions Automapify.Test/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ private MapifyConfiguration SetupConfiguration()
.CreateConfig();
return MapifyConfiguration;
}

public MapifyConfiguration SetupIgnoreConfiguration()
{
MapifyConfiguration = new MapifyConfigurationBuilder<Classroom, ClassroomDto>()
.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; }

Expand Down
1 change: 1 addition & 0 deletions Automapify/Automapify.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
<RepositoryUrl>https://github.com/Adeola-Aderibigbe/AutoMapify_Lib</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>.net 6.0, mapping</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
</Project>
14 changes: 13 additions & 1 deletion Automapify/Models/MapifyTuple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,22 @@ public MapifyTuple(string destinationMember, string sourceName, LambdaExpression
SourceExpression = sourceExpression.Compile();
}

public MapifyTuple(List<string> destinationMembers)

Check warning on line 20 in Automapify/Models/MapifyTuple.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'SourceExpression' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
{
DestinationMemberNames = destinationMembers;

IsIgnored = true;
}

public void Ignore()
{
IsIgnored = true;
}

public List<string> DestinationMemberNames { get; set; } = new List<string>();

public string SourceMemberName { get; set; }
public string SourceMemberName { get; set; } = string.Empty;
public Delegate SourceExpression { get; set; }
public bool IsIgnored { get; set; } = false;
}
}
35 changes: 31 additions & 4 deletions Automapify/Services/MapifyConfigurationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ public MapifyConfigurationBuilder<TSource, TDestination> Map<DestinationMember,
return this;
}



/// <summary>
/// Ignores property
/// </summary>
/// <typeparam name="DestinationMember"> destination member type</typeparam>
/// <param name="destinationPredicate">Destination expression</param>
/// <returns></returns>
public MapifyConfigurationBuilder<TSource,TDestination> Ignore<DestinationMember>
(Expression<Func<TDestination, DestinationMember>> destinationPredicate)
{
var members = GetMemberExpressionNames(destinationPredicate);

mapifyTuples.Add(new MapifyTuple(members));

return this;
}


/// <summary>
/// Get expression names from member
/// </summary>
Expand All @@ -31,7 +50,7 @@ public MapifyConfigurationBuilder<TSource, TDestination> Map<DestinationMember,
/// <param name="desExp">Expression</param>
/// <returns>Names of the member</returns>
private void CreateTuple<DestinationMember, SourceMember>
(Expression<Func<TDestination, DestinationMember>> destinationPredicate, Expression<Func<TSource, SourceMember>> sourcePredicate)
(Expression<Func<TDestination, DestinationMember>> destinationPredicate, Expression<Func<TSource, SourceMember>> sourcePredicate, bool isIgnored = false)
{
MemberExpression memberExpression = destinationPredicate.Body as MemberExpression;

Expand All @@ -41,24 +60,32 @@ private void CreateTuple<DestinationMember, SourceMember>

var sourceName = GetSourceMemberExpressionName(sourcePredicate);

MapifyTuple mapifyTuple = null;

if (currentMemberExpression != null)
{
if (sourcePredicate.Body is MethodCallExpression sourceExpression)
{
newSourceExpression = sourcePredicate.Compile().Invoke(Activator.CreateInstance<TSource>()) as LambdaExpression;
}
mapifyTuples.Add(new MapifyTuple(currentMemberExpression?.Member?.Name, sourceName, newSourceExpression ?? sourcePredicate));
mapifyTuple = new MapifyTuple(currentMemberExpression?.Member?.Name, sourceName, newSourceExpression ?? sourcePredicate);
}
else
{
var destinationName = GetMemberExpressionNames(destinationPredicate);

newSourceExpression = sourcePredicate.Compile().Invoke(Activator.CreateInstance<TSource>()) as LambdaExpression;

mapifyTuples.Add(new MapifyTuple(destinationName, sourceName, newSourceExpression));
mapifyTuple = new MapifyTuple(destinationName, sourceName, newSourceExpression);
}
}

if (isIgnored)
{
mapifyTuple.Ignore();
}

mapifyTuples.Add(mapifyTuple);
}

/// <summary>
/// Get expression names from member
Expand Down
41 changes: 35 additions & 6 deletions Automapify/Services/Mapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ public static TDestination Map<TSource, TDestination>(this TSource sourceObj, Ma
}
}

/// <summary>
/// Handles mapping for collection
/// </summary>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <param name="sourceObj">Source object</param>
/// <param name="destinationObj">Destination object</param>
/// <param name="destinationType">Destination type</param>
/// <param name="sourceType">Source Type</param>
/// <param name="mapifyConfiguration">Mapify configuration</param>
private static void MapCollection<TSource>(TSource sourceObj, IList destinationObj ,Type destinationType,
Type sourceType,
MapifyConfiguration mapifyConfiguration = null)

Check warning on line 127 in Automapify/Services/Mapper.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
Expand Down Expand Up @@ -156,17 +165,26 @@ private static void MapCollection<TSource>(TSource sourceObj, IList destinationO
}
}

/// <summary>
/// Maps system collection
/// </summary>
/// <param name="sourceObj">Source object</param>
/// <param name="destination">Destination object</param>
/// <param name="destinationName">Destination name</param>
/// <param name="mapifyConfiguration">Mapify configuration</param>

private static void MapCollection(object? sourceObj, IList destination,string destinationName,
MapifyConfiguration mapifyConfiguration = null)

Check warning on line 177 in Automapify/Services/Mapper.cs

View workflow job for this annotation

GitHub Actions / build

Cannot convert null literal to non-nullable reference type.
{
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);
}
}
}
Expand All @@ -179,6 +197,16 @@ private static void MapProperties<TSource,TDestination>(PropertyInfo[] destinati
{
object propertyValue = null;

if (destinationProperty.GetCustomAttribute<IgnoreAttribute>() != 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<>);
Expand Down Expand Up @@ -212,7 +240,6 @@ private static void MapProperties<TSource,TDestination>(PropertyInfo[] destinati
else if (GetSourceExpression(mapifyConfiguration?.MapifyTuples, destinationProperty.Name) is Delegate propertyExpression)
{
propertyValue = propertyExpression?.DynamicInvoke(sourceObj);

}
else
{
Expand Down Expand Up @@ -254,8 +281,10 @@ private static PropertyInfo GetProperyInfo<TSource>(Dictionary<string, MapProper
private static Delegate GetSourceExpression(IList<MapifyTuple> mapifyTuples, string propertyName)
{
if (mapifyTuples == null)
{
return default;

}

MapifyTuple mapifyTuple = mapifyTuples.Where(s => s.DestinationMemberNames.Contains(propertyName)).FirstOrDefault();
if(mapifyTuple != null)
{
Expand All @@ -267,6 +296,7 @@ private static Delegate GetSourceExpression(IList<MapifyTuple> mapifyTuples, str

private static MapifyConfiguration GetSourceConfiguration(IList<MapifyTuple> mapifyTuples,string sourcePropertyName, PropertyInfo[] properties)
{

if (mapifyTuples == null)
return default;

Expand All @@ -275,7 +305,6 @@ private static MapifyConfiguration GetSourceConfiguration(IList<MapifyTuple> map
{
return new MapifyConfiguration(newMapifyTuples);
}

return default;
}

Expand Down

0 comments on commit fd12a54

Please sign in to comment.