我们的代码中有一个通用的更新方法,用于执行
foreach (var entity in entityList)
{
Context.GetIDbSet<T>().Attach(entity);
Context.SetState(entity, EntityState.Modified);
}
我通过传入一个实体的枚举并按每个实体调用一次来测试它。
我发现,1000个实体的枚举大约需要47秒才能运行。 这是预期的行为吗?或者代码片段有问题吗?
分析显示Attach()方法比SetState()方法慢。
我运行的测试是在具有50个属性的实体上,如果有任何影响则没有关系。
答案 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秒)。