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 与此无关,因为它无法控制它。并且没有办法知道您将如何处理该 属性 值 - 枚举它,进行计数或使用 Add
、Remove
等方法。
这里有一些解决方案。
首先,为什么要使用延迟加载?它有很多副作用和效率低下,所有这些都可以通过 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);
}
}
我正在开发一个使用 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 与此无关,因为它无法控制它。并且没有办法知道您将如何处理该 属性 值 - 枚举它,进行计数或使用 Add
、Remove
等方法。
这里有一些解决方案。
首先,为什么要使用延迟加载?它有很多副作用和效率低下,所有这些都可以通过 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);
}
}