我有一个使用Code First和Entity Framework 6的MVC 4项目(由SQL Server 2008支持)。我正在尝试优化一个特别讨厌的查询,看起来像:
var tops = Context.Top
.Include(t =>
t.Foo
.Select(f => f.FooChild1
.Select(c => c.Baz)))
.Include(t =>
t.Foo
.Select(f => f.FooChild3))
.Include(t =>
t.Foo
.Select(f => f.FooChild2))
.Include(t =>
t.Foo
.Select(f => f.FooChild1
.Select(c => c.Bar)))
.Where(t => t.Foo.Count > 0)
.ToList();
关系如下:
Top
1 ----> 0..N Foo
1 ----> 0..N FooChild1
1 ----> 0..N Bar
1 ----> 0..N Baz
1 ----> 0..N FooChild2
0..N ----> 1 FooChild3
正如您所看到的,查询执行了大量的热切加载,因此生成的查询有很多连接。事实证明,延迟加载对于我对结果数据所做的事情来说太慢了。
生成的查询在我的SQL Server上执行大约需要2秒钟,但手写查询获取我需要的数据只需要大约91毫秒。我能做些什么来改善这个吗?
我尝试了什么
我尝试通过在我需要的所有其他表上调用Load()
来预加载并删除所有Include
。我不确定为什么(也许这个技巧不适用于DbContext
),但它没有效果。导航属性是延迟加载的。
我在考虑什么
我遇到的一个选择是手写一个查询我需要的数据的SQL视图,并在Code First中将实体映射到它。不确定如何完全这样做,但我希望通过这样做我可以避免生成的查询中的不良性能。
修改数据库的设计,以便我需要的信息缓存在Top
表中。我不喜欢这个选项中的重复数据,但至少我不必遍历这么多的导航属性。
任何指针?
答案 0 :(得分:0)
您可以使用预编译查询来提高案例的效果。 类似的事情(来自msdn的例子,因为我不知道你的类型):
static readonly Func<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>> s_compiledQuery2 =
CompiledQuery.Compile<AdventureWorksEntities, Decimal, IQueryable<SalesOrderHeader>>(
(ctx, total) => from order in ctx.SalesOrderHeaders
where order.TotalDue >= total
select order);
static void CompiledQuery2()
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
Decimal totalDue = 200.00M;
IQueryable<SalesOrderHeader> orders = s_compiledQuery2.Invoke(context, totalDue);
foreach (SalesOrderHeader order in orders)
{
Console.WriteLine("ID: {0} Order date: {1} Total due: {2}",
order.SalesOrderID,
order.OrderDate,
order.TotalDue);
}
}
}