Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ public QueryExpressions CreateProjection(in ProjectionRequest request, LetProper
QueryExpressions CreateProjection(in ProjectionRequest request, LetPropertyMaps letPropertyMaps, TypeMap typeMap, TypeMap[] polymorphicMaps)
{
var instanceParameter = Parameter(request.SourceType, "dto" + request.SourceType.Name);
var projection = CreateProjectionCore(request, instanceParameter, typeMap, letPropertyMaps);
var projection = !typeMap.DestinationType.IsAbstract
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better fix is to simply skip abstract types when computing polymorphic maps.

Copy link
Author

@kvpt kvpt Jun 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with that, or perhaps I don't see how to do it this way at this level.
Because the abstract type mapping don't come from the polymorphic map but from the EnumerableProjectionMapper.Project method.
This is because it is this abstract type that is used for the collection property in the entity.
I don't see how I can filter it before this moment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess both make sense, projecting to an abstract type when there are included maps and also including an abstract map, presumably for configuration reuse.

? CreateProjectionCore(request, instanceParameter, typeMap, letPropertyMaps)
: Default(typeMap.DestinationType);

foreach(var derivedMap in polymorphicMaps)
{
var sourceType = derivedMap.SourceType;
Expand Down
72 changes: 72 additions & 0 deletions src/UnitTests/Projection/ProjectAbstractWithInheritance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace AutoMapper.UnitTests.Projection;

public class ProjectAbstractWithInheritance : AutoMapperSpecBase
{
class StepGroup
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Step> Steps { get; set; } = new HashSet<Step>();
}
abstract class Step
{
public int Id { get; set; }
public string Name { get; set; }
}
class CheckingStep : Step { }
class InstructionStep : Step { }

class StepGroupModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<StepModel> Steps { get; set; } = new HashSet<StepModel>();
}
abstract class StepModel
{
public int Id { get; set; }
public string Name { get; set; }
}
class CheckingStepModel : StepModel { }
class InstructionStepModel : StepModel { }

protected override MapperConfiguration CreateConfiguration()
{
return new MapperConfiguration(cfg =>
{
cfg.CreateMap<StepGroup, StepGroupModel>();
cfg.CreateMap<Step, StepModel>();
cfg.CreateMap<CheckingStep, CheckingStepModel>()
.IncludeBase<Step, StepModel>();
cfg.CreateMap<InstructionStep, InstructionStepModel>()
.IncludeBase<Step, StepModel>();
});
}

[Fact]
public void ProjectCollectionWithElementInheritingAbstractClassWithoutException()
{
var stepGroup = new StepGroup
{
Id = 1,
Name = "StepGroup",
Steps = new List<Step>
{
new InstructionStep
{
Id = 1,
Name = "InstructionStep"
},
new CheckingStep
{
Id = 2,
Name = "CheckingStep"
}
}
};

var query = new[] { stepGroup }.AsQueryable();

Should.NotThrow(() => query.ProjectTo<StepGroupModel>(Configuration).SingleOrDefault());
}
}