0

In my MVC web application the controller calls a service layer which takes any relevant request parameters and builds the model for the view e.g.

Controller

var model = _modelService.Build(id);
return View("ViewName", model);

If there are multiple reasons why the model service will fail to build a valid model, how do I communicate that to the controller so it can return an appropriate response?

Below is a contrived example.

Model Service

var entity = _dataService.Get(id);
if (entity == null) {
  return null;
}

var entityMeta = _dataService.GetMeta(id);
if (entityMeta == null) {
  return null;
}

return new Model(entity);

Controller

var model = _modelService.Build(id);

// Null because of invalid id
if (model == null) { 
  return View("Invalid Id");
}

// Null because of missing meta data
if (model == null) {
  return View("Invalid meta data");
}
return View("ViewName", model);

In the example above there are two reasons why the model is null, but there is no way to inform the controller what has happened because all it gets is a null object back.

I can think of some possible solutions: throw an exception (which doesn't feel right - this isn't necessarily an unexpected outcome), move some of this logic to the controller (the general advise is to keep controllers slim), create a generic ModelResult object which contains the model or some information about why it has failed etc.

I feel like this is a common situation so there must be a standard pattern to apply.

4
  • 1
    You could do something similar to the DbEntityValidationException class and return a list of validation errors. An exception is fine here because most of the time you expect to receive valid input. Receiving invalid input can safely be called an exception.
    – Dan Wilson
    Oct 20, 2020 at 13:49
  • 1
    Why doesn't "throw an exception" feel right? That's exactly what exceptions were invented for: to communicate failure up the call stack. Oct 20, 2020 at 15:14
  • I'm talking about situations where it may be perfectly expected for there not to be a valid result. Maybe a search page is a good example where you wouldn't expect every search term to return valid results so you would be throwing exceptions all the time - is that an appropriate use?
    – AGB
    Oct 20, 2020 at 15:30
  • Also, I use App Insights in Azure for tracking errors and every exception is logged in there - if I use exceptions for non-exceptional situations then the logs become unwieldy.
    – AGB
    Oct 20, 2020 at 15:32

1 Answer 1

1

The simplest way is to beef up the service layer. So instead of the controller puzzling out the reason, you can have the result and errors/messages returned from the service layer.

var entity = _dataService.Get(id);
if (entity == null) {
  return null;
}

var entityMeta = _dataService.GetMeta(id);
if (entityMeta == null) {
  return null;
}

return new EntityOperationResult 
{
    Entity = entity,
    Messages = new List<Message>(),
};

Where an error might look like:

return new EntityOperationResult 
{
    Entity = null,
    Messages = new List<Message>() { new Message("Invalid foobar") }
};

This way, the service layer deals entirely with the entities and returns validation messages, but the controller can puzzle out what happened and instantiate the model from the entity.

3
  • 1
    If failures are expected then an exception is not the right pattern. Here we expect periodic failures. I use this "OperationResult" technique whenever I need to handle errors gracefully in the user interface. Handling exceptions is no so graceful for the user. +1 Oct 20, 2020 at 19:40
  • I'm going to go with something like this. The part I'm not totally comfortable with is communicating the "type" of error back to the other layers using magic strings or enums. At least with Exceptions it is strongly typed, but I'm not sure there is an alternative.
    – AGB
    Oct 20, 2020 at 21:14
  • 1
    AGB, you are free to strongly type the error portion of the Operation Result into anything you want. This might be a set of enums for each operation that has the potential to return something other than the expected type, for example.
    – Graham
    Oct 21, 2020 at 16:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.