RHarris
RHarris

Reputation: 11227

ObjectDisposedException when trying to access Thread.CurrentPrincipal with async method

I'm fairly new to the new async/await stuff. However, I have the following classes:

public abstract class PluginBase
{
   //Using EF to store log info to database
   private EFContext _context = new EFContext();
   private int Id = 1;
   protected void LogEvent(string event, string details)
   {
       _context.LogEvents.Add(new LogItem(){
          PluginId = this.Id,
          Event = event,
          Details = details,
          User = Thread.CurrentPrincipal.Identity.Name
       });
   }
 }

public class Plugin : PluginBase
{
   public void Process()
   {
      CallWebService();
   }

   public async void CallWebService()
   {
       using(var http = new HttpClient())
       {
          ...
          var result = await http.PostAsync(memberURI, new StringContent(content, Encoding.UTF8,"application/json"));
          if(result.IsSuccessStatusCode)
             _status = "Success";
          else
             _status = "Fail";

          LogEvent("Service Call - " + _status,...);

       }
}

So, the idea is that Plugin.Process gets called. It in turn calls CallWebService(). CallWebService makes an asynchronous call to http.PostAsync. When I return from that call and try to call base.LogEvent(), I get an ObjectDisposedException stating that "Safe Handle has been Closed".

I know there is something going on where when the awaitable finishes, the rest of the code of the method has to be run. Maybe its being run in some other thread or context? If this is the case, how do I get the current user at the time of writing to the log?

Thanks for your help with this.

Edit Based on the answer from Yuval, I made the following changes and it seems to work fine.

public void Process()
{
    var task = CallWebService();
    task.Wait();
}
public async Task CallWebService(List<Member> members)
{
    using(var http = new HttpClient())
    {
       ...
       using(var result = await http.PostAsync(memberURI, new StringContent content, Encoding.UTF8, "application/json")))
       {
          if(result.IsSuccessStatusCode)
             _status = "Success";
          else
             _status = "Fail";
          LogEvent("Service Call - " + _status,...);
        }
    }
 }

Upvotes: 4

Views: 1545

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149588

When I return from that call and try to call base.LogEvent(), I get an ObjectDisposedException stating that "Safe Handle has been Closed".

That's because somewhere higher up the call-chain, someone is disposing your plugin object, which hasn't really finished the asynchronous operation. Using async void is doing a "fire and forget" operation. You don't actually await on Process, hence anyone calling it assumes it finished and disposes of your object.

Change your async void method to async Task, and await it:

public Task ProcessAsync()
{
    return CallWebServiceAsync();
}

public async Task CallWebServiceAsync()
{
   using (var http = new HttpClient())
   {
      var result = await http.PostAsync(memberURI, 
                                        new StringContent(content,
                                                          Encoding.UTF8,
                                                          "application/json"));
      if (result.IsSuccessStatusCode)
         _status = "Success";
      else
         _status = "Fail";

      LogEvent("Service Call - " + _status,...);
   }
}

Note you will need to await ProcessAsync somewhere higher up the call-stack as well.

Upvotes: 3

Related Questions