-2

I am developing a process in MVC to take input from a database object and convert the data to an Excel file export. I am using NPOI tool to do this.

The current conversion process is made completely independent with no external dependencies Or Shared objects.

The excel generation works fine and generates the output as required.

The problem arises when the user initiates a second request when the first one is on progress.

When there are more than one requests the process generates the byte[] and returns to the controller to send the file to client side. But on return the file does not download. It only downloads the file related to the last request.

For example:

  • when there are 3 requests initiated one after the other.
  • The processes is carried out for first
  • The processes is carried out for second request
  • The processes is carried out for Third request
  • All requests are carried out simultaneously
  • Finishes Request1, Returns to the controller with Result for Request1
  • Does not download the file for Request1
  • Finishes Request2, Returns to the controller with Result for Request2
  • Does not download the file for Request2
  • Finishes Request3, Returns to the controller with Result for Request3
  • DOWNLOAD's the file for Request3

How can I handle this and makes sure all generated files are downloaded as they are processed.

**Controller**
public ActionResult ExportExcel(ExportCriteriaVM exportCriteriaVM)
{
.....

    var excelByteArray = _translator.CreateExcelExportFile(..., ..., ...);

    return File(excelByteArray, _documentManager.GetMimeType("xlsx"), xlsxName);
}

**Translator**

byte[] IExportTranslator.CreateExcelExportFile(Param1, Param2, Param3)
{
    return doSomeWork(Param1, Param2, Param3);           
}

private byte[] doSomeWork(CalendarSetupVM calendarSetupVM, GanttExportCriteriaVM ganttExportCriteriaVM, string filterName)
{
     using (var ms = new MemoryStream())
     {
     ........
     ........
     return ms.ToArray();
     }
}

How can I ensure the file is downloaded for each request.

11
  • 3
    So why use asynchronous processing at all? await Task.Run(() -> x()) is just an inefficient version of x(). Commented Jul 1 at 2:30
  • 2
    A SemaphoreSlim can prevent multiple threads from starting the same process. But perhaps you'd prefer a queue of some kind? It all depends on why the process breaks when you run it in parallel, and how you will manage the results. Commented Jul 1 at 3:03
  • 1
    Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. Commented Jul 1 at 3:19
  • 1
    Just call doSomeWork() inline and lose all the async stuff. You haven't answered my question. Commented Jul 1 at 4:22
  • 1
    If the processes can run in parallel you don't need to wait. Your question doesn't make sense. Commented Jul 1 at 4:23

2 Answers 2

2

First of all, are you sure there is no thread safe way to generate these excel files?

I'm not familiar with NPOI, but in a well designed library, two separate objects should be usable from two separate threads at the same time. So are you sure you are not sharing some kind of object between requests? If you are using dependency injection you should be able to specify "Transient" or "Scoped" for whatever objects you are using to ensure they are created for each request. Avoid "singleton" unless you know the object is thread safe. If you are using any temporary files you also need to ensure the filenames is unique, and make sure you are cleaning up any garbage files left behind from crashes or uncontrolled shutdowns. You might also want to consider some other library if NPOI does not work well in a web server scenario, see https://softwarerecs.stackexchange.com/ for recommendations.

If you really have to queue tasks you can use a custom task scheduler, like DedicatedThreadTaskScheduler. Such a scheduler needs to be shared by all requests to work, i.e. "Singleton" lifetime. You can use that to start a task like:

var myTask =  Task.Factory.StartNew(
     () =>  doSomeWork(...),
     CancellationToken.None,
     TaskCreationOptions.None,
     myDedicatedThreadTaskScheduler)

But this should really be the last resort, If the export process hangs for some reason your server will be completely unresponsive until the server reboots.

You might also want to run a profiler to check why this process takes so much time, having a user wait for a response without any kind of feedback is poor user experience. In many cases poor performance is due to not understanding how slow something is. Running a profiler helps find these issues, and there are often quite simple fixes.

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

Comments

0

If you are using async methods, there's no need to use Task.Run internally, unless you would like to start own async operation, while code doing it does not support it. But in your case, i am not sure you need it. What was goal to use Task.Run ? If this code runs synchronously, there are still more threads that would take in next request and process it in parallel. So i guess you could drop those asyncs from methods and Task.Run.

Also, when you are using blocking calls such as Wait() or Result on the task, it blocks the current thread and you are risking threadpool exhaustion and potential deadlocks as a consequence.

Now, if you want particular code to be executed exclusively, you need locking mechanism and there are variety of ways.

In your case, you just want one thread executing your code at a time, so i could suggest just using lock: The lock statement - ensure exclusive access to a shared resource.

There are other synchornization methods, for instance semaphores.

So, IExportTranslator.CreateExcelExportFile can be simplified to this:

byte[] IExportTranslator.CreateExcelExportFile(CalSetupVM calSetupVM, ExportCriteriaVM exportCriteriaVM, string name)
{
    lock(@lockObject)
    {
        return doSomeWork(calSetupVM, exportCriteriaVM, name) 
    }
}

C# fiddle presenting lock mechanism

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.