CRUD Class Library Design to return useful business logic failure messages, which is not exceptional

I am creating a basic CRUD library that I expect to use in both local (add link) and wcf (add service link).

What are the best return types for the Create, Uupdate, and Delete parts (which have more complex business rules) for installing CRUD?

I want to be able to minimize feedback, but I also want to provide my clients with meaningful information about when the operation failed with my business logic, but is technically justified (thus, this is not an exception).

Take, for example, CRUD for the Person class, which has the following fields: FirstName, MiddleName LastName, and Date of Brith. First, Last and DOB, but Middle is not.

How can I pass the failures of business logic back to the client? I.E. Msgstr "You must specify a value for FirstName."

  • Is this where I should throw exceptions? (he does not want an exceptional case, so I think not, but I could be wrong).
  • Should I use the void and "out" option? If so, what type should be?
  • Should I use the return type of the object and put there data about what is happening?
  • Any other option that I missed completely?
+6
design c # class-design
source share
6 answers

1.Is this where I should throw exceptions? (this does not seem like an exceptional case, so I think not, but I could be wrong).

Personally, I believe that you should return the object with the result, as well as any verification errors, and not exclude an exception for data verification, whether due to lack of information (format verification) or business logic verification. However, I propose to exclude exceptions for errors that are not related to the data itself, that is: if the database data failure is invalid with valid data, etc.

My thinking here is that rejecting the test is not an "exceptional event." I personally feel that everything a user can ruin by simply not entering enough / correct data / etc is something that is not exceptional - this is standard practice and should be handled by the API directly.

Things that are not related to what the user is doing (for example, network problems, server problems, etc.) are exceptional events and guarantee an exception.

2. Should I use the void and "out" option? If so, what type should be?

3. Should I use the return type of the object and put there data about what is happening?

I personally prefer the third option. "out" parameters are not very significant. In addition, you will want to return more than one status of information from this call - you will want to return enough information to mark the corresponding properties as invalid, as well as any complete information about the operation.

This will probably require a class that contains at least a commit status (successful / unsuccessful format / unsuccessful business logic / etc), a list of mappings for properties-> errors (i.e. IDataErrorInfo style information) and potentially a list of errors that are not tied to a specific property, but rather relate to the business logic of the operation as a whole or a combination of the proposed property values.

4. Any other option that I missed completely?

Another option that I really like is checking validation in a separate assembly at the business processing level. This allows reuse of client-side validation logic.

The nice thing is that you can greatly simplify and reduce network traffic. The client can pre-check the information and send only data on the wire, if it is valid.

The server can receive good data and check it, and also return only one result of the commit. I believe that in this case there should be at least three answers: success, failure due to business logic or failure due to formatting. This gives security (you do not need to trust the client) and gives client information that it is not processed properly, but avoids the transmission of both bad information from client-> server and verification data from server-> client, therefore it can drastically reduce traffic

The verification layer can then (securely) send information to the CRUD layer for submission.

+1
source share

For efficiency reasons, an initial input check should be done at your client level. (i.e. don’t bother sending bad data over the wires.) At the client level, failure to verify input would, in fact, be a very expected problem that should not throw exceptions. However, if you install this β€œearly” check at the client level, a data validation error occurring at any deeper level can reasonably be considered an unexpected problem, so throwing exceptions for data validation errors at these levels will not be inappropriate.

+3
source share

You may find this blog post by Rob Bugby interesting; it describes how to implement a repository for processing CRUD operations, and, in your opinion, specifically, how to implement a check by returning the "RuleViolation" collection to the client in case of a problem.

http://www.robbagby.com/silverlight/patterns-based-silverlight-development-part-ii-repository-and-validation/

[edit] This is a case for me to throw an exception: if a user is required to create a user name and no name is specified, the caller did not use the correct arguments and does not use the method in the intended order. Invalid value InvalidArgumentException.

+1
source share

You really need to check the implementation of the validation rules in Rocky Lhotka in the CSLA framework .

NOTE. I did not say that it is necessary to use its framework in bulk, since it has some connection problems that violate some SRP efforts in the latest .NET development trends.

But its infrastructure uses "automatic" notification to the user interface level and integration with validation error messages with support for Web / Winforms controls.

+1
source share

Edit: I should point out that I would usually have validation material abstracted to the business layer that would process validation and call CRUD methods after successful validation.

One way to solve this problem would be to return a response class containing all the information that your library consumer will need to evaluate what happened and what to do next. A very simple example of a class that you could use would be something like this:

public class Response<T> where T:BusinessObject { public Response(T oldOriginalValue, T newValue) { } /// <summary> /// List of Validation Messages /// </summary> public List<ValidationMessage> ValidationMessages { get; set; } /// <summary> /// Object passed into the CRUD method /// </summary> public T OldValue { get; private set; } /// <summary> /// Object passed back from the CRUD method, with values of identity fields, etc populated /// </summary> public T NewValue { get; private set; } /// <summary> /// Was the operation successful /// </summary> public bool Success { get; set; } } public class ValidationMessage { /// <summary> /// Property causing validation message (ie FirstName) /// </summary> string Property { get; set; } /// <summary> /// Validation Message text /// </summary> string Message { get; set; } } 
+1
source share

The discussion about returning the results structure with the details of failure and throwing an exception can be generalized to "Can the requested action be successfully completed?" You are developing a library and how the library reports errors.

If the library is prompted to create a User object but cannot, because the username failed to validate, you can pass the empty user along with verification failure messages and hope that the client code validates the return value. My experience (DCOM pre ATL flashbacks) with having to check return values ​​for error codes is that it's easy to get smug (or lazy) and skip it.

From what you have described, using exceptions is out of the question. Define a parent exception for all types of exceptions that your library can use. Thus, clients do not need to include a large list of helper exceptions if they do not want to. An example of this is a SqlException.

+1
source share

All Articles