如何解决Entity Framework中缓慢加入的查询问题

时间:2013-06-07 17:15:33

标签: c# .net sql entity-framework ef-code-first

我有一个使用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),但它没有效果。导航属性是延迟加载的。

我在考虑什么

  1. 我遇到的一个选择是手写一个查询我需要的数据的SQL视图,并在Code First中将实体映射到它。不确定如何完全这样做,但我希望通过这样做我可以避免生成的查询中的不良性能。

  2. 修改数据库的设计,以便我需要的信息缓存在Top表中。我不喜欢这个选项中的重复数据,但至少我不必遍历这么多的导航属性。

  3. 任何指针?

1 个答案:

答案 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);
        }
    }            
}