I have MVC web app that uses EntityFramework context and it stores it in HttpContext.Current.Items
. When HttpContext.Current isn't available then it uses CallContext.SetData
to store data in current thread storage. HttpContext is used for web app itself and CallContext
is used in unit tests to store the same EF DbContext there.
We are also trying to use async\await as we have library that relays a lot on them, and it works great in web app. But it fails in unit tests as CallContext.SetData
isn't restored after thread returns to await block.
Here is simplified sample of the issue:
public async Task Test()
{
ContextUtils.DbContext = new SomeDbContext();
using (ContextUtils.DbContext){
await DoSomeActions();
}
}
public async Task DoSomeActions(){
var data = await new HttpClient().GetAsync(somePage);
// on next line code would fail as ContextUtils.DbContext is null
// as it wasn't synced to new thread that took it
var dbData = ContextUtils.DbContext.SomeTable.First(...);
}
So in that example ContextUtils.DbContext basically sets HttpContext\CallContext.SetData. And it works fine for web app, and fails in unit test as SetData isn't shared and on ContextUtils.DbContext.SomeTable.First(...);
line DbContext is null.
I know that we can use CallContext.LogicalSetData\LogicalGetData
and it would be shared withing ExecutionContext, but it requires item to be Serializable and i don't want to mark DbContext with serialization attribute as would try to serialize it.
I also saw Stephen's AsyncEx library (https://github.com/StephenCleary/AsyncEx) that has own SynchronizationContext, but it would require me to update my code and use AsyncContext.Run
instead of Task.Run
, and i'm trying to avoid code updating just for unit tests.
Is there any way to fix it without changing the code itself just to make it work for unit tests? And where EF DbContext should be stored in unit tests without passing it as parameter and to be able to use async\await?
Thanks