Entity Framework 性能问题将 child 添加到列表

Entity Framework performance issues adding child to list

我正在开发一个使用 Entity Framework 6.1.3 的项目。现在我们在将 child object 添加到 parent 实体列表时遇到了相当大的性能问题(请参见下面的代码示例)。

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

对我来说 Entity Framework 加载所有 children 只是为了添加一个真的没有意义,所以有没有办法可以避免这种情况或者这是 Entity Framework design-flaw 我们应该找到解决方法吗?

我显然想为此找到一个解决方案,并且宁愿不必为这个问题实现纯 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);
    }
}

To me it doesn't really make sense for Entity Framework to load all children just in order to add one

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

_parent.Children.Add(child);

包含两个操作:

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

var children = _parent.Children;

(2) 对其执行一些操作(在这种情况下调用 Add 方法):

children.Add(child);

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

这里有一些解决方案。

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

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

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

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