web-dev-qa-db-fra.com

AutoMapper lève une exception StackOverflowException lors de l'appel de ProjectTo <T> () sur IQueryable

J'ai créé des classes utilisant EF Code First qui ont des collections les unes des autres . Entités:

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

Mappages:

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

Et j'obtiens StackOverflowException lorsque j'appelle ce code (Context is my dbContext):

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

Je suppose que cela se produit parce que les listes s’appellent en boucle et s’appellent sans cesse. Mais je ne comprends pas pourquoi cela se produit. Mes mappages sont-ils erronés?

18
Peter

Vous avez des entités auto-référencées ET des DTO auto-référencés. En règle générale, les DTO auto-référencés sont une mauvaise idée. Particulièrement lors d’une projection - EF ne sait pas comment se réunir et se réunir pour réunir une hiérarchie d’éléments.

Vous avez deux choix.

Premièrement, vous pouvez forcer une profondeur spécifique de la hiérarchie en modélisant explicitement vos DTO avec une hiérarchie à l’esprit:

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

C’est le moyen privilégié, car c’est le plus évident et le plus explicite.

La méthode la moins évidente et la moins explicite consiste à configurer AutoMapper pour qu’il atteigne une profondeur maximale, afin de traverser des relations hiérarchiques:

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

Je préfère aller n ° 1 parce que c'est le plus facile à comprendre, mais le n ° 2 fonctionne aussi.

28
Jimmy Bogard

Une autre option utilise la méthode PreserveReferences ().

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

J'utilise cette méthode générique:

        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