I'm using Simple Injector as the IoC container for my .Net MVC project. Here is how I register the services.
SimpleInjectorInitializer.cs
public static void Initialize() {
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
//container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle(); // replace last line with this for async/await
InitializeContainer(container);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
private static void InitializeContainer(Container container) {
container.Register<MyDbContext>(Lifestyle.Scoped);
container.Register(typeof(IUnitOfWork<>), typeof(UnitOfWork<>), Lifestyle.Scoped);
container.Register(typeof(IRepository<>), typeof(Repository<>), Lifestyle.Scoped);
container.Register<ICustomerService, CustomerService>(Lifestyle.Scoped);
//what does this do by the way?
//using (container.BeginExecutionContextScope()) {
//}
}
CustomerController
public interface ICustomerService : IService<Customer> {}
public class CustomerService : BaseService<Customer, MyDbContext>, ICustomerService {
public CustomerService(IUnitOfWork<MyDbContext> unitOfWork) : base(unitOfWork) {}
// do stuff
}
public class CustomerController : Controller {
private readonly ICustomerService _service;
public CustomerController(ICustomerService service) {
_service = service;
}
public ActionResult Index() {
var foo = _service.GetById(112); // works
// do stuff
return View();
}
public async Task<int> Foo() { // error out on calling this method
var foo = await _service.GetByIdAsync(112);
return foo.SomeId;
}
}
My problem is that whenever I used async/await, the ioc failed. Then I looked its documentation, it got a different LifeStyle
for Asynchronous methods. So I changed the DefaultScopeLifeStyle
to ExecutionContextScopeLifestyle()
, it errored out
The ICustomerService is registered as 'Execution Context Scope' lifestyle, but the instance is requested outside the context of a Execution Context Scope.
Do I need to implement a hybrid lifestyle for using asyn/await as well as synchronous methods? Or something is wrong in my design?
Error detail (with WebRequestLifestyle
)
The asynchronous action method 'foo' returns a Task, which cannot be executed synchronously.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The asynchronous action method 'foo' returns a Task, which cannot be executed synchronously.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[InvalidOperationException: The asynchronous action method 'foo' returns a Task, which cannot be executed synchronously.] System.Web.Mvc.Async.TaskAsyncActionDescriptor.Execute(ControllerContext controllerContext, IDictionary
2 parameters) +119 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary
2 parameters) +27 System.Web.Mvc.<>c__DisplayClass15.b__12() +56 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func1 continuation) +256 System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +22 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList
1 filters, ActionDescriptor actionDescriptor, IDictionary2 parameters) +190 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +522 NotFoundMvc.ActionInvokerWrapper.InvokeActionWith404Catch(ControllerContext controllerContext, String actionName) +32 NotFoundMvc.ActionInvokerWrapper.InvokeAction(ControllerContext controllerContext, String actionName) +16 System.Web.Mvc.<>c__DisplayClass22.<BeginExecuteCore>b__1e() +23 System.Web.Mvc.Async.AsyncResultWrapper.<.cctor>b__0(IAsyncResult asyncResult, Action action) +15 System.Web.Mvc.Async.WrappedAsyncResult
2.CallEndDelegate(IAsyncResult asyncResult) +16 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +49 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36 System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12 System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +22 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +49 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10 System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21 System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +29 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9765121 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
Edit I've confirmed it's not a Simple Injector issue, it's really this. I've tried to clean the solution, remove the dlls in the bin folder, still no luck with same error. However, I changed the controller to an ApiController, the asyn worked well.
As far as I can see, this problem is not related to Simple Injector and its scoping; if you wrap an execution context scope around a web request (which is something you can do by hooking into the request_start and request_end events), you will be faced by the same problem.
There are several issues about this on Stackoverflow and the rest of the interwebs, that you should take a look at, such as this q/a.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With