在IQueryable上调用ProjectTo <T>()时,AutoMapper抛出StackOverflowException

Pet*_*ter 19 c# stack-overflow entity-framework automapper ef-code-first

我使用EF Code First创建了具有彼此集合的类.实体:

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

DTO的:

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

映射:

Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
Run Code Online (Sandbox Code Playgroud)

我在调用此代码时遇到StackOverflowException(Context是我的dbContext):

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

我想这是因为它在列表中循环,无休止地互相呼叫.但我不明白为什么会这样.我的映射是错的吗?

Jim*_*ard 30

您有自引用实体和自引用DTO.一般来说,自引用DTO是一个坏主意.特别是在进行投影时 - EF不知道如何连接在一起并连接在一起并将项目层次结合在一起.

你有两个选择.

首先,您可以通过明确建立具有层次结构的DTO来强制特定深度的层次结构:

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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是首选方式,因为它是最明显和最明确的方式.

不那么明显,不太明确的方法是将AutoMapper配置为具有遍历层次关系的最大深度:

CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
Run Code Online (Sandbox Code Playgroud)

我更喜欢去#1,因为它是最容易理解的,但#2也适用.

  • `MaxDepth` 似乎对我不起作用 - 版本 `7.0.1.0` (2认同)

小智 9

其他选项是使用PreserveReferences()方法.

CreateMap<AppUser, AppUserDTO>().PreserveReferences();
Run Code Online (Sandbox Code Playgroud)