c# - framework - AutoMapper lanzando StackOverflowException al llamar a ProjectTo<T>() en IQueryable
automapper example c# mvc (3)
Otra opción es usar el método PreserveReferences ().
CreateMap<AppUser, AppUserDTO>().PreserveReferences();
He creado clases utilizando el Código EF Primero que tienen colecciones entre sí. Entidades:
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>();
}
}
Asignaciones:
Mapper.CreateMap<Field, FieldDTO>();
Mapper.CreateMap<FieldDTO, Field>();
Mapper.CreateMap<AppUserDTO, AppUser>();
Mapper.CreateMap<AppUser, AppUserDTO>();
Y estoy obteniendo StackOverflowException al llamar a este código (el contexto es mi dbContext):
protected override IQueryable<FieldDTO> GetQueryable()
{
IQueryable<Field> query = Context.Fields;
return query.ProjectTo<FieldDTO>();//exception thrown here
}
Supongo que esto sucede porque se repite en las listas que se llaman entre sí sin cesar. Pero no entiendo por qué sucede esto. ¿Están mal mis mapeos?
Tiene entidades que hacen referencia a sí mismas y DTO que hacen referencia a sí mismas. En general, las DTO que hacen referencia a sí mismas son una mala idea. Especialmente cuando se hace una proyección: EF no sabe cómo unirse y unirse y juntar una jerarquía de elementos.
Tienes dos opciones.
Primero, puede forzar una jerarquía específica al modelar explícitamente sus DTO con una jerarquía en mente:
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>();
}
}
Esta es la forma preferida, ya que es la más obvia y explícita.
La forma menos obvia y menos explícita es configurar AutoMapper para que tenga una profundidad máxima que irá a través de relaciones jerárquicas:
CreateMap<AppUser, AppUserDTO>().MaxDepth(3);
Prefiero ir al # 1 porque es el más fácil de entender, pero el # 2 también funciona.
Yo uso este método genérico:
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);
}