执行任务时释放数据库对象
Database object is disposed while executing a task
这是我正在努力的流程,
- 拨打网络电话api
- Web api 会马上return OK
- Web api 会在后台做一些工作
这是我到目前为止所取得的成就,
[Route("api/[controller]")]
public class PremController : Controller
{
private readonly myDbContext _context;
public PremController(myDbContext context)
{
_context = context;
}
[HttpGet]
public HttpResponseMessage Get()
{
Task.Factory.StartNew(() => DoWork());
return new HttpResponseMessage(HttpStatusCode.Accepted);
}
private void DoWork()
{
Delay(2000).ContinueWith(_ => GetProducts());
}
private void GetProducts()
{
var productUrls = _context.Products.Select(p => p.Url).ToArrayAsync();
}
static Task Delay(int milliseconds)
{
var tcs = new TaskCompletionSource<object>();
new Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
return tcs.Task;
}
}
但我收到错误消息,指出在新创建的任务完成之前 myDbContext 已被处理掉。我该如何解决这个问题?
这是因为您正在创建一个即发即弃的任务,没有同步上下文。如果您直接等待 DoWork()
,同步上下文将被保留,确保上下文不会被释放。更具体地说,您正在创建的任务在上下文的生命周期之外运行,正如 DI 容器(很可能是请求范围的)所定义的那样。一旦请求完成,上下文就会被释放,从而终止您的任务试图在请求之外完成的工作。
总而言之,由于多种原因,这是一个糟糕的设计。如果您需要做 "background" 工作,那应该卸载到一个完全不同的进程,而不仅仅是一个新线程。在那里运行的代码应该负责维护自己的上下文,不受网络应用程序中发生的事情的影响。 Task.Run
/Task.Factory.StartNew
对于 Web 应用程序来说非常糟糕,因为线程池是有限的,并且从该池启动新线程会降低服务器的总负载能力。
如果您发现自己想要在 Web 应用程序中启动一个新线程,请不要这样做。这几乎是普遍错误的。相反,使用 Hangfire 或类似的后台处理解决方案安排工作。
这是我正在努力的流程,
- 拨打网络电话api
- Web api 会马上return OK
- Web api 会在后台做一些工作
这是我到目前为止所取得的成就,
[Route("api/[controller]")]
public class PremController : Controller
{
private readonly myDbContext _context;
public PremController(myDbContext context)
{
_context = context;
}
[HttpGet]
public HttpResponseMessage Get()
{
Task.Factory.StartNew(() => DoWork());
return new HttpResponseMessage(HttpStatusCode.Accepted);
}
private void DoWork()
{
Delay(2000).ContinueWith(_ => GetProducts());
}
private void GetProducts()
{
var productUrls = _context.Products.Select(p => p.Url).ToArrayAsync();
}
static Task Delay(int milliseconds)
{
var tcs = new TaskCompletionSource<object>();
new Timer(_ => tcs.SetResult(null)).Change(milliseconds, -1);
return tcs.Task;
}
}
但我收到错误消息,指出在新创建的任务完成之前 myDbContext 已被处理掉。我该如何解决这个问题?
这是因为您正在创建一个即发即弃的任务,没有同步上下文。如果您直接等待 DoWork()
,同步上下文将被保留,确保上下文不会被释放。更具体地说,您正在创建的任务在上下文的生命周期之外运行,正如 DI 容器(很可能是请求范围的)所定义的那样。一旦请求完成,上下文就会被释放,从而终止您的任务试图在请求之外完成的工作。
总而言之,由于多种原因,这是一个糟糕的设计。如果您需要做 "background" 工作,那应该卸载到一个完全不同的进程,而不仅仅是一个新线程。在那里运行的代码应该负责维护自己的上下文,不受网络应用程序中发生的事情的影响。 Task.Run
/Task.Factory.StartNew
对于 Web 应用程序来说非常糟糕,因为线程池是有限的,并且从该池启动新线程会降低服务器的总负载能力。
如果您发现自己想要在 Web 应用程序中启动一个新线程,请不要这样做。这几乎是普遍错误的。相反,使用 Hangfire 或类似的后台处理解决方案安排工作。