1

I need to run a heavy code parallel to other code so that user don't have to wait .. I am trying Task.Run(() => to accomplish my task. My task is calling method from service using dependency injection.
I am getting following error:

{"Resolution of the dependency failed, type = \"Company.CacheProvider.CacheEmployeeProvider\", name = \"(none)\".\r\nException occurred while: while resolving.\r\nException is: InvalidOperationException - The PerRequestLifetimeManager can only be used in the context of an HTTP request. Possible causes for this error are using the lifetime manager on a non-ASP.NET application, or using it in a thread that is not associated with the appropriate synchronization context.\r\n-----------------------------------------------\r\nAt the time of the exception, the container was:\r\n\r\n Resolving Company.Provider.CacheProvider.CacheEmployeeProvider,(none)\r\n Resolving parameter \"cache\" of constructor Company.Provider.CacheProvider.CacheEmployeeProvider(Company.Provider.Interface.IEmployeeProvider provider, Company.Provider.Interface.ICache cache)\r\n Resolving Company.Provider.CacheProvider.RequestCache,(none) (mapped from Company.Provider.Interface.ICache, (none))\r\n"}

Error is providing the possible reason .. but I am not able to find the solution ..

Entire application is using PerRequestLifetimeManager .. how can I use it in may task .. or how can I make my task run in different context without any issue ..

UPDATE : - Registration

 var container = new UnityContainer();   

 container.RegisterType<IEmployeeProvider>(new InjectionFactory(unity => unity.Resolve<CacheEmployeeProvider>(new DependencyOverride(typeof(IEmployeeProvider), unity.Resolve<DbEmployeeProvider>()))));       
 container.RegisterType<IEmployeeService, EmployeeService>();

 DependencyResolver.SetResolver(new UnityDependencyResolver(container));
 DynamicModuleUtility.RegisterModule(typeof(Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule));

Here is the base code of EmployeeController

Dependency

[Microsoft.Practices.Unity.Dependency]
public Lazy<IEmployeeService> EmployeeService { get; set; }

Post Request

[HttpPost]
public ActionResult AddEmployee(EmployeeModel model)
{
   var employee = EmployeeService.Value.Add(model);

   Task.Run(() => SetupEmployeeArea(employee.Id)); //task to setup employee related data

   return RedirectToAction("EmployeeList"); //redirect user to the list of employees
}

Task

async Task SetupEmployeeArea(int id)
{
   EmployeeService.Value.EmployeeSetup(id); //this call firing exception
}

I also tried following , but this code is not executing completely .. while debugging I lost the pointer in middle and nothing happening.

async Task SetupEmployeeArea(int id)
{
   var employeeService = DependencyResolver.Current.GetService<IEmployeeService>();
   employeeService.EmployeeSetup(id); //not getting any exception and code is also not running
}

Please share suggestions ... or if possible please also share best alternates to fulfill my requirement (run heavy code parallel) except "Hangfire" .. I already have too much load on that :)

NMathur
  • 829
  • 1
  • 17
  • 35

1 Answers1

2

The problem is that the http context closes before the task is completed. Try to make the controller's method async and run the task like:

await Task.Run(() => SetupEmployeeArea(employee.Id)); //task to setup employee related data

Then the task finishes before the http context is closed.

If you would like to return a result before the task is finished consider using some background worker for this like QueueBackgroundWorkItem https://blogs.msdn.microsoft.com/webdev/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net/ or some external framework like Hangfire and register your dependency to be resolved outside the http context.

If there is no need for some services to be resolved inside the http context change it scope to another e.g. transient or singleton https://msdn.microsoft.com/en-us/library/ff647854.aspx. You can always create custom lifetime manager that handles cases when the http context is closed like this: https://stackoverflow.com/a/4698138/9502162.

Another approach is to load the page and then use ajax call to perform this task so then users will see the page immediately. But with this approach you make client app responsible for setting employee related data which may be unwanted.

arekzyla
  • 2,878
  • 11
  • 19
  • can you please share some useful links? – NMathur Mar 16 '18 at 09:53
  • 1
    Background worker (no Hangfire): https://blogs.msdn.microsoft.com/webdev/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net/ Async controllers: https://msdn.microsoft.com/library/ee728598%28v=vs.100%29.aspx Ajax requests: https://stackoverflow.com/questions/16186083/making-a-simple-ajax-call-to-controller-in-asp-net-mvc – arekzyla Mar 16 '18 at 09:58
  • To perform an ajax call I have to pass all the required parameter to the redirected view ... I have millions of users .. what can be the best way to pass parameters without clashing the users data – NMathur Mar 16 '18 at 10:00
  • You only need employeeId for SetupEmployeeArea. I've added suggested solution to the answer – arekzyla Mar 16 '18 at 12:06
  • no that was just an example .. I need to pass a model – NMathur Mar 16 '18 at 12:29
  • but as you suggested .. I am trying to prepare sample using background worker – NMathur Mar 16 '18 at 12:30
  • so have you tried changing scope of those services or implementing custom lifetime manager? I edited the answer and this may help – arekzyla Mar 16 '18 at 12:33
  • thanks arekzyla .. trying to implement .. will update you ASAP. – NMathur Mar 17 '18 at 08:50
  • Hello arekzyla . .thank you so much for your time and help .. I personally liked background worker solution, and will explore on my own . but for now the team has decided to setup a new hangfire server for this particular task. .. I will implement your solutions and will update all on this thread. – NMathur Mar 19 '18 at 06:46
  • 1
    I'm glad I could help – arekzyla Mar 19 '18 at 13:03