使用Entity Framework 4.1版本的Code-First,性能非常慢

本文关键字:Code-First 性能 非常 版本 Entity Framework 使用 | 更新日期: 2023-09-27 18:04:56

我们公司正在开发一个新的应用程序,它的核心是一个比较大的业务数据对象。我们决定先用代码尝试实体框架,将数据库从应用程序中抽象出来,但结果出了问题。业务对象由大约60个类和总共大约600个属性组成;然而,它是一个树结构,没有交叉/回溯指针。

我们的测试是向数据库中添加一个未初始化的类实例。使用DbContext。在我的开发机器上添加我们的数据结构花了8分钟。这是这种大小的对象的预期性能吗?是否有导致实体框架性能不佳的常见问题列表?我觉得这件事我需要帮助。

更多的数据点:在业务对象的根下的第一级有27个元素。如果有3个元素(其余的注释掉了),添加的时间是4.5秒。如果有5个元素,则需要11.8秒。有8种元素,1分12.5秒。显然,这些元素的大小变化很大,但这些似乎确实表明了某种系统问题。

使用Entity Framework 4.1版本的Code-First,性能非常慢

…我们的测试是添加一个类的未初始化实例到数据库。使用DbContext.Add…

在调用Add之前,您是否确保创建了代码优先模型并将其加载到内存中?我的意思是:如果您使用像这样的测试代码…

using (var context = new MyContext())
{
    var myHugeBusinessObject = CreateItSomeHow();
    context.HugeBusinessObjects.Add(myHugeBusinessObject);
    context.SaveChanges();
}

…这是您第一次在测试应用程序中使用上下文,Add在开始将对象添加到上下文之前,实际上会花一些时间在内存中构建EF模型。

你可以简单地通过在调用Add之前添加一个虚拟方法来分离这两个步骤,例如:

context.HugeBusinessObjects.Count();

我已经建立了一个测试:

public class MyClass
{
    public int Id { get; set; }
    public string P1 { get; set; }
    // ... P2 to P49
    public string P50 { get; set; }
    public MyClass Child1 { get; set; }
    // ... Child1 to Child26
    public MyClass Child27 { get; set; }
}

我创建了一个对象:

var my = new MyClass();
MyClass child = my;
for (int i = 0; i < 100; i++)
{
    child.Child1 = new MyClass();
    child = child.Child1;
}
child = my;
for (int i = 0; i < 100; i++)
{
    child.Child2 = new MyClass();
    child = child.Child2;
}
// and so on up to Child27

这个对象图有2700个子对象,每个子对象有50个标量属性。然后我测试了这段代码:

using (var context = new MyContext())
{
    var my = CreateWithTheCodeAbove();
    context.MyClassSet.Count();
    context.MyClassSet.Add(my);
    context.SaveChanges();
}

...Count()(=建立EF模型)大约需要25秒Add需要1秒。(在上面的循环中,将100更改为1000(图表中有27000个对象)将Add的时间几乎线性地增加到9-10秒。)此结果与将AutoDetectChangesEnabled设置为truefalse无关。

下一个有趣的结果是:如果我将20个导航属性(Child28Child47)添加到MyClass,那么构建模型(示例代码中的Count())所花费的时间将爆炸到140秒Add的持续时间只随附加属性线性增加。

所以,我的假设是:您实际上并没有测量将业务对象添加到上下文中的时间,而是度量EF在内存中构建EF模型所需的时间。构建模型的时间似乎随着模型的复杂性呈指数级增长:类上的导航属性数量,可能还包括不同涉及类的数量。

要分离这些步骤,请使用上面建议的一些虚拟调用进行测试。如果你已经分开了…天哪,忘了这篇文章吧。

所以,找到了问题所在。在继续使用NHibernate的过程中,我们遇到了一个结构性错误,这是一些实验性代码。类别正在订阅其子类的PropertyChanged事件,这导致NHibernate崩溃。

这很好!这告诉我们确实有问题。实体框架只是永远运行,没有任何迹象表明这是一个问题,我们可以做些什么。

无论如何,我们暂时会继续使用NHibernate。我们喜欢使用它来控制数据库结构