Skip to content

Commit 81d2887

Browse files
committed
- checkpoint: Move converter registration to mapping module
1 parent 61a7d29 commit 81d2887

File tree

6 files changed

+100
-18
lines changed

6 files changed

+100
-18
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Please see [Release Roadmap](https://github.com/CodeShayk/TurboMapper/blob/maste
8181
- **Performance Improvements**: Significant performance enhancements (2x+) through compiled expression trees and metadata caching
8282
- **Enhanced Collection Mapping**: Simplified API with Map method now supporting both single objects and collections
8383
- **Ignored Properties Option**: Added Ignore method to IMappingExpression to skip properties during mapping
84-
- **Custom Type Converters Registration**: Added RegisterConverter method to IMapper for custom type conversion functions
84+
- **Custom Type Converters Registration**: Added RegisterConverter method to MappingModule for custom type conversion functions that can be registered with type mappings
8585
- **Improved Nullable Type Handling**: Enhanced ConvertValue method to handle nullable types properly
8686
- **Conditional Mapping**: Added When method to IMappingExpression for conditional property mapping
8787
- **Mapping Transformations**: Added MapWith method for transformation functions during mapping

src/TurboMapper/IMapper.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ public interface IMapper
77
{
88
TTarget Map<TSource, TTarget>(TSource source);
99
IEnumerable<TDestination> Map<TSource, TDestination>(IEnumerable<TSource> source);
10-
void RegisterConverter<TSource, TDestination>(Func<TSource, TDestination> converter);
1110
bool ValidateMapping<TSource, TTarget>();
1211
string[] GetMappingErrors<TSource, TTarget>();
1312
}

src/TurboMapper/IObjectMap.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23

34
namespace TurboMapper
@@ -7,5 +8,7 @@ internal interface IObjectMap
78
void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings = null);
89

910
void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings, bool enableDefaultMapping);
11+
12+
void RegisterConverter<TSource, TDestination>(Func<TSource, TDestination> converter);
1013
}
1114
}

src/TurboMapper/Impl/Mapper.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ internal void ApplyCustomMappingsWithDefaultDisabled<TSource, TTarget>(
157157
}
158158
}
159159

160+
public void RegisterConverter<TSource, TDestination>(Func<TSource, TDestination> converter)
161+
{
162+
var key = $"{typeof(TSource).FullName}_{typeof(TDestination).FullName}";
163+
_converters[key] = converter;
164+
}
165+
160166
private void ApplyDefaultNameBasedMapping<TSource, TTarget>(
161167
TSource source,
162168
TTarget target,
@@ -195,12 +201,6 @@ private bool IsIgnoredInCustomMappings(string targetPropertyName, List<PropertyM
195201
// Custom converter system
196202
private readonly Dictionary<string, Delegate> _converters = new Dictionary<string, Delegate>();
197203

198-
public void RegisterConverter<TSource, TDestination>(Func<TSource, TDestination> converter)
199-
{
200-
var key = $"{typeof(TSource).FullName}_{typeof(TDestination).FullName}";
201-
_converters[key] = converter;
202-
}
203-
204204
public bool ValidateMapping<TSource, TTarget>()
205205
{
206206
var errors = GetMappingErrors<TSource, TTarget>();

src/TurboMapper/MappingModule.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34

45
namespace TurboMapper
@@ -7,11 +8,13 @@ public abstract class MappingModule<TSource, TTarget> : IMappingModule
78
{
89
private readonly Action<MappingExpression<TSource, TTarget>> _configAction;
910
private readonly bool _enableDefaultMapping;
11+
private readonly Dictionary<string, Delegate> _converters;
1012

1113
public MappingModule(bool enableDefaultMapping = true)
1214
{
1315
_configAction = CreateMappings();
1416
_enableDefaultMapping = enableDefaultMapping;
17+
_converters = new Dictionary<string, Delegate>();
1518
}
1619

1720
void IMappingModule.CreateMap(IObjectMap mapper)
@@ -51,8 +54,59 @@ void IMappingModule.CreateMap(IObjectMap mapper)
5154
}
5255

5356
mapper.CreateMap<TSource, TTarget>(expression.Mappings, _enableDefaultMapping);
57+
58+
// Register converters with the mapper if any were defined in this module
59+
RegisterConverters(mapper);
5460
}
5561

5662
public abstract Action<IMappingExpression<TSource, TTarget>> CreateMappings();
63+
64+
/// <summary>
65+
/// Registers a custom converter for type mappings within this module
66+
/// </summary>
67+
/// <typeparam name="TSourceConverter">Source type</typeparam>
68+
/// <typeparam name="TDestination">Destination type</typeparam>
69+
/// <param name="converter">Function to perform the conversion</param>
70+
protected void RegisterConverter<TSourceConverter, TDestination>(Func<TSourceConverter, TDestination> converter)
71+
{
72+
var key = $"{typeof(TSourceConverter).FullName}_{typeof(TDestination).FullName}";
73+
_converters[key] = converter;
74+
}
75+
76+
private void RegisterConverters(IObjectMap mapper)
77+
{
78+
foreach (var kvp in _converters)
79+
{
80+
var converter = kvp.Value as Delegate;
81+
if (converter != null)
82+
{
83+
var sourceType = converter.Method.GetParameters()[0].ParameterType;
84+
var destType = converter.Method.ReturnType;
85+
86+
// Find and invoke the RegisterConverter method with proper type parameters
87+
var method = mapper.GetType().GetMethod("RegisterConverter",
88+
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance,
89+
null,
90+
new Type[] { converter.GetType() },
91+
null);
92+
93+
if (method == null)
94+
{
95+
// Try to get the generic method and make it specific
96+
var genericMethod = mapper.GetType().GetMethod("RegisterConverter",
97+
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
98+
if (genericMethod != null && genericMethod.IsGenericMethod)
99+
{
100+
var specificMethod = genericMethod.MakeGenericMethod(sourceType, destType);
101+
specificMethod.Invoke(mapper, new object[] { converter });
102+
}
103+
}
104+
else
105+
{
106+
method.Invoke(mapper, new object[] { converter });
107+
}
108+
}
109+
}
110+
}
57111
}
58112
}

tests/TurboMapper.Tests/Release120_Tests.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,43 @@ public void Task3_4_IgnoredPropertiesOption()
141141
[Test]
142142
public void Task5_1_CustomTypeConvertersRegistration()
143143
{
144+
// Create a mapping from string to int using converter registered in module
145+
var converterModule = new StringToIntConverterModule();
144146
var mapper = new Mapper();
145-
146-
// Register a custom converter
147-
mapper.RegisterConverter<string, int>(s => int.Parse(s));
148-
mapper.RegisterConverter<int, string>(i => i.ToString());
149-
150-
// Verify the converter is registered by trying a simple conversion
151-
// Note: This test may need adjustment based on how the converter system is fully implemented
152-
var convertersExist = true; // Placeholder - actual test would check internal state
153-
154-
Assert.IsTrue(convertersExist);
147+
148+
// Register the module which will register the string to int converter
149+
((IMappingModule)converterModule).CreateMap(mapper);
150+
151+
// Test that a string value can be converted to int through the registered converter
152+
var source = new StringValueClass { Number = "42" };
153+
var result = mapper.Map<StringValueClass, IntValueClass>(source);
154+
155+
Assert.AreEqual(42, result.Number);
156+
}
157+
158+
// Test module for converter registration
159+
public class StringToIntConverterModule : MappingModule<StringValueClass, IntValueClass>
160+
{
161+
public StringToIntConverterModule() : base(true) // Enable default mapping for this test
162+
{
163+
// Register converter within the module
164+
RegisterConverter<string, int>(s => int.Parse(s));
165+
}
166+
167+
public override Action<IMappingExpression<StringValueClass, IntValueClass>> CreateMappings()
168+
{
169+
return expression => { };
170+
}
171+
}
172+
173+
public class StringValueClass
174+
{
175+
public string Number { get; set; }
176+
}
177+
178+
public class IntValueClass
179+
{
180+
public int Number { get; set; }
155181
}
156182

157183
[Test]

0 commit comments

Comments
 (0)