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

时间:2016-05-16 09:49:44

标签: c# entity-framework ef-code-first automapper stack-overflow

我使用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>();
    }
}

的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>();
    }
}

映射:

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

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

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

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

5 个答案:

答案 0 :(得分:28)

您有自引用实体和自引用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>();
    }
}

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

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

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

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

答案 1 :(得分:9)

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

CreateMap<AppUser, AppUserDTO>().PreserveReferences();

答案 2 :(得分:0)

我使用这种通用方法:

import pdfkit
html_content = u'<p>ö</p>'
pdfkit.from_string(html_content, 'out.pdf')

答案 3 :(得分:0)

当您为第二个实体提供 1 个 navigation_property 时,反之亦然,它会进入无限循环状态。因此,编译器会自动抛出 Stackoverflow 异常。

因此,为了避免这种情况,您只需从任何实体中删除一个 navigation_property。

答案 4 :(得分:-2)

...
MapperConfiguration(cfg =>
{
    cfg.ForAllMaps((map, exp) => exp.MaxDepth(1));
...