实体框架绩效问题将孩子添加到列表中



我正在研究一个我们使用实体框架6.1.3的项目。目前,在将子对象添加到父实体列表中时,我们正在遇到非常大的性能问题(请参见下面的代码示例)。

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

对我来说,实体框架为了添加一个孩子来说,加载所有孩子并没有任何意义,所以有什么方法可以避免这种情况,或者这是实体框架设计 - 如果我们找到解决方法?

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

对我来说

懒惰加载首次通过您的 Access 通过其 getter 进行导航属性。和示例代码

_parent.Children.Add(child);

由两个操作组成:

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

var children = _parent.Children;

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

children.Add(child);

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

这是一些解决方案。

首先,为什么要完全使用懒惰加载?它具有许多副作用和效率低下的效果,并且可以通过Include方法急切地提供EF来轻松解决它们。这就是为什么默认情况下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); 
    }
}

最新更新