WCF RIA Services查询速度慢

时间:2011-09-19 20:16:59

标签: silverlight entity-framework wcf-ria-services

我有一个由Entity Framework支持的WCF RIA Services域服务。实体模型非常复杂。通常,大多数操作的性能都可以。

然而我的Silverlight客户端包含主/详细信息视图。主视图是实体列表。在实体之间单击以选择它们会在服务器上触发 IQueryable GetEntity()方法,这平均花费5秒 - 这是一段不可接受的时间。

我使用Fiddler通过线路查看呼叫,我可以看到我所有的时间都花在了服务器上:

ServerGotRequest:   15:59:44.545
ServerBeginResponse:15:59:48.836
ServerDoneResponse: 15:59:48.836
ClientBeginResponse:15:59:48.836
ClientDoneResponse: 15:59:48.836
Overall Elapsed:    00:00:04.2940000

返回的数据,因为它出现在电线上,也很小。在这个例子中,大约6kb。

好的 - 所以我的问题是服务器端。对EF的查询相当简单,但有一点需要注意:我们有~25 .Include语句来引入相关实体。 .Includes选择最多4级深度的实体(例如.Include(“1.2.3.4”))。

所以我的下一个想法是DB很慢。不是这样 - 我运行SQL事件探查器,看到(肯定是可怕的)SQL执行平均为0.15秒。返回的数据并不差 - 大约275列的3行。

所以:

  • 我的瓶颈在服务器上
  • 我通过直接EF查询选择多个相关实体
  • 生成的SQL很难看,但速度够快

那我为什么慢?我该怎么调试呢?

如果我在 IQueryable GetEntity()方法的末尾设置了一个断点,那么在退出该方法后,似乎需要最多3秒才能在SQL事件探查器中显示实际查询。 WTF?

请注意,我已预先生成了我的实体框架视图,并且我尝试使用编译查询来排除EF'预热'时间。没有区别。

感谢您对此问题的任何帮助。提前谢谢。

4 个答案:

答案 0 :(得分:3)

我理解响应告诉我批量处理数据,不加载整个实体等;总的来说,我同意这种方法。但是,在这种情况下,我们选择单个实体 - 因为它来自规范化数据库,涉及跨多个表连接。

在这个确切的例子中,EF生成的可怕查询仍然在0.2秒内执行。如果我关心手写查询,它可能会再次占用1/10。事实上,它肯定足够快。线上整个实体的大小<6Kb;再次,我认为它足够小。

因此,对于我来说,在多个请求中对这个单个实体进行批处理,在这个例子中根本不起作用。显然,如果我使用直接的ADO.NET和Web服务/ WCF,我就不会遇到这个问题。无论如何,回答:

我已经说过编译EF查询没有帮助。但是,似乎这可能是因为RIA Services在我的原始查询之上应用了“Where EntityID = ID”条件,并将我编译的查询吹走了。

如果我只是在我的域名服务中执行此操作:

(from e in ctx.Entities
.Include("SubEntity")
.Include("SubEntity.SubEntity")
// and so on, X20...
where e.EntityID == id
select e).FirstOrDefault();

这行代码几乎花费了整个时间来执行,但SQL非常快。到达SQL Server的速度很慢。所以这对我来说意味着EF需要很长时间来生成查询。

为了解决这个问题,我做了一个预编译的查询,直接通过ID选择我的实体,并实现它而不是返回IQueryable:

 private static Func<DataContext, Guid, Entity> getEntityByEntityID =
        CompiledQuery.Compile<DataContext, Guid, Entity>(
        (ctx, id) => (from e in ctx.Entities
                        .Include("SubEntity")
                        .Include("SubEntity.SubEntity")
                        // and so on, X20...
                      where e.EntityID == id
                      select e).FirstOrDefault());

然后,我在域服务上公开了一个新操作,以使用已编译的查询:

public Entity GetEntityByEntityID(Guid entityID)
    {
        return getEntityByEntityID(this.ObjectContext, entityID);
    }

结果:第一次调用时它现在有点慢。对于后续呼叫,整个服务呼叫的操作现在平均约为0.5秒。

答案 1 :(得分:1)

对于这样的东西,我认为你通常会更好地使用包含而不是。只需一次调用即可在Silverlight中加载主实体,然后根据需要加载其他包含的项目。

即使您最终在视图中需要所有这些内容,您也可以通过将它们放在较小的块中并在获得它们时更新视图来获得更好的性能。与等待查询相比,它肯定会带来更好的用户体验。

答案 2 :(得分:0)

您解释它的方式 - 它是图形问题的序列化/反序列化。此外,我们遇到了一些奇怪的问题,例如当在Analyzer中执行的SQL快速运行时,从EF运行速度很慢。尝试在表格上重建statistcis

答案 3 :(得分:0)

在WCF RIA服务中 - 您必须记住它们是在发出回叫之前首先加载的客户端模型。这个客户端模型不是EF,而只是在服务器端渲染EF的样子。 WCF RIA Services调用首先加载此模型,然后通过参考对基础模型中的实体发出回调。

我建议您在返回到具有“完成工作”功能的UI客户端之前,先查看Rx框架以利用多个异步调用。使用Rx,您可以调用多个异步方法,并在返回UI之前等待所有异步方法完成。每次调用都会以较小的位加载客户端模型,允许在返回之前并行完成工作。请记住,这是一个SOA架构,并且很容易(对自己说)回到阻塞调用的设计中。

(任务并行库(TPL)也是另一种方式,但它正在进入SL5。我不熟悉TPL以及何时使用RX和/或TPL之间的细微差别。)

我已经使用这种策略将数据从我正在返回的数据的层次结构树中拉出来 - 第一个工作是上层,然后是下层,等等。请记住,客户端上的导航属性只不过是linq查询,其中主键用于过滤外键。客户端模型(此时它不是EF模型,但接近它的东西)也有参照完整性约束,但这些仅适用于插入记录而不是加载记录。