【问题标题】:When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type从“VisitLambda”调用时,重写“System.Linq.Expressions.ParameterExpression”类型的节点必须返回相同类型的非空值
【发布时间】:2020-04-28 03:22:03
【问题描述】:

我将我的数据层类库项目从 .NETCore 2.2 升级到 .NETCore3.1,还将 Nuget 包(如 EntityFrameWork Core)从 2.2.0 升级到 EFCore 3.1.0。

然后我在验证以下代码时遇到如下错误:

代码:

private async Task<List<CountryDTO>> GetCountriesDataAsync(int languageId)
{
    int pageNo = 1, pageSize = 100;
    var images = await _cacheService.GetAllAsync("imagesCacheKey");
    return await _dbContext.Countries
                    .Where(cc => cc.IsPublished.Equals(true) 
                                     && cc.LanguageId.Equals(languageId))
                    .Select(co => new CountryDTO
                            {  Uuid = co.CountryId, 
                               PNGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.PNGImageId))
                                                    .FilePath, 
                                SVGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.SVGImageId))
                                                    .FilePath, 
                          DisplayName = co.DisplayName, 
                          DisplayNameShort = co.DisplayName, 
                          Name = Helper.ReplaceChars(co.DisplayName), 
                          Path = Helper.ReplaceChars(co.DisplayName), 
                          CompleteResponse = true})
                   .Skip((pageNo - 1) * 100)
                   .Take(pageSize)
                   .ToListAsync();
}

错误:

When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
 ---> System.InvalidOperationException: When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
   at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](T node, String callerName)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitParameters(ExpressionVisitor visitor, IParameterProvider nodes, String callerName)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMember(MemberExpression memberExpression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
   at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.CosmosQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
 at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
 at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Author.Query.Persistence.CountryService.GetCountriesDataAsync(Int32 languageId) in /src/QueryStack/Author.Query.Persistence/CountryService.cs:line 243
  at Author.Query.Persistence.CountryService.GetCountriesAsync(Int32 dftLanguageId, Int32 localeLangId) in /src/QueryStack/Author.Query.Persistence/CountryService.cs:line 228
  at Author.Query.Persistence.CountryService.GetAllCountriesAsync(LanguageDTO language) in /src/QueryStack/Author.Query.Persistence/CountryService.cs:line 164
  at GraphQL.DataLoader.DataLoaderBase`1.DispatchAsync(CancellationToken cancellationToken)
   at Author.Query.New.API.GraphQL.Resolvers.CountriesResolver.<>c__DisplayClass5_1.<<Resolve>b__3>d.MoveNext() in /src/QueryStack/Author.Query.New.API/GraphQL/Resolvers/CountriesResolver.cs:line 40
--- End of stack trace from previous location where exception was thrown ---
   at GraphQL.Types.ResolveFieldContext`1.TryAsyncResolve[TResult](Func`2 resolve, Func`2 error)
   --- End of inner exception stack trace ---

任何人都可以通过提供解决此问题的指导来帮助我吗?

【问题讨论】:

  • 你已经有一个错误。此查询根本无法转换为 SQL,因为它调用 本地函数。 EF Core 2.2 的客户端执行通过将几乎所有内容拉到客户端并在那里评估其余查询来掩盖错误。这会通过加载比需要更多的记录和/或在只需要少数列时加载所有列而严重损害性能。修复(非常难以阅读!)查询,使其使用任何本地函数。
  • 顺便说一句,如果您启用了 EF Core 2.x 日志记录,您会看到 EF 在运行时已经发出警告
  • 不只是Helper.ReplaceChar 破坏了这个查询。 images.FirstOrDefault(...) 搜索本地集合。无法将其转换为 SQL。查询是如此复杂,尽管 LINQ 提供程序在尝试将其转换为 SQL 之前因该错误而失败
  • images.FirstOrDefault 也不应该使用。您只是通过 ImageId 查找 FilePath。当前代码扫描所有图像以找到匹配的图像。使用字典代替保存 ImageID 和 FilePath 值,并使用Dictionary.TryGetValue 查找路径,例如images.TryGetValue(co.PNGImageID,out var img)? img.FileName:null
  • 感谢@PanagiotisKanavos 的回复。根据您的输入,我已经更新了代码,它对我有用:)

标签: linq azure-cosmosdb c#-8.0 .net-core-3.1 ef-core-3.1


【解决方案1】:

这个错误刚刚发生在我身上,对于未来的访问者来说,这是我做错的:

我有两个模型,例如:

public User
{
    public int Id {get; set;}
    public string Name {get; set;}
    //just a silly method that does something
    public string GetName()
    {
      return this.Name + "something"
    }
}
public UserInfo
{
    public int Id {get; set;}
    // a bunch of other properties
}

在代码中的某个地方,我使用自动映射器进行投影,例如

Mapper.ProjectTo<UserInfo>(Repository.Get().ByUserName(userId)).FirstOrDefault();

当我插入 GetName 以某种方式破坏 projectTo 映射的方法时,此错误 When called from 'VisitLambda',... 开始显示。

【讨论】:

    【解决方案2】:

    这里有几个很容易解决的问题,将查询分成两半,第一个查询数据库并返回部分填充的 CountryDTO 列表,第二个遍历填充缺失项的列表:

    private async Task<List<CountryDTO>> GetCountriesDataAsync(int languageId)
    {
                int pageNo = 1, pageSize = 100;
    
                var results = _dbContext.Countries // <- do not use await on db context
                        .Where(cc => cc.IsPublished.Equals(true)
                          && cc.LanguageId.Equals(languageId))
                        .Select(co => new {
                            co.CountryId,
                            co.DisplayName,
                            co.PNGImageId,
                            co.SVGImageId
                        })
                         .Skip((pageNo - 1) * pageSize) // <- use page size no 100
                         .Take(pageSize)
                         .ToListAsync();
    
                var images = await _cacheService.GetAllAsync("imagesCacheKey");
    
                return Task.FromResult( results.Select(co => new CountryDTO
                {
                    Uuid = co.CountryId,
                    PNGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.PNGImageId))?.FilePath, // <- could be null so use ?.FilePath
                    SVGImagePath = images.FirstOrDefault(im => im.ImageId.Equals(co.SVGImageId))?.FilePath,
                    DisplayName = co.DisplayName,
                    DisplayNameShort = co.DisplayName,
                    Name = Helper.ReplaceChars(co.DisplayName),
                    Path = Helper.ReplaceChars(co.DisplayName),
                    CompleteResponse = true
                }).ToList());
    
    }
    

    我创建了一个匿名类型来保存来自数据库的返回,而不是向 DTO 添加额外的字段以获取图像 ID 等。 如果您需要从数据库中获取更多信息,只需在上下文选择语句中将字段添加到匿名类型即可。

    【讨论】:

    • 感谢@David Hinchliffe 的详细回复 :)
    • OP 的代码实际上是否存在问题,或者这仍然是 EF Core 中的错误?
    • 代码的问题是链接语句试图做一些底层数据库不知道怎么做的事情,特别是数据库不怎么做 Helper.ReplaceChars,所以 linq 不能'不要创建要在数据库上运行的 SQL 语句。 Await 可以与数据库一起使用,但要小心,任务的执行必须在 db 上下文被释放之前完成,如果使用了错误的提供者模式,就会发生这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    • 2020-12-29
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多