15

Ich habe Klassen erstellt mit EF-Code zuerst, die Sammlungen voneinander haben. Instanzen:AutoMapper werfen StackOverflowException beim Aufruf von ProjectTo <T>() auf 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>(); 
    } 
} 

DTOs:

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

Mappings:

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

Und ich bin immer Stackoverflow wenn Sie diesen Code aufrufen (Context ist mein DbContext):

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

Ich schätze, das passiert, weil es sich in Listen umschlingt, die sich gegenseitig endlos aufrufen. Aber ich verstehe nicht, warum das passiert. Sind meine Abbildungen falsch?

+0

Du hast Recht. Das Problem ist eine Endlosschleife beim Aufruf von Mapper in Listen. Deine Abbildungen sind richtig. Sie können versuchen, Listen vor dem Konvertieren von Entitäten zu leeren. – erikscandola

Antwort

23

Sie verfügen über selbstverweisende Entitäten und selbstreferenzierende DTOs. Im Allgemeinen sind selbstreferenzierende DTOs eine schlechte Idee. Besonders wenn man eine Projektion macht - EF weiß nicht, wie man sich zusammenfügt und zusammen eine Hierarchie von Gegenständen zusammenfügt.

Sie haben zwei Möglichkeiten.

Erstens können Sie eine bestimmte Tiefe der Hierarchie erzwingen, indem explizit Ihre DTOs mit einer Hierarchie im Sinne der Modellierung:

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

Dies ist die bevorzugte Art und Weise, wie es die naheliegendste und expliziter ist.

Je weniger offensichtlich, weniger explizite Weise ist AutoMapper zu konfigurieren, um eine maximale Tiefe haben sie hierarchische Beziehungen zu überqueren gehen:

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

Ich ziehe # 1 zu gehen, weil es die am leichtesten zu verstehen ist, sondern # 2 funktioniert auch.

+0

Danke Jimmy. Du hast mir wirklich geholfen. Ich denke, ich werde für die erste Wahl gehen. Es wird einige Zeit brauchen, um den ganzen Code umzuformen, aber es wird sich lohnen. – Peter

+0

@Jimmy Bogard: Ist im ersten Ansatz die Mapping-Reihenfolge wichtig? –

+0

Ich finde, dass .MaxDepth (n) ist sehr nützlich, um die Karte während des Testens fertig zu machen, so dass ich dann eingraben kann, um herauszufinden, wo die Rekursion für komplexe Graphen herkommt. Aber ich nehme es immer hinterher heraus, so dass, wenn sich das Modell ändert und Rekursion zufällig wieder eingeführt wird, wir erneut einen Fehler bekommen. –

7

Andere Option verwendet PreserveReferences() -Methode.

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

Wenn ich mich nicht irre, aktiviert MaxDepth automatisch – cdie

+0

PreserveReferences gilt nicht für ProjectTo. –

0

Ich benutze diese generische Methode:

 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

Leistung wäre sehr erbärmlich! –

Verwandte Themen