【Entity Framework Core】レコード追加と性能比較

【Entity Framework Core】レコード追加と性能比較

Entity Framework Core によるレコード追加方法と性能比較結果を記載します。

レコードの追加(AddおよびAddRange)

AddメソッドおよびAddRangeメソッドでレコード追加する例を記載します。


Addメソッド

Addメソッドで単一エンティティを設定して、SaveChangeメソッドを実行します。
するとデータベースにレコードを追加するSQLが発行されます。

コード例
using (var context = new MyDBContext())
{
    var product = new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 };

    context.Products.Add(product);
    context.SaveChanges();
}

発行されるSQL
Addメソッドで設定した単一エンティティ登録するINSERT文が発行されます。

[Parameters=[@p0='1', @p1='10', @p2='Name1' (Nullable = false) (Size = 50), @p3='1000'],
CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Products] ([Id], [CategoryId], [Name], [Price])
VALUES (@p0, @p1, @p2, @p3);


次はAddメソッドで複数の単一エンティティを設定して、SaveChangeメソッドを実行します。

コード例
using (var context = new MyDBContext())
{
    var product = new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 };
    var product = new Product { Id = 2, Name = "Name2", Price = 2000, CategoryId = 20 };
    var product = new Product { Id = 3, Name = "Name3", Price = 3000, CategoryId = 30 };

    context.Products.Add(product);
    context.SaveChanges();
}

発行されるSQL
Addメソッドで設定した複数の単一エンティティを一度に登録するINSERT文が発行されます。
[Parameters=[@p0='1', @p1='10', @p2='Name1' (Nullable = false) (Size = 50), @p3='1000', 
             @p4='2', @p5='20', @p6='Name2' (Nullable = false) (Size = 50), @p7='2000', 
             @p8='3', @p9='30', @p10='Name3' (Nullable = false) (Size = 50), @p11='3000'], 
CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Products] ([Id], [CategoryId], [Name], [Price])
VALUES (@p0, @p1, @p2, @p3),
(@p4, @p5, @p6, @p7),
(@p8, @p9, @p10, @p11);

AddRangeメソッド

AddRangeメソッドで複数エンティティを設定して、SaveChangeメソッドを実行します。
するとデータベースにレコードを追加するSQLが発行されます。

コード例
using (var context = new MyDBContext())
{
    var products = new List<Product>();
    products.Add(new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 });
    products.Add(new Product { Id = 2, Name = "Name2", Price = 2000, CategoryId = 20 });
    products.Add(new Product { Id = 3, Name = "Name3", Price = 3000, CategoryId = 30 });

    context.Products.AddRange(products);
    context.SaveChanges();
}

発行されるSQL
AddRangeメソッドで設定した複数エンティティを一度に登録するINSERT文が発行されます。
するとデータベースにレコードを追加するSQLが発行されます。

[Parameters=[@p0='1', @p1='10', @p2='Name1' (Nullable = false) (Size = 50), @p3='1000',
             @p4='2', @p5='20', @p6='Name2' (Nullable = false) (Size = 50), @p7='2000', 
             @p8='3', @p9='30', @p10='Name3' (Nullable = false) (Size = 50), @p11='3000'], 
CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Products] ([Id], [CategoryId], [Name], [Price])
VALUES (@p0, @p1, @p2, @p3),
(@p4, @p5, @p6, @p7),
(@p8, @p9, @p10, @p11);

アタッチによるレコード追加(AttachおよびAttachRange)

AttachメソッドおよびAttachRangeメソッドでエンティティをアタッチしてレコード追加する例を記載します。


Attachメソッド

Attachメソッドで単一エンティティをアタッチしたあとに、EntityState.Addedで追加状態にして、SaveChangeメソッドを実行します。
するとデータベースにレコードを追加するSQLが発行されます。

コード例
using (var context = new MyDBContext())
{
    var product = new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 };

    context.Products.Attach(product);
    context.Entry(product).State = EntityState.Added;
    context.SaveChanges();
}

発行されるSQL
Attachメソッドで設定した単一エンティティ登録するINSERT文が発行されます。

[Parameters=[@p0='1', @p1='10', @p2='Name1' (Nullable = false) (Size = 50), @p3='1000'],
CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Products] ([Id], [CategoryId], [Name], [Price])
VALUES (@p0, @p1, @p2, @p3);

AttachRangeメソッド

AttachRangeメソッドで複数エンティティをアタッチしたあとに、EntityState.Addedで追加状態にして、SaveChangeメソッドを実行します。
するとデータベースにレコードを追加するSQLが発行されます。

コード
using (var context = new MyDBContext())
{
    var products = new List<Product>();
    products.Add(new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 });
    products.Add(new Product { Id = 2, Name = "Name2", Price = 2000, CategoryId = 20 });
    products.Add(new Product { Id = 3, Name = "Name3", Price = 3000, CategoryId = 30 });

    context.Products.AttachRange(products);
    foreach (var product in products)
    {
        context.Entry(product).State = EntityState.Added;
    }
    context.SaveChanges();
}

発行SQL
AttachRangeメソッドで設定した複数エンティティを一度に登録するINSERT文が発行されます。

[Parameters=[@p0='1', @p1='10', @p2='Name1' (Nullable = false) (Size = 50), @p3='1000',
             @p4='2', @p5='20', @p6='Name2' (Nullable = false) (Size = 50), @p7='2000',
             @p8='3', @p9='30', @p10='Name3' (Nullable = false) (Size = 50), @p11='3000'], 
CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Products] ([Id], [CategoryId], [Name], [Price])
VALUES (@p0, @p1, @p2, @p3),
(@p4, @p5, @p6, @p7),
(@p8, @p9, @p10, @p11);

BulkInsert

BulkInsertメソッドで複数エンティティを一括挿入することでレコードを追加します。SaveChangeメソッドは不要です。

コード例
using EFCore.BulkExtensions;

using (var context = new MyDBContext())
{
    var products = new List<Product>();
    products.Add(new Product { Id = 1, Name = "Name1", Price = 1000, CategoryId = 10 });
    products.Add(new Product { Id = 2, Name = "Name2", Price = 2000, CategoryId = 20 });
    products.Add(new Product { Id = 3, Name = "Name3", Price = 3000, CategoryId = 30 });

    context.BulkInsert(products);
}

パッケージのインストール
BulkInsertメソッドを使用するにはEFCore.BulkExtensionsパッケージのインストールが必要です。
メニューから[ツール]>[Nugetパッケージマネージャー]>[パッケージマネージャーコンソール]を開き以下のコマンドを実行します。

Install-Package EFCore.BulkExtensions

性能比較

レコード追加方法ごとに性能測定した結果を記載します。
  • 計測環境
  • OS:Windows10(64-bit) RAM:16.0GB CPU:Core i7
    .Net7
    Entity Framework Core Version 7.0.3
    Microsoft SQL Server 2022 Developer Edition (64-bit)

  • 処理内容
  • 100万件のレコードを追加します。

  • 計測結果
  • 5回計測した結果の中央値を記載しています。

計測メソッド 処理時間(秒)
Add 80.132
AddRange 68.281
Attach 82.977
AttachRange 67.674
BulkInsert 12.269

BulkInsertが他のメソッドより圧倒的に早い結果となりました。
AddとAddRangeでは複数エンティティを設定するAddRangeのほうが早い結果となりました。
DBコンテキストにエンティティを設定する際に、Addは1つずつ追加するためAddRangeと比較して遅いと考えられます。SaveChangeのあとにデータベース発行されるINSERT文は、どちらも複数エンティティを一括登録してます。
AttachとAttachRangeも同様に複数エンティティを設定するAttachRangeのほうが早い結果となりました。こちらの結果も同様の理由であると考えれます。


Next Post Previous Post