Wednesday, March 21, 2012

Harrisburg .Net User Group Presentation

Thank you to all who attended my presentation last night. I had a great time and hope you did too. As promised, I posted my presentation and coding samples. It’s hosted on github and you can access it here.

I also want to thank all of you who provided feedback. This is invaluable and I love reading them. Especially when they provide honest critiques. If you have any questions about the content, please ping me.

Saturday, March 3, 2012

Properly Handling Exceptions

In my last post I discussed how exceptions should be treated as exceptional and not used to control work flow. Now lets take a look at how we can manage exceptions to provide a positive user experience without sacrificing the intent of the code.

To start lets review the general guidelines to exception management:

  1. Only catch exceptions you expect to occur.
  2. Only catch exceptions if you are prepared to handle them
  3. Keep exception management simple. You don’t want exception handling throwing exceptions, or then you loose all information about what really happened in the first place.
  4. Properly clean up your components in the event of an exception

With these simple rules how could we clean up our existing code?

public class Service
{
    public IEnumerable<Entity> GetEntities()
    {
        try
        {
            var entities = repository.GetEntities("select * from table …");
            return entities;
        }
        catch (Exception e)
        {
            Logger.Log(e);
            return new Entity[0];
        }
    }
}

To start we can catch an explicit exception, for example SqlException. We could then wrap the exception with a custom type and provide the specific SQL statement that caused the error. finally we can throw the new exception allowing the exception to bubble up the call stack. it would look something like this

public class Service
{
    public IEnumerable<Entity> GetEntities()
    {
        string sql = "select * from table …";
        try
        {
            var entities = repository.GetEntities(sql);
            return entities;
        }
        catch (SqlException e)
        {
            var wrapped = new ExtendedException(sql, e);
            throw wrapped;
        }
    }
}

Do you see the changes?

  1. We are only handling SqlExceptions.
  2. We handle the exception by wrapping it in a custom exception we defined and adding the SQL statement to the message.
  3. We then throw the new exception allowing it to bubble up the call stack.

We now have better control of what will happened next. For example our UI code can now distinguish between existing items, no existing items and an exception occurring. A controller action might look like this

public ActionResult Index()
{
    var results = new Service().GetModels();
    if(results.Any())
    {
        return View(results);
    }
    return RedirectToAction("AddNew");
}

Exception handling can be placed in a global action filter that logs the error and redirects the user to an “error occurred” page.

Something we didn’t cover in this example was cleaning up components. This example does require it, but lets take a look at another example which cleans up after itself in the event of an exception.

public IEnumerable<Entity> GetEntities(string sql)
{
    IDbCommand command = null;
    IDataReader reader = null;
    try
    {
        command = connection.CreateCommand();
        command.CommandText = sql;
        reader = command.ExecuteReader();
        while(reader.Read())
        {
            yield return reader.ToEntity();
        }
    }
    finally
    {
        if (reader != null)
        {
            reader.Dispose();
        }
        if(command != null)
        {
            command.Dispose();
        }
    }
}

Here we are disposing of our command and reader components within the finally block. Notice that we don’t have a catch statement, it’s not needed. Granted we could move the ExtendedSqlException into this block, but I wanted to stick with our original code sample.

One last thing to note. The .Net framework contains the keywork using which allows us to write more expressive code when disposing of components. we can rewrite the above block as

public IEnumerable<Entity> GetEntities(string sql)
{
    using (var command = connection.CreateCommand())
    {
        command.CommandText = sql;
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                yield return reader.ToEntity();
            }
        }
    }
}

Which is the exact same thing as our previous block, but the code is much more expressive and easier to read.

Exceptions are Truly Exceptional

Exceptions are meant to break way from the current workflow and inform the user that some unexpected happened. It is not meant to control the workflow the system. Too often I have seen code like this:

public class Service
{
    public IEnumerable<Entity> GetEntities()
    {
        try
        {
            var entities = repository.GetEntities("select * from table …");
            return entities;
        }
        catch (Exception e)
        {
            Logger.Log(e);
            return new Entity[0];
        }
    }
}

The problem is there is no difference between the repository simply not containing any entities and something going horribly wrong in the lower components. Handling the scenario were no entities are present in the repository is completely different than getting a SQL exception, or any exception for that matter.

In other words, if the database returns no entities, I may want to redirect the user to “add new” screen rather than display an a listing of not entities. However, if an exception was thrown I may want to inform the user something unexpected occurred, that the system administrators have been notified and they should log off of the system, until further notice.

However I cannot do that in this scenario because the calling code cannot distinguish between no entities and fatal operations.

This also means the developer must understand the result have potentially 2 meanings. 1. No data is present and 2. an exception may have occurred.

So how can we improve upon this code so we can return the expected results and still provide a positive user experience within the application itself? That will be the context for my next post.