Skip to content

Commit 16613ef

Browse files
committed
feat: adds iteration over base type properties
1 parent 43698a6 commit 16613ef

4 files changed

Lines changed: 125 additions & 13 deletions

File tree

ProjectR.Sample/DTOs/Dtos.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,15 @@ public class UpdateProductDto
4545
{
4646
public string Name { get; set; }
4747
}
48+
49+
public abstract class BaseEntityDto
50+
{
51+
public Guid Id { get; set; }
52+
}
53+
54+
[Dto<Category>]
55+
public class CategoryDto : BaseEntityDto
56+
{
57+
public string Name { get; set; }
58+
}
4859
}

ProjectR.Sample/Domain/Domain.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44

55
namespace ProjectR.Sample.Domain
66
{
7+
public abstract class BaseEntity
8+
{
9+
public Guid Id { get; set; }
10+
}
11+
12+
public class Category : BaseEntity
13+
{
14+
public string Name { get; set; }
15+
16+
public Category(Guid id, string name)
17+
{
18+
Id = id;
19+
Name = name;
20+
}
21+
}
22+
723
/// <summary>
824
/// A simple Value Object representing a monetary value.
925
/// It's immutable.

ProjectR/Policies/AnalysisHelper.cs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ public void FindBestConstructor(ITypeSymbol sourceType, ITypeSymbol destinationT
3232
}
3333

3434
var allConstructors = destinationType.GetMembers().OfType<IMethodSymbol>().Where(m => m.MethodKind == MethodKind.Constructor && m.DeclaredAccessibility == Accessibility.Public).ToList();
35+
36+
if (!allConstructors.Any())
37+
{
38+
var destinationBaseType = destinationType.BaseType;
39+
while (destinationBaseType != null)
40+
{
41+
allConstructors = allConstructors.Concat(destinationBaseType.GetMembers().OfType<IMethodSymbol>().Where(m => m.MethodKind == MethodKind.Constructor && m.DeclaredAccessibility == Accessibility.Public)).ToList();
42+
if (allConstructors.Any()) break;
43+
destinationBaseType = destinationBaseType.BaseType;
44+
}
45+
}
46+
3547
if (!allConstructors.Any())
3648
{
3749
plan.Creation.Method = CreationMethod.None;
@@ -50,6 +62,18 @@ public void FindBestConstructor(ITypeSymbol sourceType, ITypeSymbol destinationT
5062
public void FindBestFactory(ITypeSymbol sourceType, ITypeSymbol destinationType, MappingPlan plan)
5163
{
5264
var factories = destinationType.GetMembers().OfType<IMethodSymbol>().Where(m => m.IsStatic && m.DeclaredAccessibility == Accessibility.Public && SymbolEqualityComparer.Default.Equals(m.ReturnType, destinationType));
65+
66+
if (!factories.Any())
67+
{
68+
var destinationBaseType = destinationType.BaseType;
69+
while (destinationType != null)
70+
{
71+
factories = factories.Concat(destinationType.GetMembers().OfType<IMethodSymbol>().Where(m => m.IsStatic && m.DeclaredAccessibility == Accessibility.Public && SymbolEqualityComparer.Default.Equals(m.ReturnType, destinationType)));
72+
if (factories.Any()) break;
73+
destinationType = destinationType.BaseType;
74+
}
75+
}
76+
5377
FindBestCandidate(factories, sourceType, plan, isFactory: true);
5478
}
5579

@@ -68,8 +92,20 @@ private void FindBestCandidate(IEnumerable<IMethodSymbol> candidates, ITypeSymbo
6892
public void MapRemainingProperties(ITypeSymbol sourceType, ITypeSymbol destinationType, MappingPlan plan, IEnumerable<string>? ignoredMembers)
6993
{
7094
var sourceProperties = sourceType.GetMembers().OfType<IPropertySymbol>().ToList();
95+
var sourceBaseType = sourceType.BaseType;
96+
while (sourceBaseType != null)
97+
{
98+
sourceProperties.AddRange(sourceBaseType.GetMembers().OfType<IPropertySymbol>());
99+
sourceBaseType = sourceBaseType.BaseType;
100+
}
71101
var alreadyMapped = new HashSet<string>(plan.Creation.ParametersMap.Values.Select(p => p.SourceProperty.Name).Concat(plan.Instructions.Select(i => i.Destination.Name)));
72102
var destinationProperties = destinationType.GetMembers().OfType<IPropertySymbol>().Where(p => !alreadyMapped.Contains(p.Name));
103+
var destinationBaseType = destinationType.BaseType;
104+
while (destinationBaseType != null)
105+
{
106+
destinationProperties = destinationProperties.Concat(destinationBaseType.GetMembers().OfType<IPropertySymbol>().Where(p => !alreadyMapped.Contains(p.Name)));
107+
destinationBaseType = destinationBaseType.BaseType;
108+
}
73109

74110
var ignoredSet = ignoredMembers != null ? new HashSet<string>(ignoredMembers, System.StringComparer.OrdinalIgnoreCase) : new HashSet<string>();
75111
destinationProperties = destinationProperties.Where(p => !ignoredSet.Contains(p.Name));
@@ -115,6 +151,12 @@ public void MapRemainingProperties(ITypeSymbol sourceType, ITypeSymbol destinati
115151
private bool CanSatisfyParameters(IMethodSymbol method, ITypeSymbol sourceType, MappingPlan plan)
116152
{
117153
var sourceProperties = sourceType.GetMembers().OfType<IPropertySymbol>().ToList();
154+
var sourceBaseType = sourceType.BaseType;
155+
while (sourceBaseType != null)
156+
{
157+
sourceProperties.AddRange(sourceBaseType.GetMembers().OfType<IPropertySymbol>());
158+
sourceBaseType = sourceBaseType.BaseType;
159+
}
118160
return method.Parameters.All(p =>
119161
{
120162
if (p.IsOptional || p.NullableAnnotation == NullableAnnotation.Annotated || p.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) return true;
@@ -130,6 +172,12 @@ private bool CanSatisfyParameters(IMethodSymbol method, ITypeSymbol sourceType,
130172
private Dictionary<IParameterSymbol, CreationInfo.ParameterMappingInfo> MapMethodParameters(IMethodSymbol method, ITypeSymbol sourceType, MappingPlan plan)
131173
{
132174
var sourceProperties = sourceType.GetMembers().OfType<IPropertySymbol>().ToList();
175+
var sourceBaseType = sourceType.BaseType;
176+
while (sourceBaseType != null)
177+
{
178+
sourceProperties.AddRange(sourceBaseType.GetMembers().OfType<IPropertySymbol>());
179+
sourceBaseType = sourceBaseType.BaseType;
180+
}
133181
var map = new Dictionary<IParameterSymbol, CreationInfo.ParameterMappingInfo>(SymbolEqualityComparer.Default);
134182
foreach (var param in method.Parameters)
135183
{
@@ -151,8 +199,33 @@ private bool CanSatisfyParameters(IMethodSymbol method, ITypeSymbol sourceType,
151199
private IMethodSymbol? FindRecordPrimaryConstructor(ITypeSymbol recordType)
152200
{
153201
var recordProperties = recordType.GetMembers().OfType<IPropertySymbol>().Where(p => !p.IsStatic).ToDictionary(p => p.Name, System.StringComparer.OrdinalIgnoreCase);
202+
var baseRecordType = recordType.BaseType;
203+
while (baseRecordType != null && baseRecordType.IsRecord)
204+
{
205+
foreach (var prop in baseRecordType.GetMembers().OfType<IPropertySymbol>().Where(p => !p.IsStatic))
206+
{
207+
if (!recordProperties.ContainsKey(prop.Name))
208+
{
209+
recordProperties[prop.Name] = prop;
210+
}
211+
}
212+
baseRecordType = baseRecordType.BaseType;
213+
}
154214
if (!recordProperties.Any()) return null;
155-
return recordType.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(m => m.MethodKind == MethodKind.Constructor);
215+
216+
var result = recordType.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(m => m.MethodKind == MethodKind.Constructor);
217+
218+
if(result == null)
219+
{
220+
baseRecordType = recordType.BaseType;
221+
while (baseRecordType != null && baseRecordType.IsRecord)
222+
{
223+
result = baseRecordType.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(m => m.MethodKind == MethodKind.Constructor);
224+
if (result != null) break;
225+
baseRecordType = baseRecordType.BaseType;
226+
}
227+
}
228+
return result;
156229
}
157230

158231
private INamedTypeSymbol? FindMapperFor(ITypeSymbol sourceType, ITypeSymbol destinationType)

ProjectR/Policies/PolicyEngine.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,30 @@ public MappingPlan CreateProjectAsPlan(ITypeSymbol sourceType, ITypeSymbol desti
5959
private MappingPlan BuildPlanFromPolicy(ITypeSymbol sourceType, ITypeSymbol destinationType, ParsedPolicy policy, bool isApplyTo = false, bool isProjectAs = false)
6060
{
6161
var plan = new MappingPlan(sourceType, destinationType);
62-
var destinationProperties = destinationType.GetMembers().OfType<IPropertySymbol>().ToDictionary(p => p.Name);
63-
64-
plan.Creation.CustomParameterExpressions = policy.ParameterMappings;
65-
66-
// Apply custom member mappings from policy.MemberMappings
67-
foreach (var memberConfig in policy.MemberMappings)
68-
{
69-
if (destinationProperties.TryGetValue(memberConfig.Key, out var destProp))
70-
{
71-
plan.Instructions.Add(new CustomExpressionMapping(memberConfig.Value, destProp));
72-
}
73-
}
62+
//var destinationProperties = destinationType.GetMembers().OfType<IPropertySymbol>().ToDictionary(p => p.Name);
63+
64+
//var destinationBaseType = destinationType.BaseType;
65+
66+
//while(destinationBaseType != null)
67+
//{
68+
// foreach(var baseProp in destinationBaseType.GetMembers().OfType<IPropertySymbol>())
69+
// {
70+
// if(!destinationProperties.ContainsKey(baseProp.Name))
71+
// destinationProperties[baseProp.Name] = baseProp;
72+
// }
73+
// destinationBaseType = destinationBaseType.BaseType;
74+
//}
75+
76+
//plan.Creation.CustomParameterExpressions = policy.ParameterMappings;
77+
78+
//// Apply custom member mappings from policy.MemberMappings
79+
//foreach (var memberConfig in policy.MemberMappings)
80+
//{
81+
// if (destinationProperties.TryGetValue(memberConfig.Key, out var destProp))
82+
// {
83+
// plan.Instructions.Add(new CustomExpressionMapping(memberConfig.Value, destProp));
84+
// }
85+
//}
7486

7587
var strategies = policy.Strategies.Any() ? policy.Strategies : CreateDefaultBuildPolicy().Strategies;
7688

0 commit comments

Comments
 (0)