2016-05-16 18 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

表現會很可悲! –

相關問題