読者です 読者をやめる 読者になる 読者になる

EntityFramework で安易に呼んではいけないメンバ

EntiryFramework を使い始めて1年が経過しているが、
データ量の増加でやけに動作が遅くなったことがあった。

原因を調べていると何気なくとあるメンバが呼ばれていることがわかった。


結論

ToArray をうかつにやってはいけない。
(当たり前だろって突っ込まれそうな・・)


検証結果

以下に実験した結果を残してみる。


今回使ったモデル

    [Table("SampleTable")]
    public class UserProfile
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }

        [ForeignKey("RelationId")]
        public virtual RelationModel Relation { get; set; }

        [ForeignKey("Relation")]
        public virtual int RelationId { get; set; }
    }

    [Table("RelationModel")]
    public class RelationModel
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
    }


Select

1000件: 71ms
10000件: 860ms

return db.Users.ToArray().Select(q => q.FirstName).ToArray();

1000件: 50ms
10000件: 65ms

return db.Users.Select(q => q.FirstName).ToArray();


OrderBy

1000件: 108ms
10000件: 842ms

return db.Users.ToArray().OrderBy(q => q.FirstName).Select(q => q.FirstName);

1000件: 38ms
10000件: 108ms

return db.Users.OrderBy(q => q.FirstName).Select(q => q.FirstName).ToArray();


Where(自プロパティ)

1000件: 96ms
10000件: 748ms

return db.Users.ToArray().Where(q => q.FirstName.Contains("1")).Select(q => q.FirstName);

1000件: 39ms
10000件: 61ms

return db.Users.Where(q => q.FirstName.Contains("1")).Select(q => q.FirstName).ToArray();


Where(Relation のプロパティ)

1000件: 169ms
10000件: 1536ms

return db.Users.Include("Relation").ToArray().Where(q => q.Relation.FirstName.Contains("1")).Select(q => q.FirstName);

1000件: 37ms
10000件: 70ms

return db.Users.Where(q => q.Relation.FirstName.Contains("1")).Select(q => q.FirstName).ToArray();


まとめ

ここからは予測だが、EntiryFramework を介してのメモリ展開がボトルネックと思われる。
厳密に言えば SQL でのデータ取得量の違いもあるだろうが、メモリ展開に比べれば大したことないはず。

Relation を含めた ToArray が極端に遅くなったのはこのためだろう。

こんなところにつまづく人はそういないだろうが、 ToArray を呼ばざるを得ない状況もあるかもしれないので、
クエリーを考えたDB設計を考えなければならないと思った事例だった。


達人に学ぶDB設計 徹底指南書

達人に学ぶDB設計 徹底指南書