我一直在下面的圖表中顯示結果。我想知道是否有任何事情可以提高可預測性。我沒有使用SqlBulkCopy,因爲我需要利用EFv5的驗證功能。EF5爲什麼在SaveChanges()次產生這些尖峯?
如果有人能夠驗證/駁斥我的發現,那將是非常棒的。我的目標是擺脫這兩種尖峯。我在下面提供了源代碼,以便讓您快速找到它。所有你需要的是VS中的類庫項目,引用EFv5和NUnit,都可以通過NuGet獲得。只需將此代碼粘貼到Class1中,修改連接字符串並運行即可。您可以使用下面的sql腳本重新創建表。
我使用.Net 4.5,EF 5,NUnit 2.6.1,在Release模式下運行代碼,不附加任何調試器。數據庫是SqlServer 2008 R2。我以64位模式運行NUnit.exe測試,該模式顯示'Net 4.0'爲框架版本。
X軸是批次號(1000個批次總計),而Y軸是毫秒。你可以看到第一批需要大約30秒,這是因爲dbContext是「冷」的,所以這是預期的。每批次可以節省100個實體。
請注意,此問題正在尋找this answer中缺少的信息,它是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;
}
}
}
你可以通過增加批量大小來誇大問題嗎?這可能會讓你打破調試器。每批次調用Debugger.Break後8s進入批處理,您也可以啓動計時器。 – usr
也運行SQL事件探查器查看SQL級別是否發生了尖峯。查看持續時間欄。 – usr
我故意不附加調試器,所以我可以確保這不是一個因素。另外,我運行了Sql Profiler,所有插入的持續時間爲0,只有幾十個中的一個比'0'cpu多(在這種情況下只有大約'15')。 – esegura