【问题标题】:Query cannot be translated无法翻译查询
【发布时间】:2021-04-13 00:46:36
【问题描述】:

我有以下课程:

class document
{
    int Id;
    List<Tag> Tags;
}

class Tag
{
    int Id;
}

我想获取至少具有用户选择的标签之一的所有文档。

我编写了以下 linq 查询:

List<int> tagIds = tags.Select (x => x.Id).ToList ();

query.Where (doc => tagIds.Any (x => doc.Tags.Select (y => y.Id).Contains (x)));

如果我对文档列表执行它,它可以工作,但如果我使用 efcore 5 对 sqlite 数据库执行它,我会收到以下错误:

System.InvalidOperationException: 'The LINQ expression 'DbSet<DemaDocument>()
    .Where(d => __tagIds_0
        .Any(x => DbSet<Dictionary<string, object>>("DemaDocumentDemaTag")
            .Where(d0 => EF.Property<Nullable<int>>(d, "Id") != null && object.Equals(
                objA: (object)EF.Property<Nullable<int>>(d, "Id"), 
                objB: (object)EF.Property<Nullable<int>>(d0, "DocumentsId")))
            .Join(
                inner: DbSet<DemaTag>(), 
                outerKeySelector: d0 => EF.Property<Nullable<int>>(d0, "TagsId"), 
                innerKeySelector: d1 => EF.Property<Nullable<int>>(d1, "Id"), 
                resultSelector: (d0, d1) => new TransparentIdentifier<Dictionary<string, object>, DemaTag>(
                    Outer = d0, 
                    Inner = d1
                ))
            .Select(ti => ti.Inner.Id)
            .Any(p => p == x)))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'

如何使用 fluent LINQ 重写查询以使其正常工作?有可能,还是我必须检索内存中的文档然后运行查询?这并不理想,因为文档会随着时间的推移而增长......

提前致谢

【问题讨论】:

  • 您是否尝试过反转查询:query.Where(doc =&gt; doc.Tags.Any(x =&gt; tagIds.Contains(x.Id)))?这可能允许我们将 Contains 转换为 IN 语句,因为 tagsIds 列表对于查询是常量。
  • 它就像一个魅力!您是否介意将其作为答案并解释我为什么它会起作用,以便将来我能够将此解决方案应用于其他问题?提前致谢!

标签: c# entity-framework linq ef-core-5.0


【解决方案1】:

.Contains 应翻译为 SQL IN 语句,即x IN 1, 2, 3。这要求列表保持不变。在您的示例中,doc.Tags.Select (y =&gt; y.Id) 对于每个文档都是唯一的,因此无法转换为常量列表。

您所做的或多或少是检查两个列表是否相交,因此我们应该能够颠倒两个列表的顺序:

query.Where(doc => doc.Tags.Any(x => tagIds.Contains(x.Id)))

现在查询的tagIds是不变的,.Contains语句可以正确翻译。

【讨论】:

猜你喜欢
  • 2021-06-23
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
  • 2021-11-22
  • 2023-04-02
  • 2020-05-22
  • 2021-12-13
  • 2018-12-29
相关资源
最近更新 更多