Skip to content

Commit dab92b1

Browse files
authored
Merge pull request #4304 from kvpt/basic_include_abstract
Support projecting to an abstract type when there are included maps and also including an abstract map, presumably for configuration reuse
2 parents 77a082b + 48fd7b3 commit dab92b1

File tree

2 files changed

+109
-2
lines changed

2 files changed

+109
-2
lines changed

src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ private QueryExpressions CreateProjection(ProjectionRequest request)
5454
private (TypeMap, TypeMap[]) GetPolymorphicMaps(in ProjectionRequest request)
5555
{
5656
var typeMap = _configuration.ResolveTypeMap(request.SourceType, request.DestinationType) ?? throw TypeMap.MissingMapException(request.SourceType, request.DestinationType);
57-
return (typeMap, _configuration.GetIncludedTypeMaps(typeMap.IncludedDerivedTypes.Where(tp => tp.SourceType != typeMap.SourceType).DistinctBy(tp => tp.SourceType).ToArray()));
57+
return (typeMap, _configuration.GetIncludedTypeMaps(typeMap.IncludedDerivedTypes
58+
.Where(tp => tp.SourceType != typeMap.SourceType && !tp.DestinationType.IsAbstract).DistinctBy(tp => tp.SourceType).ToArray()));
5859
}
5960
public QueryExpressions CreateProjection(in ProjectionRequest request, LetPropertyMaps letPropertyMaps)
6061
{
@@ -64,7 +65,9 @@ public QueryExpressions CreateProjection(in ProjectionRequest request, LetProper
6465
QueryExpressions CreateProjection(in ProjectionRequest request, LetPropertyMaps letPropertyMaps, TypeMap typeMap, TypeMap[] polymorphicMaps)
6566
{
6667
var instanceParameter = Parameter(request.SourceType, "dto" + request.SourceType.Name);
67-
var projection = CreateProjectionCore(request, instanceParameter, typeMap, letPropertyMaps);
68+
var destinationType = typeMap.DestinationType;
69+
var projection = polymorphicMaps.Length > 0 && destinationType.IsAbstract ?
70+
Default(destinationType) : CreateProjectionCore(request, instanceParameter, typeMap, letPropertyMaps);
6871
foreach(var derivedMap in polymorphicMaps)
6972
{
7073
var sourceType = derivedMap.SourceType;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
namespace AutoMapper.IntegrationTests.Inheritance;
2+
3+
public class ProjectToAbstractTypeWithInheritance : IntegrationTest<ProjectToAbstractTypeWithInheritance.DatabaseInitializer>
4+
{
5+
public class StepGroup
6+
{
7+
public int Id { get; set; }
8+
public string Name { get; set; }
9+
public virtual List<Step> Steps { get; set; } = new();
10+
}
11+
public abstract class Step
12+
{
13+
public int Id { get; set; }
14+
public string Name { get; set; }
15+
public int StepGroupId { get; set; }
16+
public virtual StepGroup StepGroup { get; set; }
17+
}
18+
public class CheckingStep : Step { }
19+
public class InstructionStep : Step { }
20+
public abstract class AbstractStep : Step { }
21+
22+
public class StepGroupModel
23+
{
24+
public int Id { get; set; }
25+
public string Name { get; set; }
26+
public List<StepModel> Steps { get; set; } = new();
27+
}
28+
public abstract class StepModel
29+
{
30+
public int Id { get; set; }
31+
public string Name { get; set; }
32+
}
33+
public class CheckingStepModel : StepModel { }
34+
public class InstructionStepModel : StepModel { }
35+
public abstract class AbstractStepModel : StepModel { }
36+
37+
public class Context : LocalDbContext
38+
{
39+
public DbSet<StepGroup> StepGroups { get; set; }
40+
41+
public DbSet<Step> Steps { get; set; }
42+
43+
public DbSet<CheckingStep> CheckingSteps { get; set; }
44+
45+
public DbSet<InstructionStep> InstructionSteps { get; set; }
46+
47+
protected override void OnModelCreating(ModelBuilder modelBuilder)
48+
{
49+
modelBuilder.Entity<Step>(entity =>
50+
{
51+
entity.HasOne(d => d.StepGroup).WithMany(p => p.Steps)
52+
.HasForeignKey(d => d.StepGroupId);
53+
});
54+
}
55+
}
56+
57+
protected override MapperConfiguration CreateConfiguration()
58+
{
59+
return new MapperConfiguration(cfg =>
60+
{
61+
cfg.CreateMap<StepGroup, StepGroupModel>();
62+
cfg.CreateMap<Step, StepModel>();
63+
cfg.CreateMap<CheckingStep, CheckingStepModel>()
64+
.IncludeBase<Step, StepModel>();
65+
cfg.CreateMap<InstructionStep, InstructionStepModel>()
66+
.IncludeBase<Step, StepModel>();
67+
cfg.CreateMap<AbstractStep, AbstractStepModel>()
68+
.IncludeBase<Step, StepModel>();
69+
});
70+
}
71+
72+
public class DatabaseInitializer : DropCreateDatabaseAlways<Context>
73+
{
74+
protected override void Seed(Context context)
75+
{
76+
context.StepGroups.Add(new StepGroup
77+
{
78+
Name = "StepGroup",
79+
Steps = new List<Step>
80+
{
81+
new InstructionStep
82+
{
83+
Name = "InstructionStep"
84+
},
85+
new CheckingStep
86+
{
87+
Name = "CheckingStep"
88+
}
89+
}
90+
});
91+
92+
base.Seed(context);
93+
}
94+
}
95+
96+
[Fact]
97+
public void ProjectCollectionWithElementInheritingAbstractClass()
98+
{
99+
using var context = new Context();
100+
var steps = ProjectTo<StepGroupModel>(context.StepGroups).Single().Steps;
101+
steps[0].ShouldBeOfType<CheckingStepModel>().Name.ShouldBe("CheckingStep");
102+
steps[1].ShouldBeOfType<InstructionStepModel>().Name.ShouldBe("InstructionStep");
103+
}
104+
}

0 commit comments

Comments
 (0)