EN VI

C# - How can I conditionally have an "async Task" method be a no-op?

2024-03-13 02:00:07
C# - How can I conditionally have an "async Task" method be a no-op?

To get text written to the SQL Server XEvent profile, I have written the following method:

protected async Task LogToProfiler(string message)
{
    await using (var dbContext = await NoTrackingDbFactory.CreateDbContextAsync())
    {
        await dbContext.Database.ExecuteSqlAsync($"-- {message}");
    }
}

It works great, writing out the messages where I want to the profiler event list. The problem is, what I need is:

#if DEBUG
protected async Task LogToProfiler(string message)
{
    await using (var dbContext = await NoTrackingDbFactory.CreateDbContextAsync())
    {
        await dbContext.Database.ExecuteSqlAsync($"-- {message}");
    }
}
#else
protected void LogToProfiler(string message) {}
#end

So that in release mode there is no set up and tear down of a Task. Is there a way to do this?

Also, I do call it as:

if (profilingOn)
    await LogToProfiler("entering method");

Is this sufficient to avoid the setup/teardown if profilingOn == false? I believe it is. It's safer to use #if DEBUG but I can force profilingOn to false if it's built for RELEASE.

My fundamental concern is developers make mistakes and so it might end up being called even if profilingOn is false and we're running in RELEASE mode. So I want to be as safe as possible.

Solution:

You can just return Task.CompletedTask.

Furthermore, you can do something like this for all of the log methods:

protected async Task LogToProfiler(Func<string> messageProvider)
{
    if(!profilerEnabled) 
       return; //as you might know this section up to await will be called synchronously.
    var message = messageProvider();
    await using (var dbContext = await NoTrackingDbFactory.CreateDbContextAsync())
    {
        await dbContext.Database.ExecuteSqlAsync($"-- {message}");
    }
}

Everything up to first await in async method is invoked synchronously, on same thread. No task will be created. If you explicitly call Task.Yield() at the start of the method it will run on some other thread in pool.

Try running this:

async Task RunMe()
{
   Thread.Sleep(1000000);
}

like this:

RunMe();//no await

And you will see your main method stuck and not "fire-and-forget".

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login