Skip to content

Commit 0e4c3b8

Browse files
authored
Merge pull request #643 from DocSvartz/NullableEmuninParamFix
Fix to issue #640 - Nullable Emun to Nullable Destination (Class or param) - Need Sample for testing
2 parents 14cc8c4 + bb76522 commit 0e4c3b8

File tree

3 files changed

+153
-17
lines changed

3 files changed

+153
-17
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
using Shouldly;
3+
using System;
4+
5+
namespace Mapster.Tests
6+
{
7+
[TestClass]
8+
public class WhenMappingNullableEnumRegression
9+
{
10+
/// <summary>
11+
/// https://github.com/MapsterMapper/Mapster/issues/640
12+
/// </summary>
13+
[TestMethod]
14+
public void NullEnumToNullClass()
15+
{
16+
TypeAdapterConfig<Enum?, KeyValueData?>
17+
.NewConfig()
18+
.MapWith(s => s == null ? null : new KeyValueData(s.ToString(), Enums.Manager));
19+
20+
MyClass myClass = new() { TypeEmployer = MyEnum.User };
21+
22+
MyClass myClassNull = new() { TypeEmployer = null};
23+
24+
25+
var _result = myClass?.Adapt<MyDestination?>(); // Work
26+
27+
var _resultNull = myClassNull.Adapt<MyDestination>(); // Null Not Error When (object)s if (MyEnum)s - NullReferenceException
28+
29+
_result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString());
30+
31+
_resultNull.TypeEmployer.ShouldBeNull();
32+
}
33+
34+
/// <summary>
35+
/// https://github.com/MapsterMapper/Mapster/issues/640
36+
/// </summary>
37+
[Ignore] // Will work after RecordType fix
38+
[TestMethod]
39+
public void UpdateNullEnumToClass()
40+
{
41+
TypeAdapterConfig<Enum?, KeyValueData?>
42+
.NewConfig()
43+
.MapWith(s => s == null ? null : new KeyValueData(s.ToString(), Enums.Manager));
44+
45+
MyClass myClass = new() { TypeEmployer = MyEnum.User };
46+
47+
var mDest2 = new MyDestination() { TypeEmployer = new KeyValueData("Admin", null) };
48+
49+
var _MyDestination = myClass?.Adapt<MyDestination?>(); // Work
50+
var _result = _MyDestination.Adapt(mDest2);
51+
52+
_result.TypeEmployer.Key.ShouldBe(MyEnum.User.ToString());
53+
}
54+
}
55+
56+
#region TestClasses
57+
58+
class MyDestination
59+
{
60+
public KeyValueData? TypeEmployer { get; set; }
61+
}
62+
63+
class MyClass
64+
{
65+
public MyEnum? TypeEmployer { get; set; }
66+
}
67+
68+
enum MyEnum
69+
{
70+
Anonymous = 0,
71+
User = 2,
72+
}
73+
74+
class FakeResourceManager
75+
{
76+
77+
}
78+
79+
class Enums
80+
{
81+
protected Enums(string data) {}
82+
public static FakeResourceManager Manager { get; set; }
83+
}
84+
85+
record KeyValueData
86+
{
87+
private readonly string? keyHolder;
88+
private string? description;
89+
90+
public KeyValueData(string key, FakeResourceManager manager)
91+
{
92+
this.keyHolder = key?.ToString();
93+
Description = manager?.ToString();
94+
}
95+
96+
public string Key
97+
{
98+
get => keyHolder!;
99+
set { }
100+
}
101+
102+
public string? Description
103+
{
104+
get => description;
105+
set => description ??= value;
106+
}
107+
}
108+
109+
#endregion TestClasses
110+
}

src/Mapster/Adapters/BaseAdapter.cs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de
169169
var blocks = new List<Expression>();
170170
var label = Expression.Label(arg.DestinationType);
171171

172-
//var drvdSource = source as TDerivedSource
172+
//var drvdSource = _source as TDerivedSource
173173
//if (drvdSource != null)
174174
// return adapt<TSource, TDest>(drvdSource);
175175
foreach (var tuple in arg.Settings.Includes)
@@ -244,7 +244,7 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de
244244
else
245245
{
246246
//TDestination result;
247-
//if (source == null)
247+
//if (_source == null)
248248
// return default(TDestination);
249249
if (source.CanBeNull())
250250
{
@@ -263,15 +263,15 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de
263263
assignActions.Add(Expression.Assign(transformedSource, transform));
264264
assignActions.Add(assign);
265265

266-
//before(source, result, destination);
266+
//before(_source, result, destination);
267267
var beforeMappings = arg.Settings.BeforeMappingFactories.Select(it => InvokeMapping(it, source, result, destination, arg, true)).Reverse();
268268
assignActions.AddRange(beforeMappings);
269269

270-
//result.prop = adapt(source.prop);
270+
//result.prop = adapt(_source.prop);
271271
var mapping = CreateBlockExpression(transformedSource, result, arg);
272272
var settingActions = new List<Expression> {mapping};
273273

274-
//after(source, result, destination);
274+
//after(_source, result, destination);
275275
var afterMappings = arg.Settings.AfterMappingFactories.Select(it => InvokeMapping(it, source, result, destination, arg, false)).Reverse();
276276
settingActions.AddRange(afterMappings);
277277

@@ -280,13 +280,13 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression? de
280280

281281
//using (var scope = new MapContextScope()) {
282282
// var references = scope.Context.Reference;
283-
// var key = new ReferenceTuple(source, typeof(TDestination));
283+
// var key = new ReferenceTuple(_source, typeof(TDestination));
284284
// if (references.TryGetValue(key, out var cache))
285285
// return (TDestination)cache;
286286
//
287287
// var result = new TDestination();
288-
// references[source] = (object)result;
289-
// result.prop = adapt(source.prop);
288+
// references[_source] = (object)result;
289+
// result.prop = adapt(_source.prop);
290290
// return result;
291291
//}
292292

@@ -374,7 +374,7 @@ private static Expression InvokeMapping(
374374

375375
protected Expression? CreateInlineExpressionBody(Expression source, CompileArgument arg)
376376
{
377-
//source == null ? default(TDestination) : adapt(source)
377+
//_source == null ? default(TDestination) : adapt(_source)
378378

379379
var exp = CreateInlineExpression(source, arg);
380380
if (exp == null)
@@ -476,17 +476,19 @@ protected Expression CreateAdaptExpression(Expression source, Type destinationTy
476476
}
477477
internal Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping? mapping, Expression? destination = null)
478478
{
479-
if (source.Type == destinationType && arg.MapType == MapType.Projection)
480-
return source;
479+
var _source = source.NullableEnumExtractor(); // Extraction Nullable Enum
481480

482-
//adapt(source);
481+
if (_source.Type == destinationType && arg.MapType == MapType.Projection)
482+
return _source;
483+
484+
//adapt(_source);
483485
var notUsingDestinationValue = mapping is not { UseDestinationValue: true };
484-
var exp = source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue &&
485-
!arg.Context.Config.HasRuleFor(source.Type, destinationType)
486-
? source
487-
: CreateAdaptExpressionCore(source, destinationType, arg, mapping, destination);
486+
var exp = _source.Type == destinationType && arg.Settings.ShallowCopyForSameType == true && notUsingDestinationValue &&
487+
!arg.Context.Config.HasRuleFor(_source.Type, destinationType)
488+
? _source
489+
: CreateAdaptExpressionCore(_source, destinationType, arg, mapping, destination);
488490

489-
//transform(adapt(source));
491+
//transform(adapt(_source));
490492
if (notUsingDestinationValue)
491493
{
492494
var transform = arg.Settings.DestinationTransforms.Find(it => it.Condition(exp.Type));

src/Mapster/Utils/ExpressionEx.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,30 @@ public static Expression NotNullReturn(this Expression exp, Expression value)
301301
value);
302302
}
303303

304+
/// <summary>
305+
/// Unpack Enum Nullable TSource value
306+
/// </summary>
307+
/// <param name="param"></param>
308+
/// <returns></returns>
309+
public static Expression NullableEnumExtractor(this Expression param)
310+
{
311+
var _SourceType = param.Type;
312+
313+
if (_SourceType.IsNullable())
314+
{
315+
var _genericType = param.Type.GetGenericArguments()[0];
316+
317+
if (_genericType.IsEnum)
318+
{
319+
var ExtractionExpression = Expression.Convert(param, typeof(object));
320+
return ExtractionExpression;
321+
}
322+
323+
return param;
324+
}
325+
326+
return param;
327+
}
304328
public static Expression ApplyNullPropagation(this Expression getter)
305329
{
306330
var current = getter;

0 commit comments

Comments
 (0)