我正在研究一个我们使用实体框架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与此无关,因为它无法控制它。而且没有办法知道您将如何处理该属性值 - 枚举它,进行计数或使用Add
,Remove
等。方法。
这是一些解决方案。
首先,为什么要完全使用懒惰加载?它具有许多副作用和效率低下的效果,并且可以通过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);
}
}