3

I have an Asp.net MVC application and sometimes I want to give a descriptive error message to the user, but the condition of it is discovered at the Data Access Layer (DAL). So for example, I have a conditional search functionality based off form field input from the user. The DAL will fetch the results, and store and return a list of the model objects from the result.

Now, I don't want more than 1000 objects returned, so if more than 1000 objects are created, I break out of the loop and would like to give feedback to the user that the 1000 row result cap was met.

What is the way to handle these sort of conditional errors/informational messages from the DAL? Would passing a List to the DAL work? Or would I throw a custom exception/error code to the business layer (or controller)? Or maybe a Singleton to store the errors of the three layers?

3
  • I think you need to respect a separation of concerns a bit more, and that in turn will answer your question. So, more specifically, you say that your DAL has information useful to the UI. I'd suggest it doesn't - that information is useful to your MVC app/web api, and then your app will have information more relevant to what the UI expects in a response. Rather than propagate DAL errors/messages right up to the UI let the app catch it, and then relay on that information sensibly to the UI. Oct 16, 2018 at 11:24
  • @JᴀʏMᴇᴇ, thank you. I have always been kind of fuzzy in this area, and I am starting to dive into good practices for design/architecture. So when you mention let the app catch it, are you thinking a custom exception? Would limiting the return to 1000 objects be considered an error to toss to the next layer? Or if informational, should an exception still be used?
    – eaglei22
    Oct 16, 2018 at 13:12
  • Whatever makes sense to your business layer, and then to your UI layer. I've just dealt with something similar this morning - there is a constraint in my DB. If an exception is thrown when attempting to write to the DB in contradiction of the constraint, the business layer captures this. But then to the UI my 'business layer' relays the information as "you cannot add duplicate ABC if XYZ". How you deliver that depends on the architecture of your system. Oct 16, 2018 at 14:12

2 Answers 2

1

Include some additional data with your payload.

Example:

{ result: Ok,
  infos: [],
  warnings: [],
  errors: []
  data : [ List of data objects goes here ]
}

Then the UI can inspect any info, warning, or error messages and display them as necessary. Your 1000 message cap limit could be an info or warning message.

1
  • That's a good idea but I wasn't sure if encompassing it all together was a good idea, or if it was better to abstract out the error handling.
    – eaglei22
    Oct 16, 2018 at 13:16
0

Throwing an exception is typically the canonical choice for errors which occur in a lower layer (like the DAL), but need to be handled in a way which depends on the context the higher layers (like the decision about what "a descriptive message" is or how it should be displayed to some user).

However, an exception may not be appropriate when you just want to signal a warning, but still want to return an operational result from a function. For example, when your "1000 row result cap" was met, you may want to return the 1000 objects found so far. If you think of a function call like

      List<MyObject> result=dal.SelectObjects(someCondition);

then by throwing an exception the function won't even get the partial result of that operation. So for this cases, an additional error message may be more appropriate, like this:

      List<MyObject> result=dal.SelectObjects(someCondition, out someErrorObject);

This works whenever it is fine to get the error messages or warnings at the end of the operation. (Of course, you could also combine the function result and the error information into one object, as shown in Jon Raynor's answer).

There are situations where you need to return warnings or information to the user already during the running operation, not just at the end. For example, let us assume your operation can detect when the query delivers more than 1000 objects, but you don't want to stop the operation then, but to send a signal back to the UI whenever the next 1000 objects (or a certain number) are processed - for example, to trigger a progress indicator. Then you could use a delegate to return information from the DAL to some higher layer, with this signature:

    // "callback" will be called with the number of objects processed in some interval
    List<MyObject> SelectObjects(string someCondition, Action<int> callBack)

Of course, this looks a little bit contrieved. The better design choice for this case is to utilize delayed evaluation using an IEnumerable:

    IEnumerable<MyObject> SelectObjects(string someCondition)
    { 
          // some loop here, using "yield" to return the result
    }

Now the caller can decide by himself if the operation shall be stopped after 1000 objects, or after 10 seconds, or if the user should be asked interactively in this case, if if the operation shall just be continued, or if something shall happen in between.

So as you see, there are several ways of keeping the DAL completely UI or BL agnostic, nevertheless returning error conditions, warnings, or intermediate information back to the higher layers. You just have to pick which of those techniques are appropriate for your case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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