Mi configuración

  • ASP.NET Core 2.0
  • EntityFrameworkCore 2.0.1
  • AutoMapper 6.2.2

Problema

Tengo un proyecto con un DTO llamado PersonDetail y una entidad llamada Person. Cuando llamo

db.People.Where(p => p.FirstName == "Joe").Union(db.People.Where(p => Age > 30)).ProjectTo<PersonDetail>(mapperConfig).ToList(); 

No obtengo los DTO PersonDetail y Entity Framework (Core) lanza una excepción con el mensaje:

ArgumentException: la secuencia de entrada debe tener elementos de tipo 'Test.Module.Entities.Person', pero tiene elementos de tipo 'Test.Module.Dtos.PersonDetail'.


Ejemplo sin problema

Cuando ejecuto el código:

 db.People.Where(p => p.FirstName == "Joe").Union(db.People.Where(p => Age > 30)).ToList(); 

Obtengo las entidades Person sin excepciones.


Los planes de ejecución

Aquí hay un plan de trabajo (con un sindicato):

{value (Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [Test.Module.Entities.Person]). Donde (entity => ((entity! = null) And ((63ed0ebd-2c02-4496-ac8d-b836cbf13259 = = entity.CreatedBy) O (393a6bb0-b437-4664-beb0-6800f509451b == entity.CreatedBy)))). Union (value (Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [Test.Module.Entities.Person]) ))}

Ahora aquí está el mismo plan pero con proyecciones de automapper también:

{valor (Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [Test.Module.Entities.Person]). Donde (entity => ((entity! = null) And ((63ed0ebd-2c02-4496-ac8d-b836cbf13259 = = entity.CreatedBy) O (393a6bb0-b437-4664-beb0-6800f509451b == entity.CreatedBy)))). Union (value (Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [Test.Module.Entities.Person]) )). Seleccione (dto => new PersonDetail () {FirstName = dto.FirstName, LastName = dto.LastName, Deleted = dto.Deleted, Age = dto.Age, CreatedUtc = dto.CreatedUtc, CreatedBy = dto.CreatedBy, Id = dto.Id, RecordVersion = dto.RecordVersion, DisplayLabel = ((dto.FirstName + "") + dto.LastName)})}


Nota:

Solo estoy llamando a ToList para reducir este problema a su forma más pequeña. Entiendo que no parece que necesite usar ProjectTo en este ejemplo. En mi código real, estamos usando OData y necesitamos que el resultado final sea una consulta proyectada con los DTO como objetos consultables. También entiendo que esta Unión no es realmente un buen ejemplo sindical, pero de nuevo, solo para simplificar el problema de la Unión.

Ia también abrió problemas en los respectivos proyectos de GitHub:

EntityFrameworkCore : https://github.com/aspnet/EntityFrameworkCore/issues/ 11033

AutoMapper : https://github.com/AutoMapper/AutoMapper/issues/ 2537

5
Phobis 18 feb. 2018 a las 04:58

2 respuestas

La mejor respuesta

Fue un error de EF Core y se ha corregido en EF Core 2.1 https://github.com/aspnet/EntityFrameworkCore/issues/11033

3
Phobis 13 abr. 2018 a las 00:42

Es difícil saber exactamente qué está fallando sin más detalles, pero asegúrese de que su mapeo sea correcto, p. Ej. si usa perfiles de mapeo

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Person, PersonDetail>();
    }
}

Y asumiendo que su contexto EF tiene un conjunto:

public virtual DbSet<Person> People { get; set; }

Entonces debería poder consultar el contexto y el proyecto de la siguiente manera:

var details = _context.People
    .Where(p => p.LastName == 'Smith')
    .OrderBy(p => p.FirstName)
    .ProjectTo<PersonDetail>
    .ToList();

No necesita AsNoTracking ya que EF no rastrea los tipos de resultados que no son entidades, consulte los documentos en Seguimiento y proyecciones

--- ACTUALIZACIÓN ---

Lo siguiente debería funcionar, aunque EF Core lo evaluará en la memoria:

var firstNameQuery = db.People
    .Where(p => p.FirstName == "Joe")
    .ProjectTo<PersonDetail>(mapperConfig);
var ageQuery = db.People
    .Where(p => p.FirstName == "Joe")
    .ProjectTo<PersonDetail>(mapperConfig);
var results = firstNameQuery.Union(ageQuery).ToList();
3
ChrisR 28 feb. 2018 a las 09:47