Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
523 views
in Technique[技术] by (71.8m points)

c# - AutoMapper throwing StackOverflowException when calling ProjectTo<T>() on IQueryable

I have created classes using EF Code First that have collections of each other. Entities:

public class Field
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<AppUser> Teachers { get; set; }
    public Field()
    {
        Teachers = new List<AppUser>();
    }
}

public class AppUser
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string UserName => Email;
    public virtual List<Field> Fields { get; set; }
    public AppUser()
    {
        Fields = new List<FieldDTO>();
    }
}

DTOs:

public class FieldDTO
{ 
    public int Id { get; set; }
    public string Name { get; set; }
    public List<AppUserDTO> Teachers { get; set; }
    public FieldDTO()
    {
        Teachers = new List<AppUserDTO>();
    }
}

 public class AppUserDTO
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string UserName => Email;
    public List<FieldDTO> Fields { get; set; }
    public AppUserDTO()
    {
        Fields = new List<FieldDTO>();
    }
}

Mappings:

Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();

And I am getting StackOverflowException when calling this code (Context is my dbContext):

protected override IQueryable<FieldDTO> GetQueryable()
{
    IQueryable<Field> query = Context.Fields;
    return query.ProjectTo<FieldDTO>();//exception thrown here
}

I guess this happens because it loops in Lists calling each other endlessly. But I do not understand why this happens. Are my mappings wrong?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have self-referencing entities AND self-referencing DTOs. Generally speaking self-referencing DTOs are a bad idea. Especially when doing a projection - EF does not know how to join together and join together and join together a hierarchy of items.

You have two choices.

First, you can force a specific depth of hierarchy by explicitly modeling your DTOs with a hierarchy in mind:

public class FieldDTO
{ 
    public int Id { get; set; }
    public string Name { get; set; }
    public List<TeacherDTO> Teachers { get; set; }
    public FieldDTO()
    {
        Teachers = new List<TeacherDTO>();
    }
}

public class TeacherDTO 
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public string UserName => Email;
}

public class AppUserDTO : TeacherDTO
{
    public List<FieldDTO> Fields { get; set; }
    public AppUserDTO()
    {
         Fields = new List<FieldDTO>();
    }
}

This is the preferred way, as it's the most obvious and explicit.

The less obvious, less explicit way is to configure AutoMapper to have a maximum depth it will go to traverse hierarchical relationships:

CreateMap<AppUser, AppUserDTO>().MaxDepth(3);

I prefer to go #1 because it's the most easily understood, but #2 works as well.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...