Tengo entidades de este tipo para ese tema en particular: Álbum, Foto. Cada álbum contiene una colección de fotos.
Y clases Dto: AlbumWithPhotosDto, que contiene una colección de PhotoDto.
En mi perfil de automatapper describí las reglas de mapeo para todas las entidades:
CreateMap<Photo, PhotoDto>()
.ForMember(dest => dest.AverageRate, opt => opt.MapFrom(src => src.Ratings.Average(r => r.Rate)))
.ForMember(dest => dest.RatesCount, opt => opt.MapFrom(src => src.Ratings.Count))
.ReverseMap();
CreateMap<Album, AlbumWithPhotosDto>()
.ForMember(dest => dest.PhotosNum, opt => opt.MapFrom(src => src.Photos.Count))
.ReverseMap();
En primer lugar utilicé LazyLoadingProxies y cuando intenté asignar un álbum a un AlbumWithPhotosDto.
Recibí una excepción:
Tipos de asignación de errores.
Tipos de mapeo: Álbum -> ÁlbumConFotosDto DAL.Entidades.Álbum -> BLL.DataTransferObjects.AlbumWithPhotosDto
Configuración de tipo Mapa: Álbum -> AlbumWithPhotosDto DAL.Entidades.Album -> BLL.DataTransferObjects.AlbumWithPhotosDto
Miembro de destino: Fotos
Luego cambié la carga diferida por carga explícita comoes:
_context.Albums
.Include(a => a.Photos)
.Include(a => a.Author)
Pero nuevamente obtuve la misma excepción. ¿Cómo puedo solucionar esto?
Aquí las definiciones adicionales de clases:
public class Album : IEntity<int>
{
public int Id { get; set; }
/// <summary>
/// Album name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Album short description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Whether the other users can view and rate the album photos or not.
/// </summary>
public bool IsPrivate { get; set; }
/// <summary>
/// Foreign key, creator's id.
/// </summary>
public string AuthorId { get; set; }
/// <summary>
/// User who created the album but not obviously author of all photos.
/// </summary>
public virtual User Author { get; set; }
/// <summary>
/// Photos in album.
/// </summary>
public virtual ICollection<Photo> Photos { get; set; } = new HashSet<Photo>();
}
public class Photo : IEntity<int>
{
public int Id { get; set; }
/// <summary>
/// Optional photo short description.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Path to the photo file on the hard drive.
/// </summary>
public string FileName { get; set; }
/// <summary>
/// Date and time of uploading.
/// </summary>
public DateTime UploadDate { get; set; }
/// <summary>
/// Photo ratings by users.
/// </summary>
public virtual ICollection<Rating> Ratings { get; set; } = new HashSet<Rating>();
/// <summary>
/// Foreigh key, author's id.
/// </summary>
public string AuthorId { get; set; }
public virtual User Author { get; set; }
public int AlbumId { get; set; }
public virtual Album Album { get; set; }
}
public class AlbumWithPhotosDto : IDtoMetadata
{
public int Id { get; set; }
public string Description { get; set; }
public string AuthorId { get; set; }
public int PhotosNum { get; set; }
public ICollection<PhotoDto> Photos { get; set; }
}
public class PhotoDto : IDtoMetadata
{
public int Id { get; set; }
public string Base64 { get; set; }
public string Description { get; set; }
public double AverageRate { get; set; }
public int RatesCount { get; set; }
public DateTime UploadDate { get; set; }
public string AuthorId { get; set; }
public ICollection<RatingDto> Ratings { get; set; }
}
El mapeo de Clasificación también está presente, lo omití por brevedad
- Alexandr Barylo27/03/2021 a las 20:10
Tal vez se eliminó el contexto. ¿Intentaste agregar un .ToArray()?al final de su consulta, antes del mapeo?
- Grappachu27/03/2021 a las 20:15
Sí, forcé la ejecución de la consulta y los objetos del Álbum se consultaron correctamente, el contexto funciona bien
- Alexandr Barylo27/03/2021 a las 20:24
No incluiste las calificaciones en tu consulta. Por lo tanto, AutoMapper básicamente intenta llamar a .Average() en una colección vacía y, por lo tanto, falla.
Modifique su consulta como -
var albums = _context.Albums
.Include(a => a.Photos).ThenInclude(p => p.Ratings)
.Include(a => a.Author)
.ToList();
2
Lo hice, nada cambió, comenté la colección de calificaciones en la clase dto, todo sigue igual.
- Alexandr Barylo28/03/2021 a las 9:25
@AlexandrBarylo Entonces edite la publicación y comparta el código de mapeo completo, todos los modelos y también cómo está.Estamos llamando al método .Map().
-atiyar28 de marzo de 2021 a las 9:33
Como se indica en el mensaje de excepción, el problema está en la propiedad 'Fotos' en 'AlbumWithPhotosDto'. Cambie el tipo a 'Lista' e inténtelo de esa manera.
Después de invertir paso a paso, me di cuenta de que el problema aún estaba en esa línea en la configuración del mapeo:
CreateMap<Photo, PhotoDto>()
.ForMember(dest => dest.AverageRate, opt => opt.MapFrom(src => src.Ratings.Average(r => r.Rate)))
La solución fue cuidar un valor predeterminado encaso de 0 valoraciones.
CreateMap<Photo, PhotoDto>()
.ForMember(dest => dest.AverageRate, opt => opt.MapFrom(src => src.Ratings.Select(r => r.Rate).DefaultIfEmpty(0).Average()))
Y ahora incluso los LazyLoadingProxies se asignan con éxito, lo que antes consideraba incompatible con el asignador automático.