我遇到的问题是,如果不应该将子对象添加到父对象的速度非常慢。有成千上万个子对象(在这种情况下为33k记录),但这些对象都不是父对象的子对象。
当我将第一个孩子添加到父母时,需要一分钟以上才能完成:
public class ParentEntity // POCO generated by EF TT4 templates
{
public virtual int Id { get; set; }
public virtual ICollection<ChildEntity> ChildEntities {}
}
public class ChildEntity // POCO generated by EF TT4 templates
{
public virtual int Id { get; set; }
public virtual int ParentEntityId { get; set; }
public virtual ParentEntity ParentEntity { get; set; }
public virtual Warehouse Warehouse { get; set; }
public virtual WarehouseLocation WarehouseLocation { get; set; }
}
public class Warehouse { // etc } // another POCO class
public class WarehouseLocation { // etc } // another POCO class
// somewhere in a controller action method...
var parent = _parentEntityService.GetBy(id);
var child = new ChildEntity{ ParentEntityId = id,
WarehouseId = id2, WarehouseLocationId = id3 };
// ChildEntities.Add() takes more than one minute to add the
// first and only child to this parent
// why would this be so incredibly slow?
parent.ChildEntities.Add(child);
在EntityFramework中找到速度问题的最佳方法是什么?
更新:EFProf显示它发出三个SQL查询:
SELECT * FROM ChildEntities where ParentId = id
SELECT * FROM ChildEntities where WarehouseId = id2
SELECT * FROM ChildEntities where WarehouseLocation = id3
为什么它会为每个ChildEntity加载这些,当它只应为当前子项加载它们时?
编辑2:根据@LadislavMrnka,额外的查询是由模板的Fixup方法引起的。但是当我注释掉那些方法并注释掉对Fixup的调用时,它仍然很慢。这不是删除修复的正确方法(看起来它已被删除):
public class ChildEntity {
public virtual Warehouse Warehouse
{
get { return _warehouse; }
set
{
if (!ReferenceEquals(_warehouse, value))
{
var previousValue = _warehouse;
_warehouse = value;
//FixupWarehouse(previousValue); // commented out
}
}
}
答案 0 :(得分:5)
这是你的问题:
var child = new ChildEntity
{
Foo = "",
Bar = "",
ParentEntityId = id,
WarehouseId = 1,
WarehouseLocationId = 1
};
parent.ChildEntities.Add(child);
恕我直言,这完全是关于POCO模板生成的代码中隐藏的修复集合。 Fixup +延迟加载=性能问题。 Fixup尝试使模型中的所有内容保持同步。这意味着如果您设置导航属性或FK属性的一侧,它将尝试确保关系另一侧的导航属性也反映了该更改。问题是如果没有加载导航属性,它将触发延迟加载。在您的情况下,看起来设置Warehouse首先修复了ChildEntity
中的导航属性,之后尝试修复Warehouse
实例上的导航属性,但未加载其子实体集合=&gt;延迟加载导致
SELECT * FROM ChildEntities where WarehouseId = some id
WarehouseLocation
的情况也是如此。第一个查询是将父项添加到父实体上的未加载集合的结果。
解决方案是修改模板并删除所有修正(例如,EFv4.1的DbContext POCO模板+不再使用修正)或通过调用以下命令关闭延迟加载:
context.ContextOptions.LazyLoadingEnabled = false;
// Your insert logic here
context.ContextOptions.LazyLoadingEnalbed = true;
您甚至可以将代码包装在自定义IDisposable中,如:
public class DisableLazyLoadingScope : IDisposable
{
private readonly ObjectContext context;
public DisableLazyLoadingScope(ObjectContext context)
{
this.context = context;
context.ContextOptions.LazyLoadingEnabled = false;
}
public void Dispose()
{
context.ContextOptions.LazyLoadingEnabled = true;
}
}
并使用它:
using (new DisableLazyLoadingScope(context)
{
// Your insert logic here
}
答案 1 :(得分:0)
这极大地帮助了我:
context.Configuration.AutoDetectChangesEnabled = false;
我不确定这是否适合你,但如果我正确理解你的问题,当你执行Add
时,你的速度很慢?
这仍然允许您插入对象,但EF在导航对象层次结构时将花费更少的精力。我相信在更新实体时这是有问题的,但我不确定。
第二种方法(假设您使用DbContext
)是:
using (var dbCtx = new MyDataContext())
{
var ctx = ((IObjectContextAdapter)dbCtx).ObjectContext;
var customers = ctx.CreateObjectSet<Customer>();
customers.AddObject(customer);
}
因为ObjectContext
在内部处理与更改有关的事情。我现在找不到这个来源,我会试着稍后再找到它。