Search code examples
c#asynchronousasp.net-identityculture

ASP.NET Identity CultureAwaiter Code


During the development of an ASP.NET MVC application I came across the CultureAwaiter, an instance of which is returned on invocation of the extension method WithCurrentCulture.

I'm relatively fresh to Microsoft's async model, so I'm struggling to understand the intuition behind the four lines in the code that I've marked below. Note that I took this from assembly file version "2.1.30612.0" using ILSpy...I don't think MS has made the source available for us to see yet.

In those four lines, which I'm assuming run synchronously on the same thread, it looks like the variable currentCulture is set to the current thread's culture (so far so good). Two lines later, however, it just takes that variable and sets the current thread's culture to it (i.e. just reverses the assignment). What's the use of that?

The UI culture, on the other hand, has slightly different behavior in those four lines. Note the case of the 'UI'/'Ui' in the variable names. On the second of those four lines, the variable currentUICulture is set to the current thread's UI culture (presumably to "remember" it for later use). Two lines later, the current thread's UI culture is set to a different variable currentUiCulture (note the different case) ...defined at the start of the method.

My newbie understanding of the async model aside, I would have at least expected that both the CurrentCulture and CurrentUICulture have the same getting/setting behavior in this method. I could be completely wrong, but my "gut" feeling is telling me that there might be an incorrect assignment happening in those four lines.

Can anybody shed some light on this for my understanding? Is it perhaps related to ILSpy?

// Microsoft.AspNet.Identity.TaskExtensions.CultureAwaiter<T>
public void UnsafeOnCompleted(Action continuation)
{
    CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
    CultureInfo currentUiCulture = Thread.CurrentThread.CurrentUICulture;
    this._task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(delegate
    {
        // WHAT'S GOING ON IN THE NEXT FOUR LINES?
        CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
        CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
        Thread.CurrentThread.CurrentCulture = currentCulture;
        Thread.CurrentThread.CurrentUICulture = currentUiCulture;
        try
        {
            continuation();
        }
        finally
        {
            Thread.CurrentThread.CurrentCulture = currentCulture;
            Thread.CurrentThread.CurrentUICulture = currentUICulture;
        }
    });
}

Solution

  • The purpose of this is to run the continuation with the current culture settings although it might run on a different thread. But we don't want to modify the culture of that thread in a persistent way because we don't own that thread. It's shared. So we must restore the old settings before exiting.

    Probably, the decompiler just shows misleading variable names. Reflector does it correctly:

    public void UnsafeOnCompleted(Action continuation)
    {
        CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
        CultureInfo currentUiCulture = Thread.CurrentThread.CurrentUICulture;
        this._task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(delegate {
            CultureInfo info1 = Thread.CurrentThread.CurrentCulture;
            CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
            Thread.CurrentThread.CurrentCulture = currentCulture;
            Thread.CurrentThread.CurrentUICulture = currentUiCulture;
            try
            {
                continuation();
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = info1;
                Thread.CurrentThread.CurrentUICulture = currentUICulture;
            }
        });
    }