3

While there is a similar question focused on Java, I've been in debates with utilizing Task objects. What's the best way to handle returns on CRUD methods (and similar)?

Common returns we've seen over the years are:

  • Void (no return unless there is an exception)
  • Boolean (True on Success, False on Failure, exception on unhandled failure)
  • Int or GUID (Return the newly created objects Id, 0 or null on failure, exception on unhandled failure)
  • The updated Object (exception on failure)
  • Result Object (Object that houses the manipulated object's ID, Boolean or status field to with success or failure indicated, Exception information if there was one, etc)

The concern comes into play as we've started moving over to utilizing C# 5's Async functionality, and this brought the question up of how we should handle CRUD returns large-scale. In our systems we have a little of everything in regards to what we return, we want to make these returns standardized...

Now the question is what is the recommended standard? Is there even a recommended standard yet? (I realize we need to decide our standard, but typically we do so by looking at best practices, see if it makes sense for us and go from there, but here we're not finding much to work with)

  • 2
    Is there a reason why code being asynchronous would change what you want to return from it? – Doval Aug 19 '14 at 14:57
  • The only evidence I've found is void is discouraged with certain async patterns that would otherwise be fine, but since we're not finding much in general perhaps it does or doesn't matter at this point, I've not found anything really saying one way or another. Edit: other than we still need to set a standard regardless of if Async is a note worthy factor. – RualStorge Aug 19 '14 at 15:01
  • 1
    This seems to be what you're looking for. – Doval Aug 19 '14 at 15:15
  • 1
    Since the only special case is void, your question boils down to "what should I return for CRUD operations" which is in my opinion a different question that should be asked separately. – Doval Aug 19 '14 at 17:01
  • 1
    @RualStorge If the non-async version returns T, the async version should return Task<T>. Whether T should be int or bool or whatever has nothing to do with async. – svick Aug 20 '14 at 15:00
2

After a lot of searching around the net as much as I could here's the end results I've come up with based on my research.

Things to avoid

  • Avoid using void with async methods. Voids don't pass exceptions up to the calling method which can lead to hard to debug async code. (this can be mitigated, but is still discouraged)
  • Avoid using Booleans with Create/Insert Method. returning the key is just as effective if all you care about is if it worked or not, plus there are many times you'll need that key immediately after the Create/Insert

Things to Consider

  • For all CRUD methods (except read) using a "Result Object" or "Façade Object" can make sense depending on your structure. If you take advantage of the object for stuff like error handling it can be beneficial, if not it'll probably be just a waste of time and resources.

  • Create / Insert Methods returning the key field is preferable to bool or void.

  • Update Methods returning bool is preferable to key or void if the update is a separate method, if it's an upsert treat it like an insert.

  • Delete Methods returning bool is preferable to void, key is fine, but often doesn't mistake

  • Read is a given in it should return whatever data was requested.

Personal opinion

Only use Result or Façade objects if you're going to use them for stuff like error handling, otherwise.

  • Create / Insert / Upsert : key field
  • Update : Boolean
  • Delete : Boolean
  • Read : Requested Data
  • Thanks for coming back and sharing the results of your research – Eric King Aug 20 '14 at 17:58
  • I think you could get some more feedback if you're not so quick to accept this answer (Unless you don't want any more help.). – JeffO Aug 21 '14 at 16:08
  • I'd be more than happy to accept additional input, I answered because it seemed like no one else was really responding. I'll unanswer it for a bit to see if anyone else wants to chime in. By all means I'd like whatever information can be provided (as we are in a perfect point to set a new standard for ourselves) and this could save others research later. – RualStorge Aug 21 '14 at 17:03
0

You may find that certain methods are more common because certain data saving requirements are more common in applications than any type of standardization. We don't have error checks because it was mandated by some coding authority, we have error checks because we run into problems when errors aren't handled. "Of course the application crashed, you entered a null."

Being consistent is important, but that doesn't mean do everything the same. This depends on the functional requirements of your application. Differentiate what is truly different.

Example: Save v. Save & Close

Obviously these are different, but do you consider the "Save" part of the functionality to be the same? If so, they both do the same thing on the Save part, and then the close. What about from the data storage perspective? Are they the same?

If I'm going to Save and it is a new record, it would be convenient to return the newly generated primary key/id from the database (Assuming all saves do that, which they may not.) so the application can stick with that record and do any updates as necessary. However, if I'm going to save and close, is returning the ID: necessary, a waste of resources or no different for all practical purposes, so let's be consistent with our saves.

There are times (way too many) when we don't have perfect data. You'll have hours of fun playing "Who changed the db?" You can decide to have the application check to make sure only one record was updated and throw a false if not. Maybe your app isn't handling concurrency (that will never happen) in the best way, so you just updated a record another user deleted.

Return things you need when you need them. Requirements will change. Try to decide upfront if you're going to throw your own exceptions-nobody likes surprises. If things are truly the same, be consistent, but don't try to handle different requirements the same way.

  • I get what you're saying here... But generally I think it makes a lot of sense to standardize these methods to a point. By all means unique methods will need to return all sorts of things, but when you get down to shared code such as Inserting an object to the database. A standard just seems invaluable to me... Sure at the business code what I return to the front end could change dramatically, but from the repositories to the business code? – RualStorge Aug 19 '14 at 18:50
  • I agree you should generalize where you can but not at the expense of those things that either will take too much work-around to fit in the general structure, drastically hinder performance or just not get the job done. – JeffO Aug 21 '14 at 16:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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