Exceptions in service development

First, let’s suppose you need to develop a simple service method somewhere in the business logic layer of an application. The service receives an entity ID (primary key) as argument and needs to perform a call on data access layer to obtain the data item associated to that ID, and then does some computations on the data object and return a result to the client. Assuming that the data access call can be based on a LINQ query (such as when you use Entity Framework), how to you write the code to actually retrieve the entity for you? Specifically, would you use: Single, SingleOrDefault, First, or FirstOrDefault? Consider all you have read about handling exceptions in multi-layer based applications.

public object GetResult(int id)
{
  IQueryable<Entity> query = GetQuery(dataContext);
  var entity = query.Single(e => e.ID == id);
  return ComputeResult(entity);
}

Have you thought well? What’s your answer?

First of all, I hope everybody would avoid First and FirstOrDefault since in this context there should only be one entity with that ID in the database (as it’s a primary key), and in case there would be more entities with the same ID, the issue is in the database design or implementation issue, and this should be a very internal exception that if would ever be thrown it would happen during development, and should turn into a database update that would correct the behavior forever. First and FirstOrDefault are not good because they won’t throw any exception if there are two data items returned by the query sharing the same ID, and this is not OK because development won’t have a chance to fix the database issue rather sooner than later.

Some of you would have probably stated that calling SingleOrDefault is not good either because it returns a null reference in case the entity is missing, and the code below that line would eventually fail when trying to access it. OK, but simply calling Single will turn that behavior into raising an InvalidOperationException instead, which is still a low level exception (many other methods of the.NET Framework core throw the same exception type), and the situation is not actually a system exception – it’s probably just a user concurrency issue (e.g. some other user deleted the entity from the data source before the method has been called). The layered application pattern theoreticians would have probably stated that you need to call SingleOrDefault, and then immediately check the returned entity reference: if it’s null, throw a custom exception type, such as a specific EntityNotFoundException. This way you can add the custom exception type to the fault contract of your service, especially if you expose it through WCF for any reason, and be properly handled on the client side. But is this always needed and should be enforced for all development like that, including other situations? What about timeout exceptions of the database or network communication issues? Theoreticians would probably say that these are system exceptions and could be ignored (i.e. let them pass through your layer) in development, while the user concurrency issue is a thing that can happen for your application even with normal user activity, rather than when technical system malfunctions occur.

I agree that having layers carefully designed and throwing custom exception types documented in the fault contract of the service is a good practice. But I think we shouldn’t overdesign our app internals either, as otherwise your more pragmatic competition would be able to deliver a very good similar product to the market before you are able to finalize development, while it is often difficult to write custom exceptions in your environment (in my opinion .NET requires too much effort to define a custom exception type completely!), and exceptions occur rarely and may often be treated generically, using “Oops, something happened” messages and nothing else.

To clarify, in my opinion, the answer depends on the development contract you have, as a business logic developer, with your client layer developers, and/or as agreed within the application development policies your project adheres to. In many cases you can simply call Single, and let the default InvalidOperationException propagate when the entity is not found, and the user interface layer of the application will eventually present a generic message for the user in case of such of an “unknown” (generic) exception occurs. If this is not fine (such as if there is a HTTP layer that needs to return a HTTP status code depending on the exception, e.g. if your app is actually an API for third parties), it should be clearly documented as a requirement of the application (and of the logic layer as well), and then of course you’d need a custom exception type and use SingleOrDefault with a null check, throwing an instance of that custom exception when the situation occurs, but don’t overthink and create custom exceptions for all possible situations. On the other side, in some cases, such as if the end user is able to type the entity ID to compute the result for herself, then not finding the entity can be a normal behavior of the application service, not an exception at all: in this case you don’t need a custom exception either, and instead a more complex result can indicate that the computation couldn’t be performed because the entity requested doesn’t exist (e.g. changing the result type from the original output type to a tuple of the original type and an error status).

I know I might start a fight here as the topic is complex and many opinions would probably be different. I can only state that mine has been obtained by experience rather than just following rules and pattern found in software design books. I know my solution is not good in all cases, but for me it has been good enough most of the time. Anyway, I can only advise you to decide for yourself every time you need to – considering all involved aspects, and not only some of them!

Advertisements

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in .NET. Bookmark the permalink.

Add a reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s