来,给Entity Framework热热身

先来看一下Entity Framework缓慢的初始化速度给我们更新程序带来的一种痛苦。

我们手动更新程序时通常的操作步骤如下:

1)把Web服务器从负载均衡中摘下来

2)更新程序

3)预热(发出一个请求,完成程序的初始化)

4)把完成更新的Web服务器挂上负载均衡

在预热阶段,我们一般是向首页(www.cnblogs.com)发出请求(首页的加载没有用到Entity Framework)。

如果仅这样预热后就将Web服务器上线,将会给部分用户带来糟糕的用户体验——比如,第1位在发布后推荐博文的用户将会等待7秒钟左右(推荐功能中使用了Entity Framework Code First)。

为了避免这样的糟糕体验,我们不得不在预热时发出推荐博文的请求,等EF完成初始化后再发布。这不是一个好的解决方法,因为每个定义的DbContext类型都要进行这个初始化操作。

昨天,我们找到了一个更好的缓解这个问题的方法,在这篇博文中向大家分享一下。

为什么Entity Framework的初始化速度慢如蜗牛呢?

对于在应用程序中定义的每个DbContext类型,在首次使用时,Entity Framework都会根据数据库中的信息在内存生成一个映射视图(mapping views),而这个操作非常耗时。

using (var dbcontext = new CnblogsDbContext())
{
    //...
}

比如上面的代码,在第1次调用CnblogsDbContext进行数据库操作时会进行缓慢的mapping views生成操作,后续的CnblogsDbContext操作会共享已经生成的mapping views,不受这个问题影响。但是要注意的是你定义的每一个DbContext都会面临这个问题。

而我们的缓解之道则是在应用程序初始化时一次性触发所有的DbContext进行mapping views的生成操作——调用StorageMappingItemCollection的GenerateViews()方法。

代码如下(Entity Framework的版本至少是6.0才支持):

using (var dbcontext = new CnblogsDbContext())
{
    var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext;
    var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
    mappingCollection.GenerateViews(new List<EdmSchemaError>());
}

//对程序中定义的所有DbContext逐一进行这个操作

对于ASP.NET应用程序 ,可以将上面的代码放在Application_Start或者PreApplicationStartMethod中执行。

我们采用这个方法之后,在程序更新时只需发一个请求让程序启动起来,比如访问首页(www.cnblogs.com),就可以直接发布。而第1位进行推荐博文操作的用户,等待时间由原来7秒减少到0.5-0.6秒。

【参考资料】

Pre-Generated Mapping Views 

posted @ 2014-03-28 11:37 dudu 阅读(...) 评论(...) 编辑 收藏