首页 > 解决方案 > 将子级添加到列表中的实体框架性能问题

问题描述

我正在开发一个使用实体框架 6.1.3 的项目。现在,在将子对象添加到父实体列表时,我们遇到了相当大的性能问题(请参见下面的代码示例)。

我们正在使用延迟加载,所以我注意到在我们调用之前一切正常,_parent.Children.Add(child);因为它似乎从数据库中加载所有子节点只是为了能够添加一个新的子节点。由于我们的一些父对象有大约 50,000 个子对象,这会延迟这个简单的插入调用 7-8 秒,有时甚至会导致超时。

对我来说,Entity Framework 加载所有子级只是为了添加一个并没有什么意义,所以有没有办法可以避免这种情况,或者这是一个 Entity Framework 设计缺陷,我们应该找到解决方法吗?

我显然想为此找到一个解决方案,并且不希望不必为这个问题实现纯 ADO 查询。

谢谢!

public class Parent 
{
    public Guid Id { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public Guid Id { get; set; }
}

public class ParentAggregate
{
    private readonly Parent _state;

    public ParentAggregate(Parent state)
    {
        _state = state;
    }

    public void AddChild(Guid id)
    {
        var child = new Child { Id = id };
        _state.Children.Add(child);
    }
}

标签: c#listperformanceentity-frameworkadd

解决方案


对我来说,实体框架加载所有孩子只是为了添加一个真的没有意义

延迟加载发生在您第一次通过其getter访问导航属性时。以及示例代码

_parent.Children.Add(child);

由两个操作组成:

(1)检索Children属性(通过属性getter!):

var children = _parent.Children;

(2)对其进行一些操作(Add本例中调用方法):

children.Add(child);

延迟加载是由于操作 (1) 而发生的。如您所见,EF 与此无关,因为它无法控制它。并且没有办法知道您将如何处理该属性值 - 枚举它,进行计数或使用AddRemove方法。

这里有一些解决方案。

首先,为什么要使用延迟加载?它有很多副作用和低效率,所有这些都可以通过 EF 提供的开箱即用的急切加载Include方法轻松解决。这就是为什么默认情况下 EF Core(“EF 的未来”)默认情况下不使用延迟加载,并且需要一个特殊的包和过程来启用它。

其次,如果你坚持使用延迟加载,那么你有以下两种选择:

(A) 在数据修改期间禁用延迟加载(需要访问/控制DbContext实例):

dbContext.Configuration.LazyLoadingEnabled = false;
_parent.Children.Add(child);
dbContext.Configuration.LazyLoadingEnabled = true;

这也需要初始化集合属性以避免 NRE。

(B) 使用显式支持字段并提供对其的直接访问(以避免触发属性访问器的延迟加载)。例如:

public class Parent 
{
    public Guid Id { get; set; }

    private ICollection<Child> children;
    public virtual ICollection<Child> Children { get => children; set => children = value; }

    public void Add(Child child)
    {
        // use the backing field directly
        if (children == null) children = new HashSet<Child>();
        children.Add(child); 
    }
}

推荐阅读