作为 this question of mine 的后续,我得到了这个 SQL:
SELECT Documents.*
FROM Documents
WHERE Documents.ID IN
(
SELECT Keywords.DocumentID
FROM Keywords
WHERE
Keywords.Keyword = 'KeywordA' OR
Keywords.Keyword = 'KeywordB'
GROUP BY Keywords.DocumentID
HAVING COUNT(Keywords.Keyword) = 2
)
我确实使用 Linqer 将查询转换为 C# 以与 Entity Framework Core 5 一起使用:
from Document in db.Document
where
(from Keyword in db.Keyword
where
Keyword.Value == "KeywordA" ||
Keyword.Value == "KeywordB"
group Keyword by new {
Keyword.DocumentId
} into g
where g.Count(p => p.Value != null) == 2
select new {
g.Key.DocumentId
}).Contains(new { DocumentId = Document.DocumentId })
select Document
这编译成功,但在运行查询时,我收到一个错误:
<块引用>无法翻译 LINQ 表达式“<see below>
”。以可翻译的形式重写查询,或通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用,显式切换到客户端评估。有关详细信息,请参阅 https://go.microsoft.com/fwlink/?linkid=2101038。
上述错误消息中格式化的 LINQ 表达式如下:
DbSet<Keyword>()
.Where(k => k.Value == "KeywordA" || k.Value == "KeywordB")
.GroupBy(
keySelector: k => new { DocumentId = k.DocumentId },
elementSelector: k => k)
.Where(e => e
.Count(p => p.Value != null) == 2)
.Select(e => new { DocumentId = e.Key.DocumentId })
.Any(p => p == new { DocumentId = EntityShaperExpression:
EntityType: Document
ValueBufferExpression:
ProjectionBindingExpression: EmptyProjectionMember
IsNullable: False
.DocumentId })
我真的不明白这里有什么问题。我只能想象 Linqer 太老了,无法生成用于 EF Core 5 的有效 C# 代码。
有人能告诉我我在这里做错了什么以及如何解决问题吗? (即如何重写 C# 查询)
答案 0 :(得分:2)
看起来 .Contains(new { DocumentId = Document.DocumentId })
部分是您的问题。将其转换为表达式时遇到问题,因为此时 Document 尚未被评估。
如果你有 FK 设置,你可以这样重构它:
from d in db.Documents
where d.Keywords
.Where(k => (new[] { "keyword A", "keyword B" }).Contains(k.Keyword))
.Count() == 2
select d
答案 1 :(得分:2)
EF Core 查询翻译仍然不支持许多 LINQ 结构,所有这些没有确切支持/不支持什么的文档确实让我们走上了反复试验的道路,因此外部工具无法生成也就不足为奇了“正确”的翻译。
在这种特殊情况下,问题是带有复杂参数的 Contains
调用(尽管它是一个具有单个成员的类)。因为它们只支持带有原始参数的 Contains
。
因此,使这项工作所需的最小更改是替换
select new
{
g.Key.DocumentId
}).Contains(new { DocumentId = Document.DocumentId })
与
select g.Key.DocumentId).Contains(Document.DocumentId)
答案 2 :(得分:1)
试试这个:
var result =
from Document in db.Document
where
(from Keyword in db.Keyword
where
Keyword.Value == "KeywordA" ||
Keyword.Value == "KeywordB"
group Keyword by Keyword.DocumentId
into g
where g.Count(p => p.Value != null) == 2
select g.Key).Any(d => d == Document.DocumentId)
select Document;
答案 3 :(得分:1)
如果有 2 个单独的查询,1 个用于关键字,1 个用于文档,这不是问题,这也应该有效:
var matchedDocumentIds = db.Keywords
.Where(keyword => keyword.Keyword == "KeywordA" || keyword.Keyword == "KeywordB")
.GroupBy(keyword => keyword.DocumentID)
.Where(grp => grp.Count() > 2)
.Select(grp => grp.Key);
var filteredDocs = db.Documents.Where(doc => matchedDocumentIds.Any(matchedDocId => matchedDocId == doc.ID));
这假设两个表之间有一个外键。