2014-02-07 165 views
23

有一个已经窃听我关于实体框架很长一段时间的事情。实体框架,DBContext和使用()+异步?

去年我写了使用EF一个客户端一个大的应用程序。在开发过程中,一切都很顺利。

我们运系统在8月。但几周后,我开始在生产服务器上看到奇怪的内存泄漏。运行几天后(8 GB),我的ASP.NET MVC 4进程占用了机器的所有资源。这并不好。我在网上搜索,发现你应该将所有的EF查询和操作都包含在using()块中,以便处理上下文。

一天我重构我的代码使用using(),这解决了我的问题,因为那么过程坐落在一个稳定的内存使用情况。

然而,我并没有将我的查询放在首位,原因是我从Visual Studio自带的脚手架开始了我的第一个控制器和存储库,这些脚手架并没有围绕它的查询,而是使用了DbContext作为控制器本身的一个实例变量。

首先的:如果处置方面的真正重要的(东西不会是奇怪,在dbconnection需要关闭等),微软或许应该有这种在其所有的例子!

现在,我已经开始研究一个新的大型项目,并将我的所有知识放在脑后,我一直在尝试.NET 4.5和EF 6 asyncawait的新功能。 EF 6.0具有所有这些异步方法(例如SaveChangesAsync,ToListAsync等)。

public Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

TblLanguageRepo类:

public async Task<tblLanguage> Add(OrganizationTypeEnum requestOrganizationTypeEnum, tblLanguage language) 
{ 
    ... 
    await Context.SaveChangesAsync(); 
    return langaugeDb; 
} 

然而,当我现在环绕我的语句在using()块我得到的异常,DbContext was disposed,查询已经能够返回之前。这是预期的行为。查询运行异步,并且using块在查询之前完成。但是,如何在使用ef 6的异步和等待函数时以适当的方式处理我的上下文?

请指点我正确的方向。

需要在EF 6 using()?为什么微软自己的例子从来没有这样做?你如何正确使用异步功能并处理你的上下文?

+0

这是密切相关的:[EF数据上下文 - 异步/等待和多线程](http://stackoverflow.com/questions/20946677/ef-data-context-async-await-multithreading) – Noseratio

回答

5

我会去的方式“每个请求一个的DbContext”,并请求中重用的DbContext。由于所有任务都应在请求结束时完成,您可以再次安全地处理它。

见即:One DbContext per request in ASP.NET MVC (without IOC container)

其他一些优点:

  • 一些实体可能已经在从的DbContext以前 物化查询,节省了一些额外的查询。
  • 你没有所有那些额外的using陈述混乱你的代码。
1

agree with @Dirk Boer管理DbContext生命周期的最佳方法是在http请求完成时处理上下文的IoC容器。但是,如果这是不是一种选择,你也可以做这样的事情:

var dbContext = new MyDbContext(); 
var results = await dbContext.Set<MyEntity>.ToArrayAsync(); 
dbContext.Dispose(); 

using声明是在代码块结束对象的处置只是语法糖。只需简单地自己拨打.Dispose即可在没有using区块的情况下达到相同的效果。

试想想起来了,你不应该对象设置例外,如果你使用了利用块内的await关键字:

public async Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     var returnValue = langRepo.Add(RequestOrganizationTypeEnum, language); 
     await langRepo.SaveChangesAsync(); 
     return returnValue; 
    } 
} 
1

如果使用正确的n层编程patters,您的控制器应从来不知道正在制作数据库请求。这应该全部发生在你的服务层。

有几种方法可以做到这一点。一种是为每个类创建2个构造函数,一个创建一个上下文,另一个接受已经存在的上下文。这样,如果您已经在服务层中,则可以传递上下文;如果是调用服务层的控制器/模型,则可以创建一个新的上下文。

另一种是创建每个方法的内部重载并接受那里的上下文。

但是,是的,你应该包装这些使用。

从理论上讲,垃圾收集应该清理它们而不包装它们,但我并不完全相信GC。

0

恕我直言,这又是使用延迟加载造成的问题。在处理上下文之后,不能再延迟加载属性,因为处置上下文会关闭到数据库服务器的基础连接。

如果你确实有懒加载激活和using范围后出现异常,那么请参阅https://stackoverflow.com/a/21406579/870604

24

您的代码:

public Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

返回一个Task前处置库。如果您的代码async

public async Task<tblLanguage> Post(tblLanguage language) 
{ 
    using (var langRepo = new TblLanguageRepository(new Entities())) 
    { 
     return await langRepo.Add(RequestOrganizationTypeEnum, language); 
    } 
} 

那么它只会在Task完成之前处置库。实际发生的是当您点击await时,该方法返回的不完整Task(请注意,此时using块仍处于“活动”状态)。然后,当langRepo.Add任务完成时,Post方法恢复执行并处理langRepo。当Post方法完成时,返回的Task完成。请参阅我的async intro

+0

@Steven,是'添加'等待?我以为它只是返回一个TEntity(假设langRepo是一个DbSet)... – danludwig

+6

@danludwig:在这种情况下'添加'在op的问题中给出。它是可以等待的(并且应该真的叫做'AddAsync')。 –