Base solution for your next web application
Open Closed

Stackoverflow when mapping a dto object after quering the db with linq. #6092


User avatar
0
kumaran created

I am having a stackoverlow issue during the object mapper with DTO object I want to make sure my dto class is correct.

Here is the query.

public async Task<CreateOrEditOrderDto> GetOrderForEditByPaymentyId(string paymentId)
{
	var order = await _orderRepository
		.GetAll()
		.Include(o => o.OrderDetails)
		.Include(r => r.Rewards)
		.Include(p => p.OrderPromotions)
		.SingleAsync(x => x.PaymentId == paymentId);

	return ObjectMapper.Map<CreateOrEditOrderDto>(order);     <==========================Here is the stack overflow error--because order detail has order relationship which keeps loading recursive.  How can i avoid it.  See my class definition below.
}
	
[Table("Orders")]
public class Order : FullAuditedEntity 
{
	[ForeignKey("ShowId")]
	public virtual Events.Show.Show Show { get; set; }
	public virtual int ShowId { get; set; }
	..
	..
	
	public virtual OrderStatus OrderStatus { get; set; }

	public virtual ICollection<OrderDetail> OrderDetails { get; set; }
	public virtual ICollection<Reward.Reward> Rewards { get; set; }
	public virtual ICollection<OrderPromotion> OrderPromotions { get; set; }
}
	

public class OrderDetail : Entity<int>
{
	[ForeignKey("OrderId")]
	public virtual Events.Order.Order Order { get; set; }
	public virtual int OrderId { get; set; }
	[Required]
	public virtual int ShowTicketId { get; set; }

	public virtual string TicketName { get; set; }
	public virtual int TicketCount { get; set; }
	public virtual double TotalTicketAmount { get; set; }
}


public class OrderDetailDto : EntityDto
{
	public CreateOrEditOrderDto Order { get; set; }
	public int OrderId { get; set; }

	public int ShowTicketId { get; set; }

	public string TicketName { get; set; }
	public int TicketCount { get; set; }
	public double TotalTicketAmount { get; set; }
}

public class CreateOrEditOrderDto : FullAuditedEntityDto<int?>
{
	public CreateOrEditShowDto Show { get; set; }
	[Required]
	public int ShowId { get; set; }
   ..
   ..
   public ICollection<OrderDetailDto> OrderDetails { get; set; }

	public ICollection<RewardDto> Rewards { get; set; }

	public ICollection<OrderPromotionDto> OrderPromotions { get; set; }

}
   

12 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    You have self-referencing DTOs.

    refer:https://stackoverflow.com/questions/37251043/automapper-throwing-stackoverflowexception-when-calling-projecttot-on-iquery?answertab=votes#tab-top

  • User Avatar
    0
    kumaran created

    i looked at the link above and i wanted to follow this option CreateMap<AppUser, AppUserDTO>().MaxDepth(3); how do i set the maxdepth in my abp objectmapper?

  • User Avatar
    0
    maliming created
    Support Team

    I recommend that you do not use self-referencing in Dto.

  • User Avatar
    0
    kumaran created

    ok i found the customdtomapper.cs and i changed the following line in my code to use maxdepth(1)

    configuration.CreateMap<CreateOrEditOrderDto, Order.Order>().ForMember(x => x.CreatorUserId, opt => opt.Ignore()).MaxDepth(1);

    I still have stackoverflow issue on the same line as i mentioned earlier. What am i missing?

  • User Avatar
    0
    kumaran created

    It is not self referencing DTO. The child table is referencing the parent table. Please look at my class model above.

  • User Avatar
    0
    kumaran created

    my model design is basically same as "convention 4" mentioned in this link http://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx

  • User Avatar
    0
    kumaran created

    the problem is, the dto that has to match exactly as class design otherwise i am getting "AutoMapperMappingException" How can i avoid reference in dto object alone?

  • User Avatar
    0
    kumaran created

    here is the dto and the class model for example In the following dto, i removed the order reference and get the error.

    public class OrderDetail : Entity&lt;int&gt;
    {
        [ForeignKey("OrderId")]
        public virtual Events.Order.Order Order { get; set; }
        public virtual int OrderId { get; set; }
        [Required]
        public virtual int ShowTicketId { get; set; }
    
        public virtual string TicketName { get; set; }
        public virtual int TicketCount { get; set; }
        public virtual double TotalTicketAmount { get; set; }
    
    
    }
    

    public class CreateOrEditOrderDetailDto : Entity<int> { [Required] public int OrderId { get; set; }

        [Required]
        public int ShowTicketId { get; set; }
    
        public string TicketName { get; set; }
        public int TicketCount { get; set; }
        public double TotalTicketAmount { get; set; }
    }
    
  • User Avatar
    0
    maliming created
    Support Team

    Dto does not have to be consistent with the entity.

    You can look at the Dto class code in Zero.Low-level (child) Dto does not reference the superior (parent) Dto

    https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Application.Shared/Authorization/Users/Dto/GetUserForEditOutput.cs https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Application.Shared/Authorization/Users/Dto/UserEditDto.cs https://github.com/aspnetzero/aspnet-zero-core/blob/dev/aspnet-core/src/MyCompanyName.AbpZeroTemplate.Application.Shared/Authorization/Users/Dto/UserRoleDto.cs

  • User Avatar
    0
    kumaran created

    I will give a try. thanks

  • User Avatar
    0
    kumaran created

    Nope. i get the following error if the dto is different than the class.

    AutoMapper.AutoMapperMappingException: Error mapping types.

    Mapping types: CreateOrEditOrderDto -> Order Events.Order.Dtos.CreateOrEditOrderDto -> Events.Order.Order

    Type Map configuration: CreateOrEditOrderDto -> Order Events.Order.Dtos.CreateOrEditOrderDto -> Events.Order.Order

    Property: OrderDetails ---> AutoMapper.AutoMapperConfigurationException: Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters ========================================================================= CreateOrEditOrderDetailDto -> OrderDetail (Destination member list) Events.Order.Dtos.CreateOrEditOrderDetailDto -> Events.Order.OrderDetail (Destination member list)

    Unmapped properties: Order

    at AutoMapper.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps) at lambda_method(Closure , CreateOrEditOrderDto , Order , ResolutionContext ) --- End of inner exception stack trace --- at lambda_method(Closure , CreateOrEditOrderDto , Order , ResolutionContext ) at lambda_method(Closure , Object , Object , ResolutionContext ) at AutoMapper.Mapper.AutoMapper.IMapper.Map[TDestination](Object source) at Events.Order.OrdersAppService.Create(CreateOrEditOrderDto input) in C:\Kumaran\AspnetZero\Events\aspnet-core\src\Events.Application\Order\OrdersAppService.cs:line 352 at Events.Order.OrdersAppService.CreateOrEdit(CreateOrEditOrderDto input) in C:\Kumaran\AspnetZero\Events\aspnet-core\src\Events.Application\Order\OrdersAppService.cs:line 341 at Events.MultiTenancy.Payments.PaymentAppService.CreateShowPayment(ShowPaymentInfoInput input) in C:\Kumaran\AspnetZero\Events\aspnet-core\src\Events.Application\MultiTenancy\Payments\PaymentAppService.cs:line 148 at lambda_method(Closure , Object ) at Mic...

  • User Avatar
    1
    kumaran created

    found the fix. I have to ignore those attributes in CustomDtoMapper.cs that are not required to be mapped. In my case the order object in the order child tables needs to be ignored otherwise you get into recursive relationship.

           configuration.CreateMap&lt;CreateOrEditOrderDetailDto, OrderDetail&gt;().ForMember(x => x.Order, opt => opt.Ignore());
           configuration.CreateMap&lt;CreateOrEditRewardDto, Reward.Reward&gt;().ForMember(x => x.Order, opt => opt.Ignore());
           configuration.CreateMap&lt;CreateOrEditOrderPromotionDto, OrderPromotion&gt;().ForMember(x => x.Order, opt => opt.Ignore());