如何将 EF Core 与 AutoMapper ProjectTo 和 Unions 一起正确使用?

How do I correctly use EF Core with AutoMapper ProjectTo and Unions?

我的设置


问题

我有一个项目,其中包含一个名为 PersonDetail 的 DTO 和一个名为 Person 的实体。 当我打电话给

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

我没有得到 PersonDetail DTO,并且 Entity Framework(核心)抛出异常消息:

ArgumentException: The input sequence must have items of type 'Test.Module.Entities.Person', but it has items of type 'Test.Module.Dtos.PersonDetail'.


没有问题的例子

当我运行代码:

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

我得到 Person 个实体,无一例外。


执行计划

这是一份工作计划(有工会):

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

现在这是相同的计划,但也有自动映射器投影:

{value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Test.Module.Entities.Person]).Where(entity => ((entity != null) And ((63ed0ebd-2c02-4496-ac8d-b836cbf13259 == entity.CreatedBy) Or (393a6bb0-b437-4664-beb0-6800f509451b == entity.CreatedBy)))).Union(value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[Test.Module.Entities.Person])).Select(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)})}


注:

我调用 ToList 只是为了将这个问题减少到最小的形式。我知道在这个例子中我似乎不需要使用 ProjectTo。在我的实际代码中,我们使用的是 OData,我们需要最终结果是将 DTO 作为可查询对象的预计查询。我也明白这个 Union 并不是一个很好的 union 例子,再次强调,只是为了简化 Union 问题。

Ia 还在各自的 GitHub 项目上提出了问题:

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

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

如果没有更多详细信息,很难准确判断出了什么问题,但请确保您的映射是正确的,例如如果使用映射配置文件

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

并假设您的 EF 上下文有一个集合:

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

那么您应该可以查询上下文和项目如下:

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

您不需要 AsNoTracking,因为 EF 不跟踪非实体的结果类型,请参阅 Tracking and projections

上的文档

---更新---

尽管 EF Core 会在内存中计算它,但以下应该有效:

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

这是一个 EF Core 错误,已在 EF Core 2.1 中修复 https://github.com/aspnet/EntityFrameworkCore/issues/11033