如何将 EF Core 与 AutoMapper ProjectTo 和 Unions 一起正确使用?
How do I correctly use EF Core with AutoMapper ProjectTo and Unions?
我的设置
- ASP.NET核心2.0
- EntityFrameworkCore 2.0.1
- AutoMapper 6.2.2
问题
我有一个项目,其中包含一个名为 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
我的设置
- ASP.NET核心2.0
- EntityFrameworkCore 2.0.1
- AutoMapper 6.2.2
问题
我有一个项目,其中包含一个名为 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