Tired of looking for errors in log files? Use OneTrueError - Automated exception management in .NET.

Throwing exceptions

Throwing exceptions is in itself trivial. Just invoke throw with an exception object. But when should you throw? Should you create a new exception or rethrow an old one? What should you include in the exception info?

  1. What are exceptions
  2. Designing exceptions
  3. Throwing exceptions

In the previous two articles you’ve got to know what exceptions are and how to design them. This time I’ll go through some typical scenarios and show you how to throw exceptions.

As I’ve written earlier, exceptions should be thrown when something exceptional has happended. That is, if you expect something to be a certain way, it’s exceptional if it isn’t.

Validating arguments

The most simple example is when you expect method arguments:

public void PrintName(string name)
{
     Console.WriteLine(name);
}

In this case you EXPECT that a correct name is supplied, as it’s the only way to ensure that the method works as expected. The code above will work with null as an argument, but all you’ll see is an empty line. The problem is that you won’t detect that something went wrong (other than the empy line). The worst thing is that those kind of errors are hard to detect as from the code’s perspective it seems to work, while from the users perspective they do not get the expected output.

Now imagine that error in a production system where the bussiness logic (“BL-A”) comes to an incorrect conclusion thanks to invalid arguments. That in turn will probably surface somewhere else in your application. For instance where the generated result is used in some other buiness logic (“BL-B”). If you are really unlucky, your users will notice that “BL-B” doesn’t work as expected in some cases. That will lead to several hours of debugging (and probably adding a lot of logging) before discovering that it’s actually “BL-A” that didn’t get the expected arguments.

I would at any time choose to spend some extra time on making sure that I get reasonable values into my method arguments instead of having to fix business logic bugs later.

Let’s add validation:

public void PrintName(string name)
{
	if (name == null) throw new ArgumentNullException("name");
	
     Console.WriteLine(name);
}

Now we get an exception directly instead of deeper down in the buiness logic. That check might be trivial, but without it we would proably get the dreaded NullReferenceException somewhere deeper down in the call tack.

In this case the name should also contain a specific length, and may only contain alpha numerics:

public void PrintName(string name)
{
	if (name == null) throw new ArgumentNullException("name");
	if (name.Length < 5 || name.Length > 10) throw new ArgumentOutOfRangeException("name", name, "Name must be between 5 or 10 characters long");
	if (name.Any(x => !char.IsAlphaNumeric(x)) throw new ArgumentOutOfRangeException("name", name, "May only contain alpha numerics");
	
     Console.WriteLine(name);
}

If you find yourself repeating that code you could create either a Validator method on the Person class or create a PersonName class and do the validation in it. It depends on your application type and how serious you think that the invalid arguments is.

I personally do sanity checks in all of my methods, but only do business validations in those methods that do business logic.

You failed the code

The other time to throw exceptions is when a method can’t deliver the expected result. A controversal example is a method with a signature like this:

public User GetUser(int id)
{
}

It’s quite common that methods like that returns null when the user is not found. But is it really the correct way of doing so? I would say NO. I got three reasons to that.

First, the method is called GetUser() and not TryGetUser() or FindUser(). We are saying that we SHOULD get an user. Not delivering an user is therefore an exceptional case.

Second, we have somewhere got an ID for an user. And if that ID is not found in the data source something is probably wrong.

Third, in most scenarios we do expect to find an user. Hence it’s an exceptional case if we do not find one. By using null as a return value we do communicate that we expect the user to be not found and not that it’s something exceptional.

The null cancer

By using null we have to have this code everywhere:

var user = datasource.GetUser(userId);
if (user == null)
    throw new InvalidOperationException("Failed to find user: " + userId);
// actual logic here

That kind of code spread like cancer when you allow null as a return value.

Why don’t we just use:

public User GetUser(int id)
{
    if (id <= 0) throw new ArgumentOutOfRangeException("id", id, "Valid ids are from 1 and above. Do you have a parsing error somewhere?");
   
    var user = db.Execute<User>("WHERE Id = ?", id);
    if (user == null)
        throw new EntityNotFoundException("Failed to find user with id " + id);
	   
    return user;
}

Now we get more DRY code since we don’t have to make sure that we get an user back. We can instead rely on the global exception handling to display the error message to the user.

Simply think twice by deciding that returning null is OK. In most cases an exception is to prefer.

Context information

If you take the example with GetUser() you’ll see that the EntityNotFoundException is quite worthless without getting the ID. If you want to switch to better exception handling (i.e. not being afraid of throwing exceptions in all exceptional cases) you’ll also have to make sure that the exception messages is meaningful.

“Meaningful” means that you with the help of the exception message can prevent the exception in the future. That means that the future bug fix should be made in the method that provides the invalid user ID and not the the GetUser(), as the latter would be a workaround and not a bug fix.

Rethrowing exceptions

To me, rethrowing exceptions is just a useful technique to add additonal context information:

public void Query(string sql)
{
    if (sql == null) throw new ArgumentNullException("sql");
	if (!sql.Contains("SELECT")) throw new FormatException("Expected SQL to be a SELECT query. You specified: " + sql);
	
	try
	{
	    using (var cmd = _connection.CreateCommand())
		{
		    cmd.CommandText = sql;
			using (var reader = cmd.ExecuteReader())
			{
			    return ToList(reader);
			}
		}
	}
	catch (DataException err)
	{
	    //ALWAYS include the original exception
	    throw new QueryException(sql, err);
	}
}

If the ADO.NET exceptions would have had enough information rethrowing would have not been necessary.

If you catch one exception and throw another one, always make sure to add the original exception as the inner one as shown in the above example.

Catch/Log/rethrow

Some use catch/log/rethrow.

public void Query(string sql)
{
    if (sql == null) throw new ArgumentNullException("sql");
	if (!sql.Contains("SELECT")) throw new FormatException("Expected SQL to be a SELECT query. You specified: " + sql);
	
	try
	{
	    using (var cmd = _connection.CreateCommand())
		{
		    cmd.CommandText = sql;
			using (var reader = cmd.ExecuteReader())
			{
			    return ToList(reader);
			}
		}
	}
	catch (Exception err)
	{
	    _logger.Error("Failed", err);
		throw;
	}
}

DON’T.

ALL different application types in .NET have places where you can catch unhandles exceptions to log them. Simply only rethrow exceptions if there is a real reason to do so.

Do not rethrow caught exceptions

The callstack is empty when you create a new exception. It’s generated as the exception travels up the call stack.

That means that every time you throw an exception the call stack is rebuilt as the exception travels up.

The following code will therefore result in a rebuilt callstack.

try
{
   // [...]
}
catch (Exception err)
{
    // dosomething
    throw err;
}

If you want to keep the original callstack you have to either use some hacks or wrap the exception with a new exception. A typical example in the .NET framework is when you invoke a method using reflection. If an exception occurrs it’s wrapped with the TargetInvocationException.

summary

Let’s summarize what we have learned:

  1. Use the method definition to determine what’s exceptional. If it’s unclear: Is the method/argument(s) naming as good as you think?
  2. Validate the method contract and always throw if it’s violated.
  3. Always throw if the method can’t deliver the expected result (try to avoid to return null if possible)
  4. Try to include context information in the exceptions when throwing instead of using extensive logging.
This entry was posted in Architecture, CodeProject and tagged . Bookmark the permalink.
  • Jmaloney

    Thx, very insightful.

  • Przemysław Włodarczak

    >> If you want to keep the original callstack you have to either use some hacks

    No need to. Just use throw;

    try
    {
    }
    catch (Exception ex)
    {
    // do sth
    throw; // <– this rethrows original exception with preserved stack trace
    }

    • http://blog.gauffin.org/ jgauffin

      The example could be better. I was going for cases where you can’t just rethrow the exception, as the example case shows (using reflection).

      If you look at the former example you see that I just use “throw”;

      But I’ll going to change the last example to something better.