0

In my ASP.NET MVC 5 application, I cache the results of long-running database queries using Redis as an LRU cache. Whenever the user requests such a result the application first checks if it is already cached... If it’s found in the cache the application will return the cached result. If not, the application will execute the query and then cache the result.

In a few cases it can happen that the application needs to write quite a lot of cache entries (>10000). Is there a way to immediately respond to the request of the user and write the cache in the background?

I was thinking about writing the cache in a fire and forget like manner making the method writing the cache async void. I do not care about any results of the method, it “doesn’t matter” if it fails or not or if it's completed before the next requests hits the application or not… (The application does not in any way depend on the cache entries being present.)

Is using async void in ASP.NET MVC a good idea or can it cause problems? If not, what would be the correct solution to write the cache without letting the user wait?

tereško
  • 58,060
  • 25
  • 98
  • 150
musium
  • 2,942
  • 3
  • 34
  • 67
  • This may help: http://stackoverflow.com/questions/335276/how-do-i-continue-working-on-the-server-after-returning-a-response-with-asp-net – Joseph Simpson Mar 24 '16 at 18:06
  • Possible duplicate of [Fire and forget async method in asp.net mvc](http://stackoverflow.com/questions/18502745/fire-and-forget-async-method-in-asp-net-mvc) – Jason Watkins Mar 24 '16 at 18:08
  • This may help as well: https://stackoverflow.com/questions/33834540/how-do-i-start-not-awaited-background-tasks-in-an-async-method/33835718#33835718 – William Xifaras Mar 24 '16 at 18:08
  • 1
    You should avoid `async void` unless you have to have that signature to match some .NET Framework event. You should use `async Task` instead. – mason Mar 24 '16 at 18:16
  • Thanks for the comments…. Why is using async void a bad idea?... none of the linked sites answers this question. – musium Mar 24 '16 at 18:18
  • The links explain how to perform background operations. You can return void and use QueueBackgroundWorkItem to populate your cache. – Joseph Simpson Mar 24 '16 at 18:37
  • I now use QueueBackgroundWorkItem and it works fine, thanks for the link. But I'm still wondering what's the problem with async void. – musium Mar 24 '16 at 18:51
  • 1
    It's all explained on MSDN in [Async/Await Best Practices](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). – mason Mar 24 '16 at 20:00
  • @musium np, comments converted to an answer. If you could kindly accept the answer. – Joseph Simpson Mar 24 '16 at 20:35
  • 1
    Originally all the async functions (part of .Net 4.5) were supposed to return Task or Task only but to have a backward compatibility with millions of event handlers which have already been written in .Net world C# compiler designers thought of taking this exception. All event handlers like the methods who act as callbacks for events like button click etc always have a signature like void ClickEventHandler ( Object sender, EventArgs e). All such functions could have never been made asynchronous if void return type was not acceptable by async functions. – RBT Mar 24 '16 at 20:35
  • 1
    So for all your common use cases of async function except event handlers return type should always be Task or Task. Also from extensibility standpoint, today you might not be using other TPL features like continueWith etc but if tomorrow you want to do something like this then you will end up modifying the signature of your method which will frustrate you. Hence it is better to prepare in advance. – RBT Mar 24 '16 at 20:45

2 Answers2

3

Have a look to Hangfire:

"Hang fire: An easy way to perform fire-and-forget, delayed and recurring tasks inside ASP.NET applications. No Windows Service required."

sesispla
  • 300
  • 1
  • 4
1

You can return void and use QueueBackgroundWorkItem to populate your cache.

QueueBackgroundWorkItem provides a safe way to run a short-lived background task.

Joseph Simpson
  • 4,054
  • 1
  • 24
  • 28