7

我一直得到下图中显示的结果。我想知道我是否可以做些什么来提高可预测性。我没有使用SqlBulkCopy,因为我需要利用 EFv5 的验证功能。

如果有人可以验证/反驳我的发现,那就太好了。我的目标是摆脱这两种尖峰。我在下面提供源代码,以便您快速完成。您所需要的只是 VS 中的一个类库项目,其中包含对 EFv5 和 NUnit 的引用,两者都可以通过 NuGet 获得。只需将此代码粘贴到 Class1 中,修改连接字符串,然后运行它。您可以使用下面的 sql 脚本重新创建表。

我正在使用 .Net 4.5、EF 5、NUnit 2.6.1,在发布模式下运行代码,没有附加调试器。数据库是 SqlServer 2008 R2。我在 64 位模式下使用 NUnit.exe 运行测试,其中显示“Net 4.0”作为框架版本。

EFv5 简单实体插入。 1000批100个实体

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;
        }
    }
}
4

1 回答 1

1

我有一种感觉,您在数据库中遇到了死锁问题。EF 将所有 saveChanges() 调用放在一个事务中。默认情况下,SQL Server 中的事务以 Read Committed 的方式运行,这是非常严格的。也许您可以尝试更改隔离级别:

 using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
 2: TransactionOptions { IsolationLevel= IsolationLevel.Snapshot }))
 3: {
 4: // do something with EF here
 5: scope.Complete();
 6: }
于 2013-01-08T17:33:58.707 回答