1

I have MVC 4.0 application targated at targetFramework="4.5".

I have to basically convert the existing functionality of file processing from synchronous to asynchronous (so that for large file user don't have to wait for other task).

My code is

[HttpPost]
        public async Task<ActionResult> FileUpload(HttpPostedFileBase fileUpload)       
 {

Coreservice objVDS = new Coreservice ();

   //validate the contents of the file 
model =objVDS. ValidateFileContents(fileUpload);

// if file is valid start processing asynchronously
     await Task.Factory.StartNew(() => { objVDS.ProcessValidFile(model); },                                CancellationToken.None,
                    TaskCreationOptions.DenyChildAttach,
                    TaskScheduler.FromCurrentSynchronizationContext());

return view();


}

Basically I want to call a asynchronous method which is in services which does database operations( diffrent project).

I want asynchronous process to have access to the context in services methods. Thats why I am using TaskScheduler.FromCurrentSynchronizationContext() in Task.Factory.StartNew().

The service method is like following in which, based on file type, a second service is getting called for data operations

public async task ProcessValidFile(fileProcessDataModel model)
{
employeeWorkedDataservice service =new employeeWorkedDataservice()

 await Task.Factory.StartNew(() =>
                                        {
                                            service .ProcessEmployeeDataFile(model.DataSetToProcess, OriginalFileName, this, model.Source);
                                        },
                                        CancellationToken.None,
                                        TaskCreationOptions.DenyChildAttach,
                                 TaskScheduler.FromCurrentSynchronizationContext());    

}

ProcessEmployeeDataFile returns void and its not asynchronous method. When the code above is executed it does not return to controller untill it completes the data processing. I think that I am missing something here. Please guide me to solution.

Thanks, Amol

2

2 Answers 2

4

Looks like you've misunderstood how await works.

Read this https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_WhatHappensUnderstandinganAsyncMethod

Setting something running in a task will allow it to run asynchronously so you can do something else while it's running.

When you need the result to continue, you use the await keyword.

By creating your task an immediately awaiting it, you're instantly blocking until the task resolves; making it effectively synchronous.

If you're happy to return to your view without waiting for processing to complete, I don't think you need await at all, since at no point do you want to wait for the result of the operation.

public task ProcessValidFile(fileProcessDataModel model)
{
    employeeWorkedDataservice service =new employeeWorkedDataservice()

    return Task.Factory.StartNew(() =>
    {
        service.ProcessEmployeeDataFile(model.DataSetToProcess, OriginalFileName, this, model.Source);
    },
    CancellationToken.None,
    TaskCreationOptions.DenyChildAttach,
    TaskScheduler.FromCurrentSynchronizationContext());    
}

[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase fileUpload)       
{

    Coreservice objVDS = new Coreservice ();

   //validate the contents of the file 
   model =objVDS. ValidateFileContents(fileUpload);

   // if file is valid start processing asynchronously
   // This returns a task, but if we're not interested in waiting
   // for its results, we can ignore it.
   objVDS.ProcessValidFile(model);

   return view();
}

Regarding your comments:

I would seriously consider not passing your controller to your service, or having your service rely on the session and context since you're tightly coupling your business logic to your API controller.

Get the bits you need from the controller while you're in it and pass them to your service.

Sign up to request clarification or add additional context in comments.

6 Comments

thanks for your answer but even after returning view(); user does not get the view untill file upload is completed at background. Does it has to do with return Task.Factory.StartNew(() => { service.ProcessEmployeeDataFile(model.DataSetToProcess, OriginalFileName, this, model.Source); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.FromCurrentSynchronizationContext()); ?
while debugging i can see hitting return view() by execution; but user is still blocked
ok so i commented out CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.FromCurrentSynchronizationContext() ;and now its showing the page to user ; but now i dont have access to context and session values in new task so its breaking the application. how to persists context and session in this case ?
Added an edit at the bottom of the answer. In short - don't. You're tying your service to your api controller and that's not a good thing.
@J Tolley OK ..i will pass the required values to service. Thanks a ton!!
|
3

I have to basically convert the existing functionality of file processing from synchronous to asynchronous (so that for large file user don't have to wait for other task).

That's not what async does; as I describe on my blog, async does not change the HTTP protocol.

What you want is some form of "fire and forget" on ASP.NET. I have another blog post that covers a few solutions. Note that using Task.Factory.StartNew is the most dangerous of all these solutions.

The best (read: most reliable) solution is to use a proper distributed architecture: your ASP.NET app should create a description of the work to be done and place that in a reliable queue (e.g., MSMQ); then have an independent backend (e.g., Win32 service) that processes the queue. This is complex, but much less error-prone than attempting to force ASP.NET to do something it was never meant to do.

3 Comments

As i cant use context in Task.Factory.StartNew(() i have changed it to Task.Run();
@amol: Task.Run is also dangerous in ASP.NET, and not recommended.
@ Stephen Cleary Thank You !!! I ignored your comments as i had to complete it for demo (it was working for small number of records )..and later on got the thread recycling issue for large files.Now going as per your blog. Thanks once again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.