11

I am trying to asynchronously log some information to SQL Server inside of an MVC 4 controller action targeting .NET 4.0 using the AsyncTargetingPack. I would jump straight to .NET 4.5 but my app lives in Azure and we're still waiting for the update...

This code works as expected (a row is written to my database with no exceptions thrown):

public class SystemActionLogger : ISystemActionLogger
{
    private readonly ActionBlock<Tuple<SystemAction, object>> actionBlock;

    public SystemActionLogger(ISystemActionLogEntryRepository repository)
    {
        actionBlock = new ActionBlock<Tuple<SystemAction, object>>(
            entry => TaskEx.Run(async () =>
                {
                    string data = await JsonConvert.SerializeObjectAsync(entry.Item2);
                    await repository.PersistAsync(new SystemActionLogEntry(entry.Item1, data));
                }));
    }

    public void Log(SystemAction systemAction, object data)
    {
        actionBlock.Post(new Tuple<SystemAction, object>(systemAction, data));
    }
}

And this code throws a NullReferenceException:

public class SystemActionLogger : ISystemActionLogger
{
    private readonly ActionBlock<Tuple<SystemAction, object>> actionBlock;

    public SystemActionLogger(ISystemActionLogEntryRepository repository)
    {
        actionBlock = new ActionBlock<Tuple<SystemAction, object>>(async entry =>
            {
                string data = await JsonConvert.SerializeObjectAsync(entry.Item2);
                await repository.PersistAsync(new SystemActionLogEntry(entry.Item1, data));
            });
    }

    public void Log(SystemAction systemAction, object data)
    {
        actionBlock.Post(new Tuple<SystemAction, object>(systemAction, data));
    }
}

NullReferenceException: "Object reference not set to an instance of an object."

Server stack trace: 
   at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
   at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
   at System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
   at System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClassa.<OnCompletedInternal>b__0(Task param0)

Exception rethrown at [0]: 
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__1(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

I have no visibility into the exception as it is all external code. I don't understand why the second block of code fails. This is the code I originally wrote.

What am I doing wrong?

David Peden
  • 17,596
  • 6
  • 52
  • 72
  • ASP.NET does need special support for `async`, which was added in .NET 4.5. I would not try using the Async Targeting Pack on ASP.NET apps for .NET 4.0 - a few simple things would work but the ASP.NET pipeline is just not ready to see `async` code. – Stephen Cleary Oct 19 '12 at 12:15
  • 1
    Your stack trace includes a reference to `LegacyAspNetSynchronizationContext`, a type added in .NET 4.5. Did you install .NET 4.5 on your Azure server? – Stephen Cleary Oct 19 '12 at 12:16
  • Hey Stephen, thanks for the reply. I had seen a similar response from you on another SO post and was hoping that this was not the case, especially since the first block of code works. Regarding the type, I have .NET 4.5 installed on my box but I did not attempt to jump through hoops and make it a part of my deployment package to Azure. That's an interesting observation. – David Peden Oct 19 '12 at 13:44
  • I think the best option now is to just wait. Azure should be updated this month according to ScottGu. In the meantime, I'm using AppHarbor, which had .NET 4.5 support almost immediately. – Stephen Cleary Oct 19 '12 at 14:28
  • Yep, I've been anxiously waiting ever since RTW. I'm just crossing my fingers that they don't leave the roles-based deployments behind since they only seem to be talking about websites. I did see @scottgu's post and have my fingers crossed... – David Peden Oct 19 '12 at 15:07

3 Answers3

7

I was just having a very similar issue (NullReference from AssociateWithCurrentThread when using a logging task).

The issue I was having was that the original action did not await the completion of the logging task, so when the log finishes and tries to rejoin the original thread a nullReference is thrown because the original thread has terminated.

This was solved for me by making sure that the request controller awaited the logging function.

kcar
  • 833
  • 9
  • 15
0

Dataflow only works on .NET 4.5. The fact that you're running it on .NET 4.0 is unsupported and is likely why you're seeing spurious exceptions.

Andrew Arnott
  • 80,040
  • 26
  • 132
  • 171
-2

I had this problem with .net4.5 when my web service was invoking another WCF service in an async manner. I simply appended a short timed Wait() as I didn't care about the response (telemetry event).

public static void Event(string key, string message) {
    Telemetry.Event(key, message).Wait(100);
}
ohmusama
  • 4,159
  • 5
  • 24
  • 44