C# 异步Web请求、EntityFramework和DI,它是如何工作的?

C# 异步Web请求、EntityFramework和DI,它是如何工作的?,c#,asp.net,multithreading,entity-framework,asynchronous,C#,Asp.net,Multithreading,Entity Framework,Asynchronous,我搜索了很多关于这些主题的内容,但我仍然不确定它是否如预期的那样有效。为什么 我的理解是: 处理web请求时,您正在使用其池中的一个IIS线程。在该请求中,如果使用异步调用(例如,查询数据库中的某些数据),则释放该线程,以便IIS可以使用该线程处理另一个调用。那很好。可能吧 稍后,当数据库最终给出等待的数据时,代码将恢复。异步文档提到您现在可能在另一个线程中。或者不是。DotNet的决定。如果它和以前在同一个线程中,那没关系 我使用依赖项注入在PerRequest生存期内(使用Microsof

我搜索了很多关于这些主题的内容,但我仍然不确定它是否如预期的那样有效。为什么

我的理解是:

  • 处理web请求时,您正在使用其池中的一个IIS线程。在该请求中,如果使用异步调用(例如,查询数据库中的某些数据),则释放该线程,以便IIS可以使用该线程处理另一个调用。那很好。可能吧
  • 稍后,当数据库最终给出等待的数据时,代码将恢复。异步文档提到您现在可能在另一个线程中。或者不是。DotNet的决定。如果它和以前在同一个线程中,那没关系
  • 我使用依赖项注入在PerRequest生存期内(使用Microsoft Unity)注入和关闭上下文。结案是我在这里最关心的问题。它在我的同步世界中工作得非常好:dbcontext在我的web请求结束时关闭
  • 已知EntityFramework的DbContext不是线程安全的
问题1:现在,如果恢复代码位于另一个线程中,那么它是来自IIS必须处理所有请求的同一线程池,还是来自另一个线程池

问题2:如果代码在另一个线程中运行,那么WebRequest上下文呢?在异步代码真正结束之前,DI是否会正确跟踪延迟调用的结束,而不是调用Dispose()

问题3:如果我使用EntityFramework的异步方法,比如ToListSync或FirstOrDefaultAsync,我到处都读到“应该没问题”。有人能详细说明一下吗?EF是否专门跟踪web请求或初始线程?是否有某种形式的捕获发生?我的dbcontext是否会与重用初始线程的另一个web请求混淆

问题4:如果我使用EntityFramework的普通(同步)方法,但包装在任务中。会发生什么?还是“应该没事”吗


对不起,问题太多了,这已经困扰我很长时间了。

好了,我现在可以试着回答我自己的问题了

问题1: 从

是-所有线程都来自线程池。您的MVC应用程序已经是多线程的,当请求传入时,将从池中获取一个新线程并用于服务该请求。该线程将被“锁定”(来自其他请求),直到请求得到完全服务并完成。如果池中没有可用的线程,则请求将不得不等待,直到有可用的线程。 如果您有异步控制器,它们仍然从池中获取线程,但在服务请求时,它们可以放弃线程,在等待某些事情发生时(该线程可以被提供给另一个请求),当原始请求再次需要线程时,它从池中获取线程

问题2:如果代码在另一个线程中运行,那么WebRequest上下文呢?

来自ewk和 及

SynchronizationContext将确保在恢复执行时在当前线程上正确设置Web请求(HttpContext.Current)。这是ASP.NET后台工作的一部分

在异步代码真正结束之前,DI是否会正确跟踪延迟调用的结束而不是调用Dispose()?

由于Web请求可能在多个线程上流动,并且不会以初始线程结束,因此我看不出有任何理由在整个流程完成之前触发EndRequest事件。因此,我再也看不出为什么PerRequestLifeTimeManager或另一个PerRequest策略会在需要之前调用一些Dispose()而失败


问题3~4:异步和EF。正如ewk提到的,实体框架是安全的,只要您不在不同的线程中同时使用它。问题3和问题4都显示了访问DbContext的不同方式,答案是:为了安全起见,无论以何种方式运行任务,都应该只有一个对DbContext的同时访问。正如ewk提到的,使用相同的dbcontext明确地旋转两个任务是不安全的。

您是在谈论asp.net核心还是asp.net的旧版本?旧版本。假设我们在版本4.7中。它真的会有区别吗?它太复杂(太长)了,无法正确回答这个问题。但主要的一点是,即使请求在线程之间切换,asp.net(从asp.net 4.5开始)将能够跟踪(通过其SynchronizationContext),并且每个线程都将具有正确的http上下文,该上下文将从一个线程流向另一个线程。线程切换后,请求不会结束。至于EF和线程安全-如果您一次从一个线程使用它(即使有多个线程,但在任何给定的时间只有一个线程使用它),您就可以了;var t2=Task.Run(()=>再次使用上下文);等待任务。当所有(t1,t2)时,您可能会遇到麻烦,因为您可能同时从多个线程访问上下文。所以您需要知道的是,请求和线程是不同的。一个请求可能由不同的线程(至少在asp.net的非客户端版本中)处理。您的回答改变了一切。使用这个新的SynchronizedContext关键字,我能够更好地了解整个情况。现在我觉得自己很愚蠢。所以我要试着回答我自己的问题。