Saturday 30 July 2016

c# - Exceptions architecture for core library



I'm currently working on a project that separated into few parts, Core, UI, etc. Until it would be hard to change project architecture, i'd like know what the best way to work with exceptions in Core library? I mean, how to organize these exceptions? For example, i can throw system exceptions with meaningful messages:




// Database implementation within Core library
class Database
{
void Foo()
{
// ...
if (Something())
throw new InvalidDataException(message:"The something!");
else

throw new InvalidDataException(message:"It's not something!");
}
}

class UI
{
void ShowDatabase()
{
var database = new Database();
try

{
database.Foo();
}
catch (InvalidDataException e)
{
CleanUp();
MessageBox.Show(e.ToString());
}
}
}



But Core library doesn't have to deal with user in any way. Am i right? Ok, there is another way. I can throw system exceptions with error code as exception message so the UI layer can pick warning message for user itself:



static class ErrorCode
{
static string Something = "SomethingOccur";
static string NotSomething = "NotSomethingOccur";
}


class Database
{
void Foo()
{
// ...
if (Something())
throw new InvalidDataException(message:ErrorCode.Something);
else
throw new InvalidDataException(message:ErrorCode.NotSomething);
}

}

class UI
{
void ShowDatabase()
{
var database = new Database();
try
{
database.Foo();

}
catch (InvalidDataException e)
{
if (e.Message == ErrorCode.Something)
{
CleanUpSomthing();
MessageBox.Show(Resources.SomethingMessage);
}
else if (e.Message == ErrorCode.NotSomething)
{

CleanUpSomethingElse();
MessageBox.Show(Resources.NotSomethingMessage);
}
}
}
}


Now its more flexible, but e.Message == ErrorCode.Something looks ugly, i think. There is a third way, implement exceptions for any case separately:




class SomethingException : Exception
{
public SomethingException(string message = null, Exception inner = null) : base(message, inner) { }
}

class NotSomethingException : Exception
{
public NotSomethingException(string message = null, Exception inner = null) : base(message, inner) { }
}


class Database
{
void Foo()
{
// ...
if (Something())
throw new SomethingException()
else
throw new NotSomethingException();
}

}

class UI
{
void ShowDatabase()
{
var database = new Database();
try
{
database.Foo();

}
catch (SomethingException e)
{
CleanUpSomething();
MessageBox.Show(Resources.SomethingMessage);
}
catch (NotSomethingException e)
{
CleanUpSomethingElse();
MessageBox.Show(Resources.SomethingMessage);

}
}
}


It looks nicer, but there will be hundreds of exceptions for each case at some moment. Sounds bad.



So, the question is - what the best way to deal with exceptions in Core library? Maybe there is any best practices?



P.S. sorry for my English, btw.



Answer



The general practices should be like that:




  1. You can use standard .NET exception classes (ex. ArgumentNullException, InvalidOperationException) in any case where the exception type matches the particular situation. There are plenty of .NET exception classes, and you need to have some knowledge about them to know which one to throw and when.


  2. In the cases which are errors strictly connected to the logic of your Core library, you define your own exception classes, and use them to throw.


  3. You should probably create a hierarchy of exceptions, with the base exceptions classes representing general errors, and more specific exception classes inheriting from them. Example, a base exception class that you define might be named: ex. CalculationExcepion. And then you define classes that inherit from it - specifying particular kinds of calculation exceptions. With this approach the users of your library would be able to catch a base exception (to cover a number of error cases), or to handle a specific exception - based on their preference.


  4. You can introduce additional properties in your exceptions, but be careful with properties like ErrorCode not to end up with one generic exception class that might have 50 different error codes -this will be to hard to handle for the users of your Core library. You can use such a property as ErrorCode in a limited number of special cases of a particular error type, like with HTTP codes that you get when you do a GET request: 200, 500, 404, and a few other codes - but still a limited number.


  5. The public methods and properties in your Core library should be documented about what types of exceptions they throw and when could those exceptions be expected.




No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...