无法使用 OData、EF Core 和 AutoMapper 映射 List<> 导航属性

Posted

技术标签:

【中文标题】无法使用 OData、EF Core 和 AutoMapper 映射 List<> 导航属性【英文标题】:Unable to map List<> navigational properties using OData, EF Core and AutoMapper 【发布时间】:2020-01-01 15:10:51 【问题描述】:

我目前正在编写一个 ASP .NET Core API,利用 OData 进行查询,并使用 Entity Framework 与数据库对话。

我想将域对象与发送给用户的 DTO 分开,因此也开始使用 AutoMapper 将实体框架查询结果转换为我创建的 DTO。

此时(在我测试时),我的 DTO 和域对象是相同的 - 只是公共 getter/setter 属性。 DTO 示例如下:

    public class NoteDTO
    
        public int Id  get; set; 
        public string Body  get; set; 
        public string Conclusion  get; set; 
        public string Title  get; set; 

        public ManagerDTO Manager  get; set; 
    

    public class ManagerDTO
    
        public int Id  get; set; 

        public virtual List<ProductDto> Products  get; set; 
    

    public class ProductDto
    
        public int Id  get; set; 
    

我的 NotesController 中还有一个用于获取笔记的测试方法(同样,使用 OData),如下所示:

        [HttpGet]
        [EnableQuery]
        public IQueryable<NoteDTO> GetMeeting()
        
            var config = new MapperConfiguration(cfg =>
            
                cfg.CreateMap<Note, NoteDTO>();
                cfg.CreateMap<Product, ProductDto>();
                cfg.CreateMap<Manager, ManagerDTO>()
                    .ForMember(md => md.Products, conf => conf.MapFrom(m => m.Products));
            );
            return _context.Notes.ProjectTo<NoteDTO>(config);
        

然后我尝试使用以下查询访问我的 API:

https://localhost:5001/api/Notes?$select=Id,Body,Conclusion&$top=5&$expand=Manager($select=Id)

但是,这失败了,在堆栈跟踪中,我收到以下错误消息:

System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Tuple`3[TestEntityFramework.DataObjects.ProductDto,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject]]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto]' of method 'System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto] _ToEnumerable[ProductDto](System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto])'

如果我从 ManagerDTO 对象和相关的产品映射配置中删除列表,则上述查询成功。

我在 GitHub 问题上看到了这条评论,听起来像是同样的问题,但尝试实施该建议并没有帮助(假设我理解正确):https://github.com/AutoMapper/AutoMapper/issues/2853#issuecomment-482317381

还有其他人遇到过这个问题吗?我仍然习惯 AutoMapper,所以可能错过了一些明显的东西,但是通过搜索这似乎是一个相当罕见的问题,因此很难找到关于这里发生了什么的指针。

对于将 OData 查询转换为实体框架,然后再转换回 DTO 的最佳方式,我愿意接受任何其他建议 - 如果我在这里所做的不是最佳的!

【问题讨论】:

virtual 需要在ManagerDTO.Products 中吗? 可能不是——我基本上只是在复制我在模型类中的内容。但是,删除它并没有什么不同。 【参考方案1】:

您是否在使用 Automapper 集合扩展?如果没有,这应该可以解决您的问题:https://github.com/AutoMapper/AutoMapper.Collection

【讨论】:

感谢您的回复!今天我正在查看 AutoMapper ExpressionMapping 库,因为这听起来更像是我正在寻找的东西。使用它我可以生成我最初想要的查询,但是由于每次调用都会完成数据库查询,而不是通常的缓存 EF 似乎做的,所以性能不足。 这不起作用,除非您映射列表本身。如果列表是类的导航属性,则 Automapper.Collection 无效。我确信这应该有效,但它没有。 你的映射看起来像这样吗?:this.CreateMap, IList>();如果是这样,它应该按预期工作。如果没有,那么值得考虑 DBContext 的生命周期 - 它是否在您的 db 调用之间被处理和重新创建?这或许可以解释缺乏缓存的原因?

以上是关于无法使用 OData、EF Core 和 AutoMapper 映射 List<> 导航属性的主要内容,如果未能解决你的问题,请参考以下文章

带有 EF 和自动映射器的 OData:无法比较..'。仅支持原始类型、枚举类型和实体类型

使用Odata基于EF查询DTO

使用 EF 和 WebAPI,如何返回 ViewModel 并支持 IQueryable/OData? [复制]

使用 Asp.Net WebApi + EF + Odata 深度插入数据

.Net Core 1.1 上的 OData v4 缺少 /$metadata

Breeze、OData 和无 EF