71577


### 解決したいこと

C# + Entity Frameworkアプリケーションで`Take()`をかまさない`ToList()`が極端に遅い原因を知りたい。

プログラム作成中にリスト表示箇所が極端に遅かったため、`VisualStudio`のデバッグ機能にて問題個所を調べたところ  
`ToList()`で極端に遅くなっていることに気づきました。

### 該当するソースコード

[code]

    using (var db = new AppDbContext())
    {
        Decimal pKey1 = XXX;
        Decimal pKey2 = XXXXX;
        Decimal pCode = XX;
    
        var r1 = db.V_DATA
            .Where(s => s.Key1 == pKey1)
            .Where(s => s.Key2 == pKey2)
            .Where(s => s.Code <= pCode)
            .ToList();  // ここで7,363ミリ秒(7秒程)かかる
    }
    ============================
    /// Entityクラス
    public partial class V_DATA
    {
        [Key]
        [Column(Order = 0, TypeName = "numeric")]
        public decimal Key1 { get; set; }
    
        [Key]
        [Column(Order = 1, TypeName = "numeric")]
        public decimal Key2 { get; set; }
    
        [Key]
        [Column(Order = 2, TypeName = "numeric")]
        public decimal Key3 { get; set; }
    
        [Key]
        [Column(Order = 3)]
        public string Column4 { get; set; } // 最大
    
        [Key]
        [Column(Order =4 , TypeName = "numeric")]
        public decimal Column5 { get; set; }
    
        [Key]
        [Column(Order = 5, TypeName = "numeric")]
        public decimal Column6 { get; set; }
    }
    
    
[/code]

`ToList()`で取得される件数は`100件`。試しに`ToList()`直前に生成されたSQLを`SSMS`から実行しましたが  
ほぼ0秒で結果が返ってきたためクエリチューニングの問題ではないように思えました。

[code]

    var r1 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode)
        .Selct(s => s); // デバッグモードで停止してSql文字列を取得
    
[/code]

## 質問

以下の点についてご教示をお願い致します。

  * `Take()`無しで速度を向上する方法を知りたい。
  * `ToList()`のみで極端に遅くなる理由を知りたい。

なお、`V_DATA`は`SQLServer`データベースにおいて`View`として定義されています。

### 検証したこと１（Take(100).ToList()）⇒少し早くなる(及第点)

[code]

    var r1 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode)
        .Take(100)  // ToList()で取得される件数と同数の100を指定
        .ToList();  // 早くなった。⇒1,112ミリ秒(1秒程)
    
    
[/code]

### 検証したこと２（ToArray）⇒遅い

[code]

    var r1 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode)
        .ToArray();  // ToArray化。同様に遅い。⇒7,576ミリ秒(7秒程)
    
    
[/code]

### 検証したこと３（Any）⇒早い

[code]

    var r1 = db.V_DATA
        .Where(s => s.Key1 == pKey1)
        .Where(s => s.Key2 == pKey2)
        .Where(s => s.Code <= pCode)
        .Any();  // Any()化。早い。⇒49ミリ秒(7秒程)
    
[/code]

### 計測

別の質問サイトで指摘いただき計測しましたので、そちらのコードも載せます。

[code]

       var startTime = DateTime.Now;
        var r0 = db.V_DATA
            .Where(s => s.Key1 == pKey1)
            .Where(s => s.Key2 == pKey2)
            .Where(s => s.Code <= pCode);
        var endTime = DateTime.Now;
        Console.WriteLine ($"Where ×3 に要した時間 {endTime - startTime}");
        startTime = DateTime.Now;
        var r1 = r0.ToList (); 
        endTime = DateTime.Now;
        Console.WriteLine ($"ToList に要した時間 {endTime - startTime}");
    
[/code]

  1. 結果↓

[code]

    Where ×3 に要した時間 00:00:00.0021529
    ToList に要した時間 00:00:06.9929449
    
[/code]

  2. `Where().Take(100).ToList()とつなげた場合

[code]

    Where ×1 に要した時間 00:00:00
    Take(100) に要した時間 00:00:00.0032364
    ToList に要した時間 00:00:00.8432526
    
[/code]

`ToList`がはやくなります。。

### その他

`V_DATA`ビューは概ね以下の定義になっています。  
Column4の最大桁数は`11桁`、その他数値項目は`12桁`です。

[code]

    [dbo].[V_DATA](
        [Key1] [numeric](6, 0) NOT NULL,
        [Key2] [numeric](7, 0) NOT NULL,
        [Key3] [numeric](3, 0) NOT NULL,
        [Column4] [nvarchar](max) NOT NULL,
        [Column5] [numeric](22, 0) NOT NULL,
        [Column6] [numeric](22, 0) NOT NULL,
    )
    
[/code]

### 利用バージョンなど

  * Windows 10
  * Windows Forms アプリケーション
  * .NET Framework 4.8
  * Entity Framework 6.2.0
  * SQL Server 2014

困っています。ご回答お待ちしております。

## 追記 2020/10/29 16:00

# SQL直接実行で早くなった

`db.Database.Log =
Console.Write;`にて生成されたSQLを取得し、`SqlQuery`で実行したところ、想定外に応答が早く(`170ミリ秒`)なりました。原因不明です。

[code]

    db.Database.SqlQuery<List<V_DATA>>(/* ここに生成されたSQL文字列 */).ToList()
    
[/code]


CC BY-SA 4.0
2020-10-29T00:03:06.413
71568
2020-10-29T08:45:32.743
2020-10-29T07:02:01.930
42517
42517
question
c# entity-framework
EntityFramework(C#)のToList()が極端に遅くなる原因を知りたい


[データベース操作のログ記録と受信](https://docs.microsoft.com/ja-
jp/ef/ef6/fundamentals/logging-and-interception)でどのようなクエリが投げられているかを確認してみてください。

> なお、`V_DATA`はSQLServerデータベースにおいてViewとして定義されています。

Viewは単なるクエリです。参照の度にクエリが展開されテーブルから読み込み直します。

SQL Serverにはインデックス付きのビューという機能もありますが、Enterprise Editionでしか結果が使われません。（[クエリ
オプティマイザーによる自動的なインデックス付きのビュー使用](https://docs.microsoft.com/ja-jp/previous-
versions/sql/2014/getting-started/features-supported-by-the-editions-of-sql-
server-2014?view=sql-server-2014#rdbms-manageability)）

* * *

> SQL直接実行で早くなった
[code]

    /// Entityクラス
    public partial class V_DATA
    {
        [Key]
        [Column(Order = 0, TypeName = "numeric")]
        public decimal Key1 { get; set; }
    
[/code]

Entityクラスのすべてのメンバーに`[Key]`が付けられています。これは適切でしょうか？ 主キーでないものには付けるべきではありません。

* * *

また、[追跡なしのクエリ](https://docs.microsoft.com/ja-jp/ef/ef6/querying/no-
tracking)として[`.AsNoTracking()`](https://docs.microsoft.com/ja-
jp/dotnet/api/system.data.entity.queryableextensions.asnotracking?view=entity-
framework-6.2.0)を追加することができます。これを行うとオブジェクト状態が追跡されなくなるためオブジェクト生成が高速化されます。ただし、オブジェクトの値を更新後にDBへ保存できなくなります。


CC BY-SA 4.0
2020-10-29T03:29:35.577
71577
2020-10-29T08:45:32.743
2020-10-29T08:45:32.743
4236
4236
71568
answer