webentwicklung-frage-antwort-db.com.de

AutoMapper löst StackOverflowException aus, wenn ProjectTo <T> () für IQueryable aufgerufen wird

Ich habe mit EF Code First Klassen erstellt, die jeweils Sammlungen voneinander haben . Entitäten:

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

Zuordnungen:

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

Beim Aufruf dieses Codes erhalte ich StackOverflowException (Context ist mein dbContext):

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

Ich vermute, das passiert, weil es sich in Listen umläuft und sich endlos ruft. Aber ich verstehe nicht, warum das so ist. Sind meine Zuordnungen falsch?

18
Peter

Sie verfügen über selbst referenzierende Entitäten und selbst referenzierende DTOs. Im Allgemeinen sind selbstreferenzierende DTOs eine schlechte Idee. Vor allem bei einer Projektion - EF weiß nicht, wie man eine Hierarchie von Elementen zusammenfügt und zusammenfügt.

Sie haben zwei Möglichkeiten.

Erstens können Sie eine bestimmte Tiefe der Hierarchie erzwingen, indem Sie Ihre DTOs explizit unter Berücksichtigung einer Hierarchie modellieren:

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 der bevorzugte Weg, da es der naheliegendste und eindeutigste ist.

Die weniger offensichtliche, weniger explizite Methode besteht darin, AutoMapper so zu konfigurieren, dass er eine maximale Tiefe hat, durch die hierarchische Beziehungen durchlaufen werden:

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

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

28
Jimmy Bogard

Eine andere Option ist die PreserveReferences () -Methode.

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

Ich verwende 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
Xtremexploit