我加入 .NetCore 3.1 抛出一个关于 NavigationExpandingExpressionVisitor 的异常,那是什么?

My join .NetCore 3.1 throws an exception about NavigationExpandingExpressionVisitor, what is that?

我有一个 .NetCore 3.1 项目。我知道从 EF Core 2 到 3 有重大变化,但寻找解决方案让我发现了一些毫无意义的地方。

以下适用于 .NetCore 2.2。

我有一个从其他查询生成的用户名列表。我现在想在我们的人事数据库中找到这些用户名,目的是返回每个用户名的关联电子邮件地址。一个人可以选择使用公司电子邮件地址或提供不同的地址。如果 person.EmailAddress 字段为空,那么我需要的地址是附加公司域的用户名。

private static List<string> GetEmailAddrsFromBp(PersonnelContext personnelContext, IEnumerable<string> userNames) {
    try {

        var personEmail = (
            from person in personnelContext.Persons
            join userName in userNames
                on person.userName.Trim().ToLower() equals userName.Trim().ToLower()
            where person.ActualEndDate == null
            select person.EmailAddress.Trim().Equals("")
                ? person.userName.Trim().ToLower() + "@myCompany.com"
                : person.EmailAddress.Trim().ToLower()
        ).Distinct().OrderBy(a => a).ToList();

        return personEmail;
    } catch (Exception e) {
        throw new Exception("GetEmailAddrsFromBp: " + e.Message);
    }
}

在 3.1 中我得到异常:

Processing of the LINQ expression 'DbSet<Persons>
    .Join(
        outer: __p_0, 
        inner: person => person.userName.Trim().ToLower(), 
        outerKeySelector: userName => userName.Trim().ToLower(), 
        innerKeySelector: (person, userName) => new { 
            person = person, 
            userName = userName
         })' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.

我不明白这个错误。转到建议的 Microsoft 站点没有帮助。其他谷歌搜索已证明无济于事。到底是怎么回事? "simple" 现在加入如何?

I do not understand this error.

错误信息当然不是用户友好的。唯一相关的部分是

This may indicate either a bug or a limitation in EF Core.

可以安全地读作 "This is either a bug or a limitation in EF Core."

What is going on? How do you do "simple" joins now?

可以加入"simple",但不能加入内存集合。事实上,从未真正支持连接到内存集合。只是 EF Core 1.x / 2.x 使用所谓的 客户端评估 来处理它无法翻译的内容。但是 implicit client evaluation has been removed in 3.0,现在您应该找到一个可翻译的构造,或者通过 LINQ to Objects(或 System.Linq.Async)显式切换到客户端评估。

由于专门针对连接切换到客户端评估效率不高,因此最好 find/use 可翻译的查询结构。如果你使用非equi或多键连接,你基本上没有选择。但是对于单键等值连接,有一个在所有 EF / EF Core 版本中都支持的结构,它是 Enumerable.Contains 转换为 SQL IN (val1, val2, ..., valN).

所以针对你的具体案例的解决方案是这样的:

userNames = userNames.Select(userName => userName.Trim().ToLower()).Distinct();

var personEmail = (
    from person in personnelContext.Persons
    where userNames.Contains(person.userName.Trim().ToLower())
    // the rest of the query unchanged...