He declarado un mapa que asigna una entidad a un DTO. Ese DTO tiene una referencia de clave externa a otro DTO que debe ser mapeado por automapper, usando ProjectTo. Esto funciona perfectamente bien cuando ejecuto la solución, pero cuando uso los mapas en mis pruebas unitarias, no funciona hasta que elimino la propiedad de clave externa de mi DTO. Creo que falta algo en mi configuración de AutoMapper, pero no estoy seguro.
El modelo se ve así:
public class PendingReportDto { public Guid Id { get; set; } public Guid PatientId { get; set; } public long Identifier { get; set; } public DatabaseType Database { get; set; } public DateTime? ReportedDate { get; set; } public PatientDto Patient { get; set; } public IdentifierType IdentifierType { get; set; } }El mapa se ve así:
CreateMap<Report, PendingReportDto>() .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.Database, opt => opt.MapFrom(src => src.Database)) .ForMember(dest => dest.PatientId, opt => opt.MapFrom(src => src.PatientId)) .ForMember(dest => dest.ReportedDate, opt => opt.MapFrom(src => src.ReportedDate)) .ForMember(dest => dest.Identifier, opt => opt.MapFrom(src => src.Identifier)) .ForMember(dest => dest.IdentifierType, opt => opt.MapFrom(src => src.IdentifierType)) .ForMember(dest => dest.Patient, opt => opt.MapFrom(src => src.Patient));El paciente tiene su propio mapa que funciona perfectamente bien por sí solo. El mapa anterior se usa así:
return ReadContext.Reports .Where(x => x.Database == databaseType && x.ReportedDate == null) .ProjectTo<PendingReportDto>(_mapper.ConfigurationProvider) .ToListAsync(cancellationToken: cancellationToken);Al hacer eso me sale el siguiente error:
System.ArgumentNullException: Value cannot be null. (Parameter 'bindings')Automapper está configurado así en el proyecto de prueba unitaria:
public static class SetupAutomapper { public static IMapper Setup() { var config = new MapperConfiguration(opts => { var profiles = typeof(MappingProfile).Assembly.GetTypes().Where(x => typeof(MappingProfile).IsAssignableFrom(x)); foreach (var profile in profiles.Distinct()) { opts.AddProfile(Activator.CreateInstance(profile) as MappingProfile); } }); return config.CreateMapper(); } }Funciona si uso una declaración de selección, en lugar de usar ProjectTo para asignar a mi DTO.
ACTUALIZAR:
La investigación adicional muestra que el culpable podría ser yo ejecutando una base de datos en memoria, en lugar de mi base de datos normal, cuando ejecuto mis pruebas unitarias. Si lo cambio, incluso con el mismo conjunto de datos, funciona según lo previsto. ¿Podría ser un error con EF Core in-memory db y automapper?
Así que estoy bastante seguro de que encontré el problema al usar ProjectTo para mapear las propiedades de navegación inversa. El problema no radica en Automapper en sí mismo o en la forma en que lo configuré en mi configuración de prueba.
El culpable parece ser el proveedor de db: Entity Framework Core in-memory db.
Si cambio la base de datos con un localdb o un MS SQL DB normal, funciona bien. El proveedor de base de datos en memoria tiene ciertas limitaciones, lo que parece limitar el uso de ProjectTo con Automapper.
Fuente: https://docs.microsoft.com/en-us/ef/core/testing/
Cómo configurar la base de datos local:
private static void SetupLocalDb(DbContextOptionsBuilder builder) { builder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=Testing;Trusted_Connection=True;"); }