我使用的是Entity Framework 4.2,我遇到了相当严重的性能问题。我正在使用POCO方法,继承自DbContext,这是一个解释问题的小样本:
我有一个包含2个表的数据库 - A和B:
A中有一行(1,'Test'),B有6000行(SomeValue只是0到5999之间的数字) - 所有这些都通过外键列引用A行。
我从数据库创建一个edmx并关闭代码生成。然后我创建以下类:
public class DatabaseContext : DbContext
{
public DatabaseContext(string name) : base(name)
{
Configuration.AutoDetectChangesEnabled = false;
As = Set<A>();
Bs = Set<B>();
}
public DbSet<A> As { get; private set; }
public DbSet<B> Bs { get; private set; }
}
public class A
{
public virtual int AId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<B> Bs { get; private set; }
public void AddB(B b)
{
if (b == null)
{
throw new ArgumentNullException("b");
}
if (Bs == null)
{
Bs = new List<B>();
}
if (!Bs.Contains(b))
{
Bs.Add(b);
}
b.A = this;
}
}
public class B
{
public virtual int BId { get; set; }
public virtual A A { get; set; }
public virtual int SomeValue { get; set; }
}
现在我只需执行以下操作:
var ctx = new DatabaseContext("ScalabilityTestEntities");
var a = ctx.As.FirstOrDefault();
a.Bs.Add(new B { SomeValue = 987 });
最后一行(我添加一个新的B)在我的四核,4gb RAM 64位Windows 7机器上占用6秒的区域,该机器在本地运行数据库。
真正糟糕的是它似乎会以指数方式降低某些因素,因为如果你将B中的行数加倍,则需要接近20秒!
我真的很感激有任何提示可以让这种情况更快发生。非常感谢!
答案 0 :(得分:4)
导航属性的世界可能是痛苦的。我们基本上不得不逐步停止使用它们,因为它们背后会导致很多性能问题(特别是当你进入附加和分离实体时,但这是一个不同的故事)。
当您访问a.Bs时,它会加载该A的所有B。
在这种特殊情况下,如果你实际上并不需要B的完整列表而你只想添加一个新的B,那么最好简单地创建一个B并将其AId设置为一个ID。
答案 1 :(得分:3)
在这种情况下,您应该禁用延迟加载:
var ctx = new DatabaseContext("ScalabilityTestEntities");
ctx.Configuration.LazyLoadingEnabled = false;
var a = ctx.As.FirstOrDefault();
a.Bs = new List<B>();
a.Bs.Add(new B { SomeValue = 987 });
默认情况下启用延迟加载(导致访问集合时会加载所有6000个B),因为导航集合声明为virtual
。如果您从不需要或想要使用延迟加载,则应完全删除virtual
关键字或在上下文构造函数中禁用延迟加载。