以下のグラフに示す結果を一貫して取得しています。予測可能性を高めるために何かできることはないかと考えています。EFv5の検証機能を利用する必要があるため、SqlBulkCopyは使用していません。
誰かが私の調査結果を検証/反論できれば幸いです. 私の目的は、両方の種類のスパイクを取り除くことです。すぐに使えるように、以下にソース コードを提供します。必要なのは VS のクラス ライブラリ プロジェクトだけで、EFv5 と NUnit への参照があり、どちらも NuGet から入手できます。このコードを Class1 に貼り付け、接続文字列を変更して実行するだけです。以下の SQL スクリプトを使用して、テーブルを再作成できます。
私は.Net 4.5、EF 5、NUnit 2.6.1を使用しており、デバッガーを接続せずにリリースモードでコードを実行しています。データベースは SqlServer 2008 R2 です。NUnit.exe を 64 ビット モードで使用してテストを実行すると、フレームワーク バージョンとして「Net 4.0」が表示されます。
X 軸はバッチ番号 (合計 1000 バッチ) で、Y 軸はミリ秒です。最初のバッチに約 30 秒かかることがわかります。これは、dbContext が「コールド」であるためです。各バッチは 100 個のエンティティを保存します。
この質問は、この回答に欠けている情報を少し探していることに注意してください。これは、EF 保存のジッターです。
私が使用しているコードは次のとおりです。
テーブル:
CREATE TABLE [dbo].[Entity1](
[Id] [int] IDENTITY(1,1) NOT NULL,
[IntField] [int] NOT NULL,
[StrField] [nvarchar](50) NOT NULL,
[DateField] [datetimeoffset](7) NOT NULL,
CONSTRAINT [PK_Entity1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
クラス:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using NUnit.Framework;
namespace ClassLibrary1
{
public class Entity1
{
public int Id { get; protected set; }
public int IntField { get; set; }
public string StrField { get; set; }
public DateTimeOffset DateField { get; set; }
}
public class MyContext : DbContext
{
public MyContext(string connStr) : base(connStr) { }
public virtual DbSet<Entity1> Entities { get { return base.Set<Entity1>(); } }
}
[TestFixture]
public class Class1
{
[Test]
public void EfPerf()
{
var entities = buildEntities(100000);
int batchSize = 100;
var batchTimes = new List<Stopwatch>();
for (int i = 0; i < entities.Length; i += batchSize)
{
var sw = Stopwatch.StartNew();
using (var ctx = buildCtx())
{
for (int j = i; j < i + batchSize; j++)
ctx.Entities.Add(entities[j]);
ctx.SaveChanges();
}
sw.Stop();
batchTimes.Add(sw);
}
batchTimes.ForEach(sw => Console.Out.WriteLine("Elapsed ms: " +
sw.ElapsedMilliseconds));
}
private MyContext buildCtx()
{
var cs = "Data Source=your db server;" +
"Initial Catalog=your db;" +
"Persist Security Info=True;" +
"User ID=your user;" +
"Password=your pwd";
var ctx = new MyContext(cs);
//ctx.Configuration.ProxyCreationEnabled = false;
return ctx;
}
private Entity1[] buildEntities(int count)
{
var entities = new Entity1[count];
for (int i = 0; i < count; i++)
entities[i] = new Entity1 { IntField = i, StrField = "str" + i,
DateField = DateTimeOffset.UtcNow };
return entities;
}
}
}