2

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?

4
  • 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. Commented 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
    Commented 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. Commented Oct 16, 2018 at 14:12
  • You could return 1001 objects to the client. The client still presents only the first 1000 but the presence of #1001 indicates that there is [at least one] object more to come.
    – Phill W.
    Commented Feb 22 at 11:48

2 Answers 2

2

Throwing an exception is 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 of 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 cannot return 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. A more natural 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 callers can decide by themselves whether the operation shall be stopped after 1000 objects, or after 10 seconds, or if the user should be asked interactively in this case, if the operation shall just be continued, or if something different 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.

2

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
    Commented Oct 16, 2018 at 13:16

Your Answer

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

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