SQL Server Why does Entity Framework Core require "top 200" for ContainsTable() order by?

vulvrdjw  于 2023-10-15  发布在  其他
关注(0)|答案(1)|浏览(111)

In SSMS the following query works fine, returning the expected 1 row:

SELECT * 
FROM CONTAINSTABLE(AppUsers, *, 'hick', 200) AS t 
INNER JOIN AppUsers u ON u.Id = t.[KEY] 
ORDER BY t.[RANK] DESC

But when I call the following using Entity Framework, the exact same select:

listUsers = await dbContext.AppUsers.FromSqlInterpolated(
        $@"SELECT * FROM CONTAINSTABLE(AppUsers, *, {Query}, 200) as t INNER JOIN AppUsers u on u.Id = t.[KEY] ORDER BY t.[RANK] desc")
        .ToListAsync();

I get an exception:

Microsoft.EntityFrameworkCore.Query: Error: An exception occurred while iterating over the results of a query for context type 'LouisHowe.core.Data.NoTrackingDbContext'.

Microsoft.Data.SqlClient.SqlException (0x80131904): The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.

at Microsoft.Data.SqlClient.SqlCommand.<>c.b__208_0(Task 1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask 2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable 1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, 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.SingleQueryingEnumerable 1.AsyncEnumerator.MoveNextAsync()

ClientConnectionId:34a279b8-9257-4414-b7d0-b0a3c135a4db
Error Number:1033,State:1,Class:15

Now this works:

listUsers = await dbContext.AppUsers.FromSqlInterpolated(
        $@"SELECT top 200 * FROM CONTAINSTABLE(AppUsers, *, {Query}, 200) as t INNER JOIN AppUsers u on u.Id = t.[KEY] ORDER BY t.[RANK]")
        .ToListAsync();

But why do I have to add top 200 as I'm passing the 200 in to CONTAINSTABLE() ?

6vl6ewon

6vl6ewon1#

The answer is here :
Composing with LINQ requires your SQL query to be composable, since EF Core will treat the supplied SQL as a subquery. Composable SQL queries generally begin with the SELECT keyword, and cannot contain SQL features that aren't valid in a subquery, such as:

  • On SQL Server, an ORDER BY clause that isn't used with OFFSET 0 OR TOP 100 PERCENT in the SELECT clause

相关问题