In the last one we mentioned a centralized management Exceptions, so that all Exception are produced in the same document, for us late Review, reconstruction, will not see the flying throw new Exceptions(...)
, but also try to avoid the same kind of Exception has With different Messages, different parameter information is recorded.
Also in the last article, we also propose a refinement Exception way that is in accordance with the type, ErrorCode, Cause this refinement.
For example, the following file structure will be formed in the project: (for example code, please visit Github )
The code files related to Exception are in the green box. Among them XXX_ErrorCodes
, the XXX_ExceptionFactory
other types should be marked as internal
, that is, only visible inside the class library.
In the above file structure, I briefly simulated the hierarchical structure of a project, not necessarily all of them, but roughly as follows:
- Common is the project’s public class library, which contains various auxiliary codes, framework codes, etc., which represent the basic part of your project;
- Dal puts the code that connects to various stores (MySql, Redis, etc.), that is, puts DataAccessObject;
- Repo shields various details and directly provides services for various services;
- Services represents your various business modules. Of course, there are different names and meanings in your project structure, as everyone knows it;
- Application specific applications provide Endpoint, open the various functions of Service, and face the caller.
Why do we need to describe a project hierarchy in detail, because we need to solve a problem in a while, that is, the visibility of Exception.
Let’s sell it first, and talk about it later. C# Exception Handling Best Practices. Before that, solve a small problem first.
ExceptionFactory and Ensure
The Exception centralized management , in addition to our use of ExceptionFactory, there is a writing we often see that Ensure, or EnsureXXX.
Refer to the source code of .net core, we will see a lot of Ensure classes, the source code link https://source.dot.net/#q=Ensure.
We often complex or repeat appearance in the judgment written Ensure, Ensure the Chinese meaning “ensure” It is this meaning.
C# Exception Handling Best Practices. The sample code is as follows:
// Ensure internal static class Ensure { public static void NickNameNotExisted(string newNickName) { bool alreadyExisted = true; // Some Check if(alreadyExisted) { throw IdentityExceptionFactory.NickNameExisted(); } } public static T NotNull<T>([NotNull]T? obj, string paramName) where T : class { if (obj == null) { throw new ArgumentNullException(paramName); } return obj; } }
C# Exception Handling Best Practices
We observe that the Exception in the Ensure class is also ExceptionFactory
generated (of course those basic Exception types, such ArgumentNullException
as directly new).
That is, C# Exception Handling Best Practices the Ensure
class is an abstraction of judgment (if statement, etc.), and ExceptionFactory is just a generator of Exception.
Of course, when we don’t throw an exception directly on the spot, but call the Ensure class method to throw an exception, there will be a slight difference:
- Stack Trace is different, the first line is always the method of Ensure
- No big deal, because Stack Trace will record the subsequent calls in detail;
- The code logic is not as straightforward as writing throw directly
- Clearly use the name Ensure, write it in the code rules, and unite within the team. When you see Ensure, you know that an exception may be thrown.
- Pay attention to CodeAnalysis and NRT (null reference types)
- If the project enables the above two functions, you need to add the corresponding Attribute. For example, the
NotNull
method of the above code , otherwise CodeAnalysis will remind you of errors such as CS8602 after Ensure .
- If the project enables the above two functions, you need to add the corresponding Attribute. For example, the
Exception visibility
For example, C# Exception Handling Best Practices is DalException
not to be seen on the Service, or visible only to Repo?
In fact, there are no obvious errors either visible or invisible, but in general,
- Hide and wrap the lower Exception, which is more friendly to the caller.
- For example, in the WebApplication project, we can only face Exception from Service without knowing the details of Dal or Repo.
- And the exception from IdentityService, there is only IdentityException (not counting the basic Exception of ArgumentNull)
- Hide and wrap the Exception in the lower layer, you can add more useful information to each layer
- If you are calling a third-party library, try to hide and wrap it into your own exception type. For example, if you call the MySqlConnector library in Dal, it will be
MySqlException
put in the innerException of DalException. Let other parts of the program not have to face an outsider who might be replaced.
Conclusion
The above is more specific, so if it is different from your actual use, please communicate more and make progress together, and I will add it to the article.
In the next article, I will talk about some practical experiences about catching Exception, such as global exception handling and exceptions in asynchronous programming.
thanks for reading C# Exception Handling Best Practices.