Skip to content

Commit bc73606

Browse files
committed
- checkpoint code push
1 parent 2d3802f commit bc73606

File tree

11 files changed

+3563
-529
lines changed

11 files changed

+3563
-529
lines changed

src/TurboMapper/IObjectMap.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ namespace TurboMapper
55
internal interface IObjectMap
66
{
77
void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings = null);
8+
void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings, bool enableDefaultMapping);
89
}
910
}

src/TurboMapper/Impl/Mapper.cs

Lines changed: 154 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,25 @@
77

88
namespace TurboMapper.Impl
99
{
10+
internal class MapperConfiguration
11+
{
12+
public List<PropertyMapping> Mappings { get; set; }
13+
public bool EnableDefaultMapping { get; set; }
14+
15+
public MapperConfiguration(List<PropertyMapping> mappings, bool enableDefaultMapping)
16+
{
17+
Mappings = mappings ?? new List<PropertyMapping>();
18+
EnableDefaultMapping = enableDefaultMapping;
19+
}
20+
}
21+
1022
internal class Mapper : IMapper, IObjectMap
1123
{
12-
private readonly Dictionary<Type, Dictionary<Type, List<PropertyMapping>>> _configurations;
24+
private readonly Dictionary<Type, Dictionary<Type, MapperConfiguration>> _configurations;
1325

1426
public Mapper()
1527
{
16-
_configurations = new Dictionary<Type, Dictionary<Type, List<PropertyMapping>>>();
28+
_configurations = new Dictionary<Type, Dictionary<Type, MapperConfiguration>>();
1729
}
1830

1931
public void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings = null)
@@ -22,9 +34,20 @@ public void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings = null)
2234
var targetType = typeof(TTarget);
2335

2436
if (!_configurations.ContainsKey(sourceType))
25-
_configurations[sourceType] = new Dictionary<Type, List<PropertyMapping>>();
37+
_configurations[sourceType] = new Dictionary<Type, MapperConfiguration>();
2638

27-
_configurations[sourceType][targetType] = mappings ?? new List<PropertyMapping>();
39+
_configurations[sourceType][targetType] = new MapperConfiguration(mappings, true);
40+
}
41+
42+
public void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings, bool enableDefaultMapping)
43+
{
44+
var sourceType = typeof(TSource);
45+
var targetType = typeof(TTarget);
46+
47+
if (!_configurations.ContainsKey(sourceType))
48+
_configurations[sourceType] = new Dictionary<Type, MapperConfiguration>();
49+
50+
_configurations[sourceType][targetType] = new MapperConfiguration(mappings, enableDefaultMapping);
2851
}
2952

3053
public TTarget Map<TSource, TTarget>(TSource source)
@@ -41,8 +64,11 @@ public TTarget Map<TSource, TTarget>(TSource source)
4164
if (_configurations.ContainsKey(sourceType) &&
4265
_configurations[sourceType].ContainsKey(targetType))
4366
{
44-
var mappings = _configurations[sourceType][targetType];
45-
ApplyCustomMappings(source, target, mappings);
67+
var config = _configurations[sourceType][targetType];
68+
if (config.EnableDefaultMapping)
69+
ApplyCustomMappings(source, target, config.Mappings);
70+
else
71+
ApplyCustomMappingsWithDefaultDisabled(source, target, config.Mappings);
4672
}
4773
else
4874
// Default name-based mapping
@@ -67,6 +93,19 @@ internal void ApplyCustomMappings<TSource, TTarget>(
6793
ApplyDefaultNameBasedMapping(source, target, mappings);
6894
}
6995

96+
internal void ApplyCustomMappingsWithDefaultDisabled<TSource, TTarget>(
97+
TSource source,
98+
TTarget target,
99+
List<PropertyMapping> mappings)
100+
{
101+
// Apply only custom mappings, no default mappings
102+
foreach (var mapping in mappings)
103+
{
104+
var sourceValue = GetNestedValue(source, mapping.SourcePropertyPath);
105+
SetNestedValue(target, mapping.TargetPropertyPath, sourceValue);
106+
}
107+
}
108+
70109
private void ApplyDefaultNameBasedMapping<TSource, TTarget>(
71110
TSource source,
72111
TTarget target,
@@ -77,22 +116,48 @@ private void ApplyDefaultNameBasedMapping<TSource, TTarget>(
77116

78117
foreach (var sourceProp in sourceProps)
79118
{
80-
// Check if this property is already mapped in custom mappings
81-
var isMapped = customMappings.Exists(m =>
82-
m.SourcePropertyPath.Split('.').Last() == sourceProp.Name);
119+
// For default mapping, check if this source property maps to any target property in custom mappings
120+
// by checking if there's a custom mapping that targets a property with the same name as sourceProp.Name
121+
var targetProp = targetProps.FirstOrDefault(p =>
122+
p.Name == sourceProp.Name &&
123+
p.CanWrite);
83124

84-
if (!isMapped)
125+
if (targetProp != null)
85126
{
86-
var targetProp = targetProps.FirstOrDefault(p =>
87-
p.Name == sourceProp.Name &&
88-
p.CanWrite &&
89-
p.PropertyType.IsAssignableFrom(sourceProp.PropertyType));
127+
// Check if this target property is already targeted by any custom mapping
128+
var isTargeted = customMappings.Exists(m =>
129+
m.TargetPropertyPath.Split('.').Last() == targetProp.Name);
90130

91-
if (targetProp != null)
131+
if (!isTargeted)
92132
{
93133
var sourceValue = sourceProp.GetValue(source);
94-
var convertedValue = ConvertValue(sourceValue, targetProp.PropertyType);
95-
targetProp.SetValue(target, convertedValue);
134+
135+
if (IsComplexType(sourceProp.PropertyType) && IsComplexType(targetProp.PropertyType))
136+
{
137+
// Handle nested object mapping
138+
if (sourceValue != null)
139+
{
140+
var nestedTargetValue = targetProp.GetValue(target);
141+
if (nestedTargetValue == null)
142+
{
143+
nestedTargetValue = Activator.CreateInstance(targetProp.PropertyType);
144+
targetProp.SetValue(target, nestedTargetValue);
145+
}
146+
147+
var nestedSourceValue = sourceValue;
148+
ApplyNameBasedMapping(nestedSourceValue, nestedTargetValue);
149+
}
150+
else
151+
{
152+
targetProp.SetValue(target, null);
153+
}
154+
}
155+
else
156+
{
157+
// Handle simple types or type conversion
158+
var convertedValue = ConvertValue(sourceValue, targetProp.PropertyType);
159+
targetProp.SetValue(target, convertedValue);
160+
}
96161
}
97162
}
98163
}
@@ -113,6 +178,7 @@ internal void ApplyNameBasedMapping<TSource, TTarget>(TSource source, TTarget ta
113178
var sourceValue = sourceProp.GetValue(source);
114179

115180
if (IsComplexType(sourceProp.PropertyType) && IsComplexType(targetProp.PropertyType))
181+
{
116182
// Handle nested object mapping
117183
if (sourceValue != null)
118184
{
@@ -123,14 +189,20 @@ internal void ApplyNameBasedMapping<TSource, TTarget>(TSource source, TTarget ta
123189
targetProp.SetValue(target, nestedTargetValue);
124190
}
125191

126-
var nestedSourceValue = sourceValue;
127-
ApplyNameBasedMapping(nestedSourceValue, nestedTargetValue);
192+
// Recursively map the nested object properties
193+
ApplyNameBasedMapping(sourceValue, nestedTargetValue);
128194
}
129195
else
130196
{
131-
var convertedValue = ConvertValue(sourceValue, targetProp.PropertyType);
132-
targetProp.SetValue(target, convertedValue);
197+
targetProp.SetValue(target, null);
133198
}
199+
}
200+
else
201+
{
202+
// Handle simple types or type conversion
203+
var convertedValue = ConvertValue(sourceValue, targetProp.PropertyType);
204+
targetProp.SetValue(target, convertedValue);
205+
}
134206
}
135207
}
136208
}
@@ -192,7 +264,7 @@ private bool IsComplexType(Type type)
192264
return false;
193265
if (type.IsArray)
194266
return false;
195-
if (type.GetInterface("IEnumerable") != null)
267+
if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type) && type != typeof(string))
196268
return false;
197269
return true;
198270
}
@@ -205,27 +277,78 @@ private object ConvertValue(object value, Type targetType)
205277
return value;
206278

207279
if (targetType.IsEnum)
208-
return Enum.Parse(targetType, value.ToString());
280+
return Enum.Parse(targetType, value.ToString(), ignoreCase: true);
209281

210282
if (targetType == typeof(string))
211283
return value.ToString();
212284

213-
if (targetType.IsValueType)
214-
return Convert.ChangeType(value, targetType);
285+
if (targetType == typeof(Guid))
286+
return Guid.Parse(value.ToString());
215287

216-
// Handle complex types recursively
217-
if (IsComplexType(targetType) && value != null)
218-
return Map(value, targetType);
288+
if (targetType.IsValueType)
289+
{
290+
try
291+
{
292+
if (targetType == typeof(int) && value is double doubleValue)
293+
return (int)doubleValue; // Explicit truncation for double to int
294+
else if (targetType == typeof(int) && value is float floatValue)
295+
return (int)floatValue; // Explicit truncation for float to int
296+
else
297+
return Convert.ChangeType(value, targetType);
298+
}
299+
catch (FormatException)
300+
{
301+
// If conversion fails, return default value for the target type
302+
return Activator.CreateInstance(targetType);
303+
}
304+
catch (InvalidCastException)
305+
{
306+
// If conversion fails, return default value for the target type
307+
return Activator.CreateInstance(targetType);
308+
}
309+
}
219310

311+
// Handle complex types recursively - this is for when we need to convert
312+
// an object of one type to another (e.g., assigning Address object to AddressWithConfig property)
313+
if (IsComplexType(targetType) && value != null && !targetType.IsAssignableFrom(value.GetType()))
314+
{
315+
try
316+
{
317+
// Use the main Map method to convert the object from one type to another
318+
var sourceType = value.GetType();
319+
var genericMapMethod = typeof(Mapper).GetMethod(nameof(Map),
320+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
321+
322+
if (genericMapMethod != null)
323+
{
324+
var specificMapMethod = genericMapMethod.MakeGenericMethod(sourceType, targetType);
325+
return specificMapMethod.Invoke(this, new object[] { value });
326+
}
327+
}
328+
catch
329+
{
330+
// If mapping fails, return the original value
331+
return value;
332+
}
333+
}
334+
220335
return value;
221336
}
222337

223338
private object Map(object source, Type targetType)
224339
{
225340
var sourceType = source.GetType();
226-
var mapMethod = typeof(Mapper).GetMethod(nameof(Map), new[] { sourceType, targetType });
227-
var genericMapMethod = mapMethod.MakeGenericMethod(sourceType, targetType);
228-
return genericMapMethod.Invoke(this, new object[] { source });
341+
// Get the generic Map method (TTarget Map<TSource, TTarget>(TSource source))
342+
var genericMapMethod = typeof(Mapper).GetMethod(nameof(Map),
343+
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
344+
345+
if (genericMapMethod == null)
346+
{
347+
throw new InvalidOperationException($"Could not find Map method for source type {sourceType}");
348+
}
349+
350+
var specificMapMethod = genericMapMethod.MakeGenericMethod(sourceType, targetType);
351+
return specificMapMethod.Invoke(this, new object[] { source });
229352
}
230353
}
231354
}

src/TurboMapper/MappingModule.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ void IMappingModule.CreateMap(IObjectMap mapper)
3535
{
3636
var targetProp = targetProps.FirstOrDefault(p =>
3737
p.Name == sourceProp.Name &&
38-
p.CanWrite &&
39-
p.PropertyType.IsAssignableFrom(sourceProp.PropertyType));
38+
p.CanWrite);
4039

4140
if (targetProp != null)
4241
// Add default mapping for unmapped properties
@@ -51,7 +50,7 @@ void IMappingModule.CreateMap(IObjectMap mapper)
5150
}
5251
}
5352

54-
mapper.CreateMap<TSource, TTarget>(expression.Mappings);
53+
mapper.CreateMap<TSource, TTarget>(expression.Mappings, _enableDefaultMapping);
5554
}
5655

5756
public abstract Action<IMappingExpression<TSource, TTarget>> CreateMappings();

src/TurboMapper/ServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public static class ServiceCollectionExtensions
1111
public static IServiceCollection AddTurboMapper(
1212
this IServiceCollection services)
1313
{
14+
if (services == null)
15+
throw new ArgumentNullException(nameof(services));
16+
1417
// Register ObjectMapper as singleton
1518
services.AddSingleton<IMapper, Mapper>(serviceProvider =>
1619
{

0 commit comments

Comments
 (0)