我想编写一个LINQ查询,用于查找与特定课程具有最多intersect标记的10门课程。
我写了它,但是我得到了一个异常。我不想使用ToList
或AsEnumerable
,因为我不想在得到10之前执行查询来获得所有课程。
我不想使用其他ORM。
代码:
var courses = await _dbContext.Courses
.AsNoTracking()
.Where(c => c.Id != request.Id)
.Select(c => new
{
Course = c,
IntersectTagsCount = c.Tags.Sum(t => course.Tags.Contains(t) ? 1 : 0)
})
.OrderByDescending(c => c.IntersectTagsCount)
.Take(10)
.ToListAsync();
我得到的错误是:
系统操作无效异常:LINQ表达式't =〉__course_Tags_1 .包含(t)?1:“0”未能转换。请以可以转换的形式重写查询,或者通过插入对“AsEnumerable”、"AsAsyncEnumerable“、”ToList“或”ToListAsync“的调用来显式切换到客户端计算。
实体如下所示:
它存储在数据库中,如下所示:
1条答案
按热度按时间23c0lvtd1#
尝试将另一个实体或DTO插入查询表达式时会出现问题。(countrse.Tags)
给予这个。
**编辑:**好吧,我没注意到你在实体中将标签存储为数组。不幸的是,你将无法转换一个Array.Sum,其中Tags属性可能需要通过一个值转换器向下转换为一个varchar,以构建一个逗号分隔的值来存储在DB中。
我的建议是,如果您需要能够按标签查询/合并课程,那么您应该规范化表结构,以便标签引用可以跨课程关联。类似这样的关系将是标准的多对多关系,其中我们创建一个标签表(TagId和TagName),然后创建一个CourseTags表(CourseId和TagId作为复合PK,FK返回到各自的表)
实体结构略有变化:
EF可以完全在幕后管理CourseTags表。在上面的例子中,我只是使用标签名称/字符串作为ID/PK(即“TestTagName 1”),因为这个选项可能会减少重构使用的麻烦。如果预计会有很多标签,我建议使用标识整数作为ID,因为这将更容易索引。
现在假设您的客户端代码传递了一个过滤后的标签ID数组,以检查为course.tags(字符串数组),那么EF现在可以通过CourseTags关系在相关的Tag行之间构建查询:
不同之处在于,EF在课程和标签之间具有关系,它可以围绕Linq操作构建查询,因为它知道课程将与一组标签记录相关联,而不是包含标签值附加在一起的单个列。
另一个可能适用于EF Core 7的选项是将标签作为JSON值集存储在课程中,因为我相信EF Core确实支持对JSON数据的查询。不幸的是,这不是我花时间修补的东西,但如果您决定确实更喜欢将标签存储在字段中而不是关系表中,则可能值得研究。