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.