17

下面的executeTime第一次是30秒,下次我执行同一组代码时是25秒。在 SQL Profiler 中观看时,我立即看到一个登录名,然后它就在那里停留了大约 30 秒。然后,只要运行 select 语句,应用程序就会完成 ToList 命令。当我从 Management Studio 运行生成的查询时,数据库查询只需要大约 400 毫秒。它返回 14 行和 350 列。看起来将数据库结果转换为实体所需的时间是如此之小以至于不明显。

那么在进行数据库调用之前的 30 秒内发生了什么?

如果实体框架这么慢,我们是不可能使用它的。有什么我做错了或者我可以改变什么来大大加快速度吗?

更新: 好吧,如果我使用编译查询,第一次需要 30 秒,第二次需要 1/4 秒。我能做些什么来加快第一次通话的速度吗?

using (EntitiesContext context = new EntitiesContext()) 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    var groupQuery = (from g in context.Groups.Include("DealContract") 
                    .Include("DealContract.Contracts") 
                    .Include("DealContract.Contracts.AdvertiserAccountType1") 
                    .Include("DealContract.Contracts.ContractItemDetails") 
                    .Include("DealContract.Contracts.Brands") 
                    .Include("DealContract.Contracts.Agencies") 
                    .Include("DealContract.Contracts.AdvertiserAccountType2") 
                    .Include("DealContract.Contracts.ContractProductLinks.Products") 
                    .Include("DealContract.Contracts.ContractPersonnelLinks") 
                    .Include("DealContract.Contracts.ContractSpotOrderTypes") 
                    .Include("DealContract.Contracts.Advertisers") 
                where g.GroupKey == 6 
                select g).OfType<Deal>(); 
    sw.Stop(); 
    var queryTime = sw.Elapsed; 
    sw.Reset(); 
    sw.Start(); 
    var groups = groupQuery.ToList(); 
    sw.Stop(); 
    var executeTime = sw.Elapsed; 
} 
4

4 回答 4

15

我遇到了同样的问题,我的查询需要 40 秒。

我发现问题出在.Include("table_name")功能上。我拥有的越多,情况就越糟。相反,我将我的代码更改为在查询后立即延迟加载我需要的所有数据,这将总时间从 40 秒减少到大约 1.5 秒。据我所知,这完成了完全相同的事情。

因此,对于您的代码,它将是这样的:

var groupQuery = (from g in context.Groups
            where g.GroupKey == 6 
            select g).OfType<Deal>(); 

var groups = groupQuery.ToList();

foreach (var g in groups)
{
    // Assuming Dealcontract is an Object, not a Collection of Objects
    g.DealContractReference.Load();
    if (g.DealContract != null)
    {
        foreach (var d in g.DealContract)
        {
            // If the Reference is to a collection, you can just to a Straight ".Load"
            //  if it is an object, you call ".Load" on the refence instead like with "g.DealContractReference" above
            d.Contracts.Load();
            foreach (var c in d.Contracts)
            {
                c.AdvertiserAccountType1Reference.Load();
                // etc....
            }
        }
    }
}

顺便说一句,如果您要在当前代码的查询上方添加这行代码,它会将时间缩短到大约 4-5 秒(在我的选项中仍然太长)据我了解,该MergeOption.NoTracking选项禁用了很多更新和将内容插入数据库的跟踪开销:

context.groups.MergeOption = MergeOption.NoTracking;
于 2009-03-28T23:11:04.187 回答
4

这是因为包含。我的猜测是您急于将大量对象加载到内存中。构建与您的数据库实体相对应的 c# 对象需要很长时间。

我对您的建议是尝试仅延迟加载您需要的数据。

于 2009-03-26T17:16:44.977 回答
2

据我所知,使查询的初始编译更快的唯一方法是使查询不那么复杂。有关实体框架编译查询的性能注意事项的 MSDN 文档并未表明有任何方法可以保存已编译查询以在不同的应用程序执行会话中使用。

我要补充一点,我们发现拥有大量包含可以使查询执行比拥有更少的包含和稍后在相关实体上执行更多加载更慢。需要一些试验和错误才能找到正确的介质。

但是,我不得不问你是否真的需要你在这里包含的每个实体的每个属性。在我看来,这个查询中有大量不同的实体类型,因此实现它们可能会非常昂贵。如果您只是想获得您不打算更新的表格结果,那么出于各种原因,将您实际需要的(相对)较少数量的字段投影到平面匿名类型中应该会明显更快。此外,这使您不必担心急切加载、调用 Load/IsLoaded 等。

您当然可以通过预编译实体视图来加速初始视图的生成。MSDN 上有这方面的文档。但是由于您在执行第一个查询时支付了该费用,因此您对简单查询的测试表明,这对您来说运行时间约为 2 秒。说 2 秒很好,但它不会保存任何其他内容。

于 2009-03-26T20:57:12.590 回答
0

EF 需要一段时间才能启动。它需要从 xml 构建元数据,并可能生成用于映射的对象。所以启动需要几秒钟,我认为没有办法解决这个问题,除非永远不要重新启动你的程序。

于 2009-03-26T18:55:56.830 回答