2016-05-16 91 views
15

我已经使用EF Code First创建了具有彼此集合的类。 实体:AutoMapper在调用ProjectTo <T>()时抛出StackOverflowException IQueryable上的

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 
} 

我想这会发生,因为它循环列表无休止地调用对方。但我不明白为什么会发生这种情况。我的映射错了吗?

+0

你说得对。在列表中调用映射器时,问题是无限循环。你的映射是对的。您可以尝试在转换实体之前清空列表。 – erikscandola

回答

23

您有自引用实体和自引用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也适用。

+0

谢谢吉米。你真的帮助我。我想我会去首选。重构整个代码需要一些时间,但这将是值得的。 – Peter

+0

@Jimmy Bogard:在第一种方法中,映射顺序是否重要? –

+0

我发现.MaxDepth(n)对于在测试过程中完成映射非常有用,因此我可以深入研究以找出复杂图形的递归位置。但之后我总是把它拿出来,这样如果模型改变了,并且递归被不经意地重新引入,我们会再次出现错误。 –

7

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

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

如果我没有错,MaxDepth automaticaly启用它 – cdie

+0

PreserveReferences不适用于ProjectTo。 –

0

我用这个泛型方法:

 public static TTarget Convert<TSource, TTarget>(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<TTarget>(serializedObject); 
    } 
+0

表现会很可悲! –

相关问题