Let's build an error handling aspect: part 2
In the last post, I laid out the bare essentials of an error handling aspect for my service layer.
Before I proceed further with the aspect, let's examine the layer right above the service layer: the UI layer. I'm using ASP.NET MVC.
With ASP.NET MVC, there is a concept of ModelState, which holds information about validations performed during model binding, and is also used by the controller to convey error information to the view (through the Html.ValidationSummary helper, for instance). I'm already using ModelState to convey simple validation errors; I'd like to use it to convey any errors that the service layer might encounter. There are many ways to approach this; I've chosen the "callback" approach--passing a lambda to the service layer in order to communicate error information from the service layer up to the UI layer. Here's the basics:
Dependency injection gives me an object that implements the ITerritoryService interface, but I have to take one more step and pass that service the lambda x => ModelState.AddModelError("", x) in the controller instructor. This is because ModelState doesn't actually get instantiated until the controller does (so I can't pass it to the service object any sooner).
Now, when there's an exception in Territory service, I can simply use this "callback" to add an error to the ModelState. (Side note: a benefit to this approach is that it does not couple TerritoryService to my UI: if I wanted to use this service with another UI--say, WebAPI or WebForms--then I would simply have to pass in a different lambda).
Let's get the aspect to use that callback.
By using args.Instance, we can get a reference to the object in which the method resides. If that object can be cast to ITerritoryService, then we can simply call AddModelError. This works great: now anytime there's an exception in TerritoryService, it will be added as an error to ModelState and displayed to the user.
But clearly there are still issues here:
- This only works for ITerritoryService--what about the other services?
- Swallowing exceptions: we should at least be logging the exception, not just ignoring it.
- If the method has a return type, then it's returning null. If it's meant to return a reference type, that's probably fine, but what if it's returning a value type?
- What if it's returning a collection, and the UI assumes that it will get an empty collection instead of a null reference?
- Do we really want to show the exception message to the user?
Next time, we'll start addressing the first issue, and figure out a way to make this aspect more generic.