web-dev-qa-db-ja.com

Entity Framework Core; (MS)SQLServerに対するクエリでORDERBYを使用する

Microsoft SQL Server2016に対してEntityFrameworkCoreと組み合わせて次のクエリを使用しようとしています。

_SELECT [a], [b], [c]
FROM [x]
WHERE [a] = {0}
ORDER BY  [b]
_

私はこのクエリを次のように使用します:

_context.MySet.AsNoTracking()
  .FromSql(MyQuery, aValue)
  .Skip(pageSize * page)
  .Take(pageSize)
  .Select(x => x.ToJsonDictionary())
  .ToList()
_

これを.NETCore REST API with paginationで使用し、ページネーションをより使いやすくするためにレコードを(アルファベット順に)ソートしたいのですが。上記を実行すると次のエラーが発生します。ステートメント:

ORDER BY句は、TOP、OFFSET、またはFOR XMLも指定されていない限り、ビュー、インライン関数、派生テーブル、サブクエリ、および共通テーブル式では無効です。FETCHステートメントでのオプションNEXTの使用が無効です。 ORDER BY句は、TOP、OFFSET、またはFOR XMLも指定されていない限り、ビュー、インライン関数、派生テーブル、サブクエリ、および共通テーブル式では無効です。FETCHステートメントでのオプションNEXTの使用が無効です。

同様の問題を探して、私はこれらの他のいくつかの投稿( 12 、)を見つけましたが、EFCoreと組み合わせて使用​​されたものはありません/または、私の場合には当てはまらない別のコンテキスト(サブクエリなど)で使用していました。

クエリの_ORDER BY_ではなくEFの.OrderBy(..)構文を使用しようとしましたが、これで問題が解決しません。また、クエリのSELECTの後に_TOP 100 PERCENT_を_ORDRE BY_と組み合わせて追加してみました。これは機能しましたが、列を注文しませんでした。無視されただけです。この制限については、 EF制限 で説明されています。また、_TOP 100 PERCENT..._を_TOP 99.99 PERCENT..._または_TOP 9999999..._ `に置き換える この投稿 も見つかりました。これはうまくいくはずのようですが、正しく感じられません。この問題は一般的にさらに説明されています ここ

概要:ビューでORDERBYを使用することはお勧めできません。ビューの外でORDERBYを使用します。実際、正しい設計は同じことを意味します。 TOPをViewsと一緒に使用すると、Viewがテーブルのすべての行を返さないか、ORDERBYを完全に無視する可能性が高くなります。

さらに、私は「ビュー」という言葉に混乱しています。私にとって、ビューという用語は、_CREATE VIEW .._構文によって作成されたビューの使用法を指します。プレーンな「通常の」SQLクエリもビューと見なされますか?または、EF Coreがリクエストを何らかのビューでラップしていて、これがこのエラーの原因となっている本当の問題ですか?

よくわかりませんが、これまでのところ、私が見つけたすべての「解決策」は一種の「ハッキー」のようです。考え?

3
Tobias Würth

少し単純化しましょう。これが私がテストのために思いついたものです。また、 EFクエリから生成されたSQLを出力するためのコード を追加しました。

_class Program
{
    static void Main(string[] args)
    {
        DbClient context = new DbClient();

        var rawSql = "select [Id], [Title] from Post order by [Title]";

        var query = context.Posts.AsNoTracking()
            .FromSql(rawSql)
            .Skip(1)
            .Take(4)
            .OrderBy(x => x.Title);

        var generated = query.ToSql();

        var results = query.ToList();
    }
}

class DbClient : DbContext
{
    public DbSet<Post> Posts { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("conn_string");
    }
}

class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public override string ToString() => $"{Id} | {Title}";
}
_

generatedの値を見ると、queryのSQLが何であるかがわかります。

_SELECT [t].[Id], [t].[Title]
FROM (
    SELECT [p].[Id], [p].[Title]
    FROM (
        select [Id], [Title] from Post order by [Title]
    ) AS [p]
    ORDER BY (SELECT 1)
    OFFSET 1 ROWS FETCH NEXT 4 ROWS ONLY
) AS [t]
ORDER BY [t].[Title]
_

3つの_order by_句があり、最も内側の句はrawSqlからのものであることに注意してください。

エラーメッセージを見て、それが合法でない理由を確認できます。

[...]サブクエリ[...]では、OFFSET [...]も指定されていない限り、ORDERBY句は無効です。

doesによる中間の順序にはオフセットが含まれているため、サブクエリ内にある場合でも有効です。

これを修正するには、rawSqlから順序を削除し、OrderBy()linqメソッドを使い続けるだけです。

_var rawSql = "select [Id], [Title] from Post";

var query = context.Posts.AsNoTracking()
    .FromSql(rawSql)
    .Skip(1)
    .Take(4)
    .OrderBy(x => x.Title);
_

これにより、以下が生成されます。

_SELECT [t].[Id], [t].[Title]
FROM (
    SELECT [p].[Id], [p].[Title]
    FROM (
        select [Id], [Title] from Post
    ) AS [p]
    ORDER BY (SELECT 1)
    OFFSET 1 ROWS FETCH NEXT 4 ROWS ONLY
) AS [t]
ORDER BY [t].[Title]
_

現在、すべてのorder by句はサブクエリに含まれていないか、オフセット句があります。

3
gunr2171