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?

asked Oct 15, 2018 at 19:43
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? 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. Commented Feb 22, 2024 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.

answered Dec 15, 2018 at 8:49
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.

answered Oct 15, 2018 at 20:27
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. Commented Oct 16, 2018 at 13:16

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.