4

We are working on a large-ish MVC web application with multiple backing stores including a SQL Server database accessed via Entity Framework 6.0. We are using asynchronous operations wherever we can, but we repeatedly--in initial implementations--come across scenarios where we inadvertently start a second async operation on the same EF context (we use the same context for all EF operations), which throws an exception.

I am not new to C# enterprise app development, but I am pretty new to EF and I've been thinking about possible ways to communicate that a given repo method must be awaited immediately instead of potentially included in a Task.WhenAll() or awaited after another method. Has anyone considered this?

Some pseudo-code for discussion purposes:

// we know that our CURRENT implementation of ICarRepo will utilize EF
// this declaration is in a backend-agnostic library
public interface ICarRepo
{
  Task<ICar> GetAsync(int carID);
}

// in our EF project
public class CarRepoEF: ICarRepo
{
   public async Task<ICar> GetAsync(int carID)
   {
     return await DataContext.Cars.SingleOrDefaultAsync(c => c.ID == carID);
   }
}

Our current approach is to simply provide comments in scenarios where we can fire off an async repo method and continue before awaiting the result:

// not EF
var driverTask = driverRepo.GetAsync(driverID);
//...other repo methods, logic, object instantiation, etc
// EF, await it immediately
var car = await carRepo.GetAsync(carID);
var driver = await driverTask;

We are using DI everywhere (including the DataContext in a request scope) so if we wanted to communicate this to consumers we'd have to do so through the backend-agnostic interface, which makes me think this is perhaps a fool's errand (why make the repo interface communicate a limitation of the current implementation of the interface?). Specifically, I was considering wrapping Task in a custom class that would communicate that it can't handle more than one at a time (singleton???), or extending Task itself to communicate the same and have interfaces that front EF operations return these instead of a plain Task.

Should I give up on trying to engineer a solution to this quasi-problem? Is there a more fundamental, architectural change that could make this problem go away that I am not aware of?

EDIT

After some more research, I did find a SO question that deals with this issue (even using the same DI framework) and one of the answers introduced the idea of using a factory to create contexts.

  • 2
    if sharing the DataContext means that you can't really claim to be a Task, either don't share the DataContext or don't claim to be a Task – Caleth Oct 7 '15 at 13:11
  • @Caleth that is an excellent, succinct way of putting it and makes me wonder why there aren't more discussions about this issue with EF (or maybe everybody is creating contexts for each async request). – Sven Grosen Oct 7 '15 at 13:36
  • @SvenGrosen There is no need for discussion. Context is not thread-safe and it is stressed enough that it should only ever be accessed from single thread/continuation chain. – Euphoric Oct 7 '15 at 13:40
  • @Euphoric I meant discussion around ways to work around this limitation, which I certainly understand why it is in place. Again, I'm an EF newbie so my whole line of thinking is probably naive. – Sven Grosen Oct 7 '15 at 13:43
  • @SvenGrosen The "workaround" is to use Context in single thread only. EF is not going to change just because your code is massive spaghetti and it is impossible to know from which thread the Context will be called. – Euphoric Oct 7 '15 at 13:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.