我正在关注Tom Dykstra的Getting Started with Entity Framework 6 Code First using MVC 5教程。本教程的Part 4涵盖了EF6的连接弹性和命令拦截功能。
作为演示命令拦截使用的一种方式,本教程提供了两个示例DbCommandInterceptor
实现。一个(SchoolInterceptorLogging
)与记录器连接,记录每个执行的SQL命令,执行所花费的时间以及是否存在异常。另一个示例(SchoolInterceptorTransientErrors
)模拟瞬态DB错误,以便可以测试连接弹性。
虽然本教程没有特别提及同步问题,但我认为因为DbCommandInterceptor
的单个实例是通过DbInterception.Add()
注册的,所以每个{ - 3}}状态都被使用必须同步DbCommandInterceptor
。例如,SchoolInterceptorLogging
可能应该同步使用_stopwatch
,而SchoolInterceptorTransientErrors
可能应该同步_counter
。
我考虑过通过DbContext
或ThreadStaticAttribute
使用线程本地存储。但是,我不确定这是否足够。假设我的应用程序使用ThreadLocal<T>
异步保存所有更改。同一个线程是否可以处理两个异步保存操作?如果是这样,那么线程本地存储将无法工作。
另一个想法是DbContext.SaveChangesAsync()
。但是,使用CallContext
时,建议仅使用不可变类型。 (参见Stephen Cleary的博客文章store per-DbContext
state in the call context。)
第三个想法是使用Implicit Async Context ("AsyncLocal"),其中键是传递给NonQueryExecuting()/ NonQueryExecuted(),ReaderExecuting()/ ReaderExecuted()或ScalarExecuting()/ ScalarExecuted的ConcurrentDictionary
对象(),值是状态对象。但是,我对这种方法有两个问题:
传递给DbCommandInterceptionContext
/ *Executing()
方法的*Executed()
对象是否与每个保存操作不同?
通过在ReaderExecuting()
中设置断点并在拦截上下文对象上调用GetHashCode()
,对于每个重试,它们似乎是不同的,更不用说保存操作了(测试过的EntityFramework版本6.1) 0.3)。
是否始终会调用*Executed()
方法?
这是为了确保ConcurrentDictionary
方法中添加到*Executing()
的所有内容始终可以通过*Executed()
方法进行清理。
那么,DbCommandInterceptor
如何实现每个DbContext
状态?