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

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

public class Field { public int Id { get; set; } public string Name { get; set; } public virtual List Teachers { get; set; } public Field() { Teachers = new List(); } } 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 Fields { get; set; } public AppUser() { Fields = new List(); } } 

DTO的:

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

映射:

 Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); 

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

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

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

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

你有两个选择。

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

 public class FieldDTO { public int Id { get; set; } public string Name { get; set; } public List Teachers { get; set; } public FieldDTO() { Teachers = new List(); } } 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 Fields { get; set; } public AppUserDTO() { Fields = new List(); } } 

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

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

 CreateMap().MaxDepth(3); 

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

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

 CreateMap().PreserveReferences(); 

我使用这种通用方法:

  public static TTarget Convert(TSource sourceItem) { if (null == sourceItem) { return default(TTarget); } var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var serializedObject = JsonConvert.SerializeObject(sourceItem, deserializeSettings); return JsonConvert.DeserializeObject(serializedObject); }