我有一个名为tf_ArtikelSearch的表值函数,该函数将语言代码作为输入。我可以使用[
作为SQL查询成功执行它SELECT *
FROM tf_ArtikelSearch('D')
WHERE PackungId = 38673
现在,我想使用Linq在其中生成相同(或相似)查询。但是当我运行下面的代码
public async Task<IEnumerable<ArtikelSearchResult>> Search(ArtikelSearchFilter filter)
{
var query = Set.FromSqlInterpolated($"dbo.tf_ArtikelSearch ({filter.SprachCode})")
.Where(result => result.PackungId == 38673);
return await query.ToListAsync();
}
我收到以下异常:
System.InvalidOperationException : FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it. Consider calling `AsEnumerable` after the FromSqlRaw or FromSqlInterpolated method to perform the composition on the client side.
at Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitFromSql(FromSqlExpression fromSqlExpression)
at Microsoft.EntityFrameworkCore.Query.SqlExpressionVisitor.VisitExtension(Expression extensionExpression)
at EntityFrameworkCore.TemporalTables.Query.AsOfQuerySqlGenerator.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.<VisitSelect>b__18_1(TableExpressionBase e)
at Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.GenerateList[T](IReadOnlyList`1 items, Action`1 generationAction, Action`1 joinAction)
at Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitSelect(SelectExpression selectExpression)
at Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.GetCommand(SelectExpression selectExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalCommandCache.GetRelationalCommand(IReadOnlyDictionary`2 parameters)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
at Refdata.SAI.Data.Repositories.ArtikelSearchRepository.Search(ArtikelSearchFilter filter) in C:\dev\Refdata.SAI\Source\Refdata.SAI.Data\Repositories\ArtikelSearchRepository.cs:line 27
at Refdata.SAI.Data.Tests.Integration.ArtikelSearchResultRepositoryTests.ArtikelSearch_OK_Test() in C:\dev\Refdata.SAI\Source\Refdata.SAI.Data.Tests.Integration\ArtikelSearchResultRepositoryTests.cs:line 21
--- End of stack trace from previous location where exception was thrown ---
请注意,没有where子句的EF查询可以正常工作。我是在做错什么还是EF core 3.1无法做到这一点?
简而言之-您不能(至少对于SqlServer而言)。 docs的Composing with LINQ部分指出:
使用LINQ进行组合要求您的原始SQL查询必须是可组合的,因为EF Core会将提供的SQL视为子查询。可以组成的SQL查询以SELECT关键字开头。
和
SQL Server不允许通过存储过程调用进行组合,因此,任何尝试将附加查询运算符应用于此类调用的尝试都将导致无效的SQL。在FromSqlRaw或FromSqlInterpolated方法之后立即使用AsEnumerable或AsAsyncEnumerable方法,以确保EF Core不会尝试对存储过程进行组合。
因此,您需要获取所有数据并在客户端进行过滤,或者创建将接受PackungId
作为参数的新存储过程,或为此创建视图/表值函数。
根据Raw SQL Queries,您不能那样做>
SQL Server不允许通过存储过程调用进行组合,因此任何 尝试将其他查询运算符应用于此类调用将导致 在无效的SQL中。正确使用AsEnumerable或AsAsyncEnumerable方法 在
FromSqlRaw
或FromSqlInterpolated
方法之后,以确保EF Core不会尝试组成存储过程。您可以从数据库中获取数据,然后按如下所示在内存中添加
Where
条件:
var searchResult = Set.FromSqlInterpolated($"dbo.tf_ArtikelSearch ({filter.SprachCode})").ToList(); var filteredResult = searchResult.Where(result => result.PackungId == 38673);
或者您可以通过添加新的
tf_ArtikelSearch
参数来更新PackungId
功能。