【问题标题】:How to work around slow joining queries in Entity Framework如何解决实体框架中连接缓慢的查询
【发布时间】:2013-06-07 17:15:33
【问题描述】:

我有一个 MVC 4 项目,它使用 Code First 和 Entity Framework 6(由 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 表中。我不喜欢这个选项中的数据重复,但至少我不必遍历这么多导航属性。

任何指针?

【问题讨论】:

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


    【解决方案1】:

    在您的情况下,您可以使用预编译查询来提高性能。 类似的东西(来自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);
            }
        }            
    }
    

    【讨论】:

    • 感谢您的回复。我不确定编译的查询会对我有帮助......也许你可以详细说明?我的理解是,如果您重复进行相同的查询,编译查询会很有帮助。我的问题是生成的查询效率低下(比手动创建的查询慢 20 倍),而且我觉得与执行查询所需的 2 秒相比,动态查询编译时间可以忽略不计SQL Server。
    • 你分析了吗?您是否将 ef 生成的查询与您的手工查询进行了比较?如果您在比较中显示它们,我可以更准确地帮助您。
    猜你喜欢
    • 2017-05-08
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-25
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多