是什么导致.Attach()在EF4中变慢?

时间:2011-05-06 22:04:50

标签: c# entity-framework-4

我们的代码中有一个通用的更新方法,用于执行

foreach (var entity in entityList)
{
    Context.GetIDbSet<T>().Attach(entity);
    Context.SetState(entity, EntityState.Modified);
}

我通过传入一个实体的枚举并按每个实体调用一次来测试它。

我发现,1000个实体的枚举大约需要47秒才能运行。 这是预期的行为吗?或者代码片段有问题吗?

分析显示Attach()方法比SetState()方法慢。

我运行的测试是在具有50个属性的实体上,如果有任何影响则没有关系。

1 个答案:

答案 0 :(得分:68)

我可以确认这种缓慢的行为,我也找到了主要原因。我用以下模型做了一点测试......

public class MyClass
{
    public int Id { get; set; }
    public string P1 { get; set; }
    // ... properties P2 to P49, all of type string
    public string P50 { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<MyClass> MyClassSet { get; set; }
}

......和这个测试程序......

using (var context = new MyContext())
{
    var list = new List<MyClass>();
    for (int i = 0; i < 1000; i++)
    {
        var m = new MyClass()
        {
            Id = i+1,
            P1 = "Some text ....................................",
            // ... initialize P2 to P49, all with the same text
            P50 = "Some text ...................................."
        }
        list.Add(m);
    }

    Stopwatch watch = new Stopwatch();
    watch.Start();
    foreach (var entity in list)
    {
        context.Set<MyClass>().Attach(entity);
        context.Entry(entity).State = System.Data.EntityState.Modified;
    }
    watch.Stop();
    long time = watch.ElapsedMilliseconds;
}

测试1

正好是上面的代码:

- &GT; 时间= 29,2秒


测试2

注释掉这一行...

//context.Entry(entity).State = System.Data.EntityState.Modified;

- &GT; 时间= 15,3秒


测试3

注释掉这一行...

//context.Set<MyClass>().Attach(entity);

- &GT; 时间= 57,3秒

这个结果非常奇怪,因为我期望调用Attach不是必需的,因为无论如何都要改变状态。


测试4

删除属性P6到P50(因此我们在实体中只有5个字符串),原始代码:

- &GT; 时间= 3,4秒

所以,是的,很明显,物业的数量非常重要。


测试5

在循环之前添加以下行(再次使用所有50个属性建立模型):

context.Configuration.AutoDetectChangesEnabled = false;

- &GT; 时间= 1,4秒


测试6

再次使用AutoDetectChangesEnabled = false,但只有5个属性:

- &GT; 时间= 1,3秒

所以,没有变化跟踪,属性的数量就不再重要了。


<强>结论

到目前为止,大部分时间似乎用于通过更改跟踪机制获取附加对象属性的快照。如果您不需要它,请禁用代码段的更改跟踪。 (我猜您的代码中确实不需要更改跟踪,因为通过将权限的状态设置为Modified,您基本上将所有属性标记为已更改。所以所有列将在更新语句中发送到数据库。)

修改

上述测试时间处于调试模式。但释放模式没有太大的区别(例如:测试1 = 28,7秒,测试5 = 0,9秒)。