Automapper 6.0.2.0,Mapper.Map() 在映射子实体时抛出 StackOverflow

Automapper 6.0.2.0, Mapper.Map() throws StackOverflow when mapping Child Entities

开门见山,我有以下型号:

public abstract class ControlData
{
    public DateTime CreatedDate { get; set; }
    public int CreatedById { get; set; }
    [ForeignKey("CreatedById")]
    public Collaborator CreatedBy { get; set; }
    public DateTime? UpdatedDate { get; set; }
    public int? UpdatedById { get; set; }
    [ForeignKey("UpdatedById")]
    public Collaborator UpdatedBy { get; set; }
}

[Table("My_Position_Table")]
public class Position : ControlData
{
    [Key]
    [Column("PositionId")]
    public int Id { get; set; }
    [Column("PositionStatusId")]
    public int StatusId { get; set; }
    [ForeignKey("StatusId")]
    public PositionStatus Status { get; set; }
    public int OpportunityId { get; set; }


    [ForeignKey("OpportunityId")]
    public Opportunity Opportunity { get; set; }
    public int Total { get; set; }
    [Column("PositionDurationId")]
    public int DurationId { get; set; }
    [ForeignKey("DurationId")]
    public PositionDuration Duration { get; set; }
    public DateTime StartDate { get; set; }
    //TODO Agregar las otras propiedades con sus respectivos catalogos
    public string PracticeId { get; set; }
    [ForeignKey("PracticeId")]
    public Practice Practice { get; set; }
    public int RoleId { get; set; }
    [ForeignKey("RoleId")]
    public PersonRole Role { get; set; }
    public int PlatformId { get; set; }
    [ForeignKey("PlatformId")]
    public Platform Platform { get; set; }
    public int LevelId { get; set; }
    [ForeignKey("LevelId")]
    public Level Level { get; set; }
    public int EnglishLevelId { get; set; }
    [ForeignKey("EnglishLevelId")]
    public EnglishLevel EnglishLevel { get; set; }
    public string CountryId { get; set; }
    public int LocationId { get; set; }
    [ForeignKey("LocationId")]
    public Location Location { get; set; }
    public int? OfficeId { get; set; }
    public int OperationId { get; set; }
    [ForeignKey("OperationId")]
    public Person Operation { get; set; }
    public int? EvaluatorId { get; set; }
    [ForeignKey("EvaluatorId")]
    public Collaborator Evaluator { get; set; }
    public int? SourcerId { get; set; }
    [ForeignKey("SourcerId")]
    public Collaborator Sourcer { get; set; }
    public List<Candidate> Candidates { get; set; }
    public int? PositionCancellationReasonId { get; set; }
    [ForeignKey("PositionCancellationReasonId")]
    public PositionCancellationReason CancellationReason { get; set; }
    public string CancellationComments { get; set; }
    public int? CancellationById { get; set; }
    [ForeignKey("CancellationById")]
    public Collaborator CancellationBy { get; set; }
    public DateTime? CancellationDate { get; set; }
    public bool Active { get; set; }
    public bool WhereAvailable { get; set; }
    public bool RequestAsset { get; set; }
    public string CityZone { get; set; }
    public string TravelsTo { get; set; }
    public string Description { get; set; }
    public string SpecificationFile { get; set; }
    public int PositionPriorityId { get; set; }
    public int? SourcingGroupId { get; set; }
}

[Table("My_Opportunity_Table")]
public class Opportunity : ControlData
{
    [Column("OpportunityId")]
    [Key]
    public int Id { get; set; }
    [Column("OpportunityStatusId")]
    public int StatusId { get; set; }
    [ForeignKey("StatusId")]
    public OpportunityStatus Status { get; set; }
    public string ProjectId { get; set; }
    [ForeignKey("ProjectId")]
    public Project Project { get; set; }
    public string MarketId { get; set; }
    [ForeignKey("MarketId")]
    public Market Market { get; set; }
    public string CustomerId { get; set; }
    [ForeignKey("CustomerId")]
    public Customer Customer { get; set; }
    public string Name { get; set; }
    [Column("OpportunityTypeId")]
    public int TypeId { get; set; }
    [ForeignKey("TypeId")]
    public OpportunityType Type { get; set; }
    [Column("OpportunityPriorityId")]
    public int PriorityId { get; set; }
    [ForeignKey("PriorityId")]
    public OpportunityPriority Priority { get; set; }
    public int? OpportunityCancellationReasonId { get; set; }
    [ForeignKey("OpportunityCancellationReasonId")]
    public OpportunityCancellationReason CancellationReason { get; set; }
    public string CancellationComments { get; set; }
    public int? CancellationById { get; set; }
    [ForeignKey("CancellationById")]
    public Collaborator CancellationBy { get; set; }
    public DateTime? CancellationDate { get; set; }
    public bool Active { get; set; }        
    public List<OpportunityRole> OpportunityRoles { get; set; }
    public List<Position> Positions { get; set; }

}

此外,我在 DTO 中得到了它们的等价物:

public abstract class ControlDataDTO
{

    public DateTime CreatedDate { get; set; }
    public int CreatedById { get; set; }
    public CollaboratorPlainDTO CreatedBy { get; set; }
    public DateTime? UpdatedDate { get; set; }
    public int? UpdatedById { get; set; }
    public CollaboratorPlainDTO UpdatedBy { get; set; }
}

public class PositionDTO: ControlDataDTO
{

    public int Id { get; set; }
    public int StatusId { get; set; }
    public PositionStatusDTO Status { get; set; }
    public int OpportunityId { get; set; }
    public OpportunityDTO Opportunity { get; set; }
    public int Total { get; set; }
    public int DurationId { get; set; }
    public PositionDurationDTO Duration { get; set; }
    public DateTime StartDate { get; set; }
    public string PracticeId { get; set; }
    public PracticeDTO Practice { get; set; }
    public int RoleId { get; set; }
    public PersonRoleDTO Role { get; set; }
    public int PlatformId { get; set; }
    public PlatformDTO Platform { get; set; }
    public int LevelId { get; set; }
    public LevelDTO Level { get; set; }
    public int EnglishLevelId { get; set; }
    public EnglishLevelDTO EnglishLevel { get; set; }
    public string CountryId { get; set; }
    public int LocationId { get; set; }
    public LocationDTO Location { get; set; }
    public int? OfficeId { get; set; }
    public int OperationId { get; set; }
    public PersonDTO Operation { get; set; }
    public string OperationIS { get; set; }
    public bool WhereAvailable { get; set; }
    public bool RequestAsset { get; set; }
    public string CityZone { get; set; }
    public string TravelsTo { get; set; }
    public string Description { get; set; }
    public int CandidatesAccepted { get; set; }
    public int CandidatesRejected { get; set; }
    public int CandidatesWaiting { get; set; }
    public bool HasCandidatesWaiting { get; set; }
    public int TotalCandidates { get; set; }
    public string SpecificationFile { get; set; }
    public int? EvaluatorId { get; set; }
    public int? SourcerId { get; set; }
    public CollaboratorDTO Sourcer { get; set; }
    public int? SourcingGroupId { get; set; }
    public PositionCancellationReasonDTO CancellationReason { get; set; }
}

public class OpportunityDTO: ControlDataDTO
{

    public int Id { get; set; }
    public int StatusId { get; set; }
    public OpportunityStatusDTO Status { get; set; }
    public string ProjectId { get; set; }
    public ProjectDTO Project { get; set; }
    public string MarketId { get; set; }
    public MarketDTO Market { get; set; }
    public string CustomerId { get; set; }
    public CustomerDTO Customer { get; set; }
    public string Name { get; set; }
    public int TypeId { get; set; }
    public OpportunityTypeDTO Type { get; set; }
    public int PriorityId { get; set; }
    public OpportunityPriorityDTO Priority { get; set; }
    public int? OpportunityCancellationReasonId { get; set; }
    public OpportunityCancellationReasonDTO CancellationReason { get; set; }
    public string CancellationComments { get; set; }
    public int? CancellationById { get; set; }
    public CollaboratorPlainDTO CancellationBy { get; set; }
    public DateTime? CancellationDate { get; set; }
    public CollaboratorDTO Responsible { get; set; }
    public List<OpportunityRoleDTO> OpportunityRoles { get; set; }
    public int TotalPositions { get; set; }
    public bool CandidatesWarning { get; set; }
    public bool Active { get; set; }
    public List<PositionDTO> Positions { get; set; }
}

对于此映射初始化,我们使用配置文件,如下所示:

public class AutoMapperConfig
{
    public static void RegisterMappings()
    {
        Mapper.Initialize(cfg =>
        {
            // ...
            cfg.AddProfile<OpportunityMappingProfile>();
            // ...
        });
    }
}

public class OpportunityMappingProfile : Profile
{
    public OpportunityMappingProfile()
    {
        CreateMap<Opportunity, OpportunityDTO>()
            .ForMember(x => x.Responsible, x => x.MapFrom(c => GetFromOpportunityRoles(c.OpportunityRoles, Constants.OpportunityResponsible)))
            .ForMember(x => x.TotalPositions, x => x.MapFrom(c => c.Positions.Count()))
            .ForMember(x => x.CandidatesWarning, x => x.MapFrom(c => c.Positions.Count() > 0 ? 
                c.Positions.Any(pos => pos.Candidates.Any(cand => cand.StatusId == 3)) : 
                false))
            .ForMember(x => x.CreatedBy, x => x.MapFrom(c => Mapper.Map<CollaboratorPlainDTO>(c.CreatedBy)))
            .ForMember(x => x.UpdatedBy, x => x.MapFrom(c => Mapper.Map<CollaboratorPlainDTO>(c.UpdatedBy)))
            .ForMember(x => x.Positions, x => x.MapFrom(c => Mapper.Map<List<PositionDTO>>(c.Positions))).PreserveReferences(); --> Even using this method, Whosebug exception is still occurring...
        CreateMap<OpportunityDTO, Opportunity>()
            .ForMember(x => x.CancellationReason, x => x.Ignore())
            .ForMember(x => x.CreatedBy, x => x.Ignore())
            .ForMember(x => x.UpdatedBy, x => x.Ignore())
            .ForMember(x => x.Positions, x => x.Ignore());
    }

    private Collaborator GetFromOpportunityRoles(List<OpportunityRole> opportunityRoles, string rol)
    {
        var opportunityRole = opportunityRoles.FirstOrDefault(opp => opp.ProjectRoleTypeId == rol);
        return opportunityRoles != null ? opportunityRole.Collaborator : null;
    }
}

最后,在我收到注释错误的地方执行映射的逻辑...

public class OpportunityLogic : IOpportunityLogic
{
    // Properties...

    public OpportunityLogic(parameters list here, though irrelevant for this example)
    {
        // ...
    }

    public ActionResponse<List<OpportunityDTO>> GetOpportunitiesWithPositions(int personId)
    {
        // Information is retrieved from DB, here...
        List<Opportunity> listOpportunities = opportunityRepository.Get(                
            opp => opp.Status,
            opp => opp.Market,
            opp => opp.Customer,
            opp => opp.Type,
            opp => opp.Project,
            opp => opp.Status,
            opp => opp.Positions,
            opp => opp.Positions.Select(pos => pos.Candidates),
            opp => opp.Positions.Select(pos => pos.Status),
            opp => opp.Positions.Select(pos => pos.Practice),
            opp => opp.Positions.Select(pos => pos.Role),
            opp => opp.Positions.Select(pos => pos.Platform),
            opp => opp.Positions.Select(pos => pos.Sourcer));

        // After having retrieved data, here I re-define my model.
        listOpportunities = listOpportunities
            .Where( opp => opp.StatusId == (int)Constants.OpportunityStatus.Open &&
                           opp.Active == true &&
                           opp.Positions.Any(pos => pos.StatusId == (int)Constants.PositionStatus.Open &&
                                                    pos.Candidates.Any(can => can.PersonId == personId &&
                                                                              can.Active == true &&
                                                                              (can.StatusId == (int)Constants.CandidateStatus.Lead || 
                                                                               can.StatusId == (int)Constants.CandidateStatus.Waiting))))
            .ToList();

        // MY PROBLEM IS HERE....
        var mappedOpportunities = Mapper.Map<List<OpportunityDTO>>(listOpportunities);            

        return new ActionResponse<List<OpportunityDTO>> (mappedOpportunities);

    }
}

我的问题是在尝试将我的模型 (List) 映射到 DTO (List) 时开始的;该错误是众所周知的“Whosebug Exception”。如果我使用“PreserveReferences()”方法,为什么仍然抛出相同的异常?。 “MaxDepth() 方法”在尝试了不同的深度级别 (1,2,3...) 之后也是如此。

我花了太多时间试图解决这个问题,老实说,我已经没有主意了。如果有人知道在这里做什么,我将非常感激。 感谢并提前问候!!

从 6.1.0 开始,只要可以静态检测到递归,就会在配置时自动设置 PreserveReferences。如果您的情况没有发生这种情况,请打开一个带有完整重现的问题,我们将进行调查。

http://automapperdocs.readthedocs.io/en/latest/5.0-Upgrade-Guide.html#circular-references

但是您必须删除 MapFrom 中的 Map 调用。那些 ForMember-s 不需要。

这里的问题是我缺少 另一个需要保留其引用的映射配置文件,但这里没有提到,因为我遗漏了那部分,正是那部分导致了我所有的问题。