Return to Article List

Persistence is a Key Field

01/31/15

If at first (or second or third) you don't succeed, comment it all out (just in case you decide it wasn't so bad after all) and write it over - a.ka. if the front door doesn't work, try the back.

This week in Coder Foundry's Master Class, we started on a new project - a ticket system for tracking help desk-type inquiries. It came with project specification packed with lists - user roles to implement, views that must be created for different classes of user, and functionality (such as notifying developers when assigned a new ticket) that must be built in. Conveniently, it also came with a suggested database design, though I ended up modifying it. Although it’s not a complex system, there are lots of moving parts.  We are building it using C#, MVC with Razor views, and code first design.

Early on, I made my first acquaintance with async tasks and the related await keyword. It was the first time I’d met them, and so far I don’t like them at all. Here’s one of them, an async method which calls another async method:


public async Task OLDRemoveUserFromProject(string userId, int projectId)
{
      if (!await this.IsOnProject(userId, projectId))
      {
           var pu = db.ProjectUsers.SingleAsync(p => p.ProjectId == projectId && p.UserId == userId); 
           db.ProjectUsers.Remove(pu.Result);
           db.SaveChanges();
      }
}

I’ve highlighted the key features as I understand them:

  • async Task: The method return type is async Task, which it must be because it queries the database asynchronously (using SingleAsync).
  • await: await is used to call isOnProject because that is also async and we need the result from that before proceeding, so we must “await” it;  otherwise the  program would go on its merry way and the result from isOnProject might not be back yet when we try to use it.   
  • pu.Result: Although the answer to isOnProject is yes or no (i.e. boolean), because it’s coming from an async function it comes back wrapped in a Task Result object; to get at it, we have to access the Task.Result property (pu.Result here).

The final part I’ve highlighted above is the “OLD” part of the method name. That’s because async is like ripples in a pond: once you go down that path the effects just emanate outward - up the call stack. I kept running into unanticipated consequences from this and our other async functions. I fixed this and that but then there was something else, and so my final fix was to rewrite the functions to avoid using async. I was spending precious time trying to understand async and work them all out and my ticket tracker project was stalled. It was a classic case of there wasn’t time to do it right but there was time to do it over - to stop those painful ripples!

I can’t avoid async forever; it’s critical for applications that see a large number of concurrent or long-running requests, unless you don’t care if you leave your users hanging with a poorly responsive application.  Before I can successfully employ it though, I’m going to have to learn more about it. To do that, I’m going to have to find a way to shoehorn it in between a constantly evolving list of looming deadlines -  a common dilemma of programming life. It might be awhile, but it will happen.

###

 

Comments

On 02/06/15 David.Jolly said:
This async stuff looks absolutely God-awful! I remember you had reservations about it, even before you started the boot camp. All I can say is:.persevere. If you keep at it, it will become clear. It always does. What if instead of waiting you just while loop until your op with the desired data structure succeeds? Probably a naive question.
On 02/10/15 Andrew Jensen said:
Understanding exactly how threads work can be a bit difficult. Having a solid understanding of operating system design and architecture helps a lot. However, in lieu of a lecture on OS theory, take a look at this excellent blog by Stephen Cleary. He does a nice job of explaining the async and await keywords. They definitely work a lot differently than a while loop that is constantly running and actively waiting for a change in condition - such an action occupies a continuous stream of processor time at the expense of other tasks. I'll give you an absurd example. A while loop waiting for a condition change is a bit like your child running in circles in your living room indefinitely until you grab her and say, "Hey, go do the dishes." While she's running in circles she's a complete nuisance. You'd really like to vacuum the living room floor and rearrange the furniture, but there's no way you can do it with all that ridiculous running going on. Async and await allow you to metaphorically put your child down for a nap so you can vacuum the living room floor and rearrange the furniture, then wake her up later so she can do the dishes. You've been able to go about your business, uninterrupted, and her nap hasn't inconvenienced you in the slightest; rather, it has likely made things much easier on you. http://blog.stephencleary.com/2012/02/async-and-await.html

Add Comment

Return to Article List