如何使用per-DbContext状态正确实现DbCommandInterceptor?

时间:2015-08-11 20:16:51

标签: c# entity-framework logging entity-framework-6 interceptor

我正在关注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

我考虑过通过DbContextThreadStaticAttribute使用线程本地存储。但是,我不确定这是否足够。假设我的应用程序使用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状态?

0 个答案:

没有答案