2021 C# Exception Handling Best Practices

C# Exception Handling Best Practices, Regarding the basic grammar and function of exceptions, I won’t repeat them here. Let me record some thoughts about Exceptions in the project.

C# Exception Handling Best Practices

The basic document is the official or great god’s explanation and elaboration on this issue. This article is based on these documents, so I won’t repeat them, you can refer to and record by yourself.

C# Exception Handling Best Practices
C# Exception Handling Best Practices

Use Exception instead of Error Code

In the initial design of the project, sometimes we define the following structure in order to clarify the error type:

enum ErrCode
{
    Ok,
    ArgumentErr,
    OtherErr1,
    OtherErr2
}

class SomeClass
{
    public ErrCode DealSomething()
    {
        //.....
        return ErrCode.Ok;
    }
}

class CallerClass
{
    public void CallSome(SomeClass some)
    {
		ErrCode err = some.DealSomething();
        
		if(err == ErrCode.Ok)
        {
            //....
        }
        else
        {
            //.....
        }
    }
}

C# Exception Handling Best Practices

Using ErrCode does not mean that it is completely impossible. C# Exception Handling Best Practices. For example, returning ErrCode in Web Api calls is a good choice.

1. Eat abnormalities

But in other cases, using ErrCode will make users miserable, because every call must be handled and judged carefully, otherwise it will be particularly easy to eat exceptions .

Compare the following program fragments:

Use ErrCode

class SomeClass 
{ 
    public ErrCode DealSomething() 
    { 
        //..... 
        return ErrCode.SomeErr; 
    } 
} 
class CallerClass 
{ 
    public void CallSome(SomeClass some) 
    { 
		some.DealSomething(); 
		giveMoney(); 
	} 
}

C# Exception Handling Best Practices

Use Exception

class SomeClass 
{ 
    public void DealSomething() 
    { 
        //..... 
        if(somthingWrong) 
        { 
        	throw new SomeException(...); 
        } 
    } 
} 
class CallerClass 
{ 
    public void CallSome(SomeClass some) 
    { 
		some.DealSomething(); 
		giveMoney(); 
	} 
}

C# Exception Handling Best Practices

ErrCode was used on the left because the ErrCode was accidentally not accepted, which caused the subsequent code to continue to execute;

With Exception, even if it is not processed, the subsequent code will not be executed, and it will be thrown to wait for the upper user to process it.

In addition, Exception has many other benefits, such as Stack Trace information, cross-process and so on.

So the essence of Exception is the existing error-handling mechanism, so don’t use custom ErrCode anymore.

Second, the use of Exception in the project

Since we clearly use Exception as our error handling solution. So how do you use it?

1. Use refined Exception

First of all, avoid the following wording:

class Dal
{
    public void Add(Entity entity)
    {
        //....
        if(somthingWrong)
        {
            throw new Exception("add wrong");
        }
        //...
    }
}

C# Exception Handling Best Practices

The problem with this way of writing is that the caller can’t clarify the reason and type of the Exception, and only depends on the document or message to understand. When the project accumulates, the same Exception is flying in the sky, and the log is full of all kinds of magic message.

We can refine the Exception in multiple dimensions: type, error type, and specific reason. C# Exception Handling Best Practices.

Exception particular type of use, for example ConnectionExceptionTimeoutException, a Exception that is defined for each error type,

This method must be clear to everyone, but what if there are more than a dozen errors in a module, and you have more than a dozen modules?

C# Exception Handling Best Practices There are two ways to deal with this situation.

  • Divided by internal and externalIf an exception needs to be received and processed by the external caller of the module, then define a specific Exception type, such as ConnectionException, TimeoutException;If an exception is handled by the module memory, only the same InnerException (or other name) plus other information is used to distinguish it. (Did you smell ErrCode again, that’s right).This method is often seen in many open source libraries.
  • A module defines an Exception type, Exception and ErrCode are combinedI just said that ErrCode can’t be used, and I mentioned it here, don’t get me wrong, look down carefully.For example, the following code:
class ErrCode
{
    public int Code { get; set; }
    public string? Name { get; set; }
    public string? Message { get; set; }
}

class DalException : Exception
{
    public ErrCode Code { get; }
    
    public DalException(ErrCode errCode, Exception? innerException = null):base(errCode.Message, innerException)
    {
    	Code = errCode;    
    }
}

C# Exception Handling Best Practices

  • ErrCode is no longer simply an enum, but has a greater use.In this way, each module has its own Exception type, and each error type can be effectively classified.This method is often used in business modules and basic framework modules of company projects.

2. Use Exception.Data

The above code snippet, are you a bit surprised, buddy, C# Exception Handling Best Practices . you fixed the Message directly into ErrCode, isn’t it a bit radical?

In fact, this is also the experience summed up in actual development. Try not to let programmers write all kinds of Exception Messages.

But we can use Exception.Data to achieve more scenarios.

For example, the following code snippet:

static class ExceptionFactory
{
    public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
    {
        DalException ex = new DalException(ErrCodes.MigrationErr);
        
        ex.Data["OldVersion"] = oldVersion;
        ex.Data["NewVersion"] = newVersion;
        ex.Data["Cause"] = cause;
    }
}

class SomeCls
{
    void Migration()
    {
        //.....
        throw ExceptionFactory.OnMigrationErr(oldVersion,)
    }
}

C# Exception Handling Best Practices

In this way, the specific information that needs to be recorded is fixed for each error, and the cause is to let the programmer record the current cause. Of course, you can rewrite OnMigrateError to record another scene.

The benefit of recording the specific information required by the exception in the Data Dictionary instead of writing it in the Message is to facilitate further processing in the future, such as structured logs.

Of course, this brings a problem that needs attention, that is, the explicit handling of Exception.Data, because the Exception.ToString() method does not print Data.

In terms of logging, C# Exception Handling Best Practices. if you use Serilog , it is recommended that you use Serilog.Exceptions , which is very friendly to Exception.Data.

So far, we are refining the Exception and gradually use the type —> ErrorCode —> specific cause

3. Centralized management

Exceptions flying all over the sky, all unclear solutions need to be solved by rules, but the rules need to be kept in mind at all times, so the most reliable solution is to use code structure.

First create a ErrCodesstatic class, and then create a ExceptionFactoryclass, so that the exceptions thrown in the business code have throw ExceptionFactory.XXException()this form.

//All error codes
internal static class ErrCodes
{
     public static ErrCode MigrationErr {get;} = new ErrCode(1, nameof(MigrationErr), "Error happens in Migration.");
     //........ other errors
}

//All Exceptions are generated from this
internal static class ExceptionFactory
{
     public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
     {
         DalException ex = new DalException(ErrCodes.MigrationErr);
        
         ex.Data["OldVersion"] = oldVersion;
         ex.Data["NewVersion"] = newVersion;
         ex.Data["Cause"] = cause;
     }
     //..... Other scenes
}

C# Exception Handling Best Practices

In this way, when code review, as long as you see a programmer in the code new XXException, then urge him ExceptionFactoryto find suitable exception scenarios in it or add it yourself.

With the accumulation of the project, even abnormal, many types of errors, we all simply ErrCodesand ExcepionFactorytwo classes, summarized, reconstruction. C# Exception Handling Best Practices.

Leave a Comment