I have an application that takes a ZIP code from the user which may or may not be valid for the selected state. I consider the case of an invalid ZIP code to be a rare issue that would really only result from a typo.
The relevant SQL tables are Quote and Address. Saving a quote is done in one database call, with all parameters for both tables being supplied to a stored procedure.
Currently in the case of an invalid ZIP code an exception is raised in the stored procedure, caught in C# at the data layer, and a custom InvalidZipCodeException is thrown. The custom exception is then caught at the UI layer and the user is notified of the error. I originally designed it this way to avoid an extra database call to check the validity of the ZIP code every time a quote is saved.
I've recently read some materials on data validation and realized I'm using exceptions to control logic flow here, which is generally frowned upon. It seems silly to me to make a separate database call just to validate the ZIP code when the overwhelming majority of cases involve valid data. I'd like some more educated opinions on whether my design is poor in this specific case.
As JohnLBevan suggests in the comments, it's really not worth reworking it if (1) it's already working OK and (2) it's consistent with how the rest of the application validates. While it is currently thought to be bad practice to control logic flow with exceptions, like anything else it's a judgement call.
You mentioned that every 5 digit number is not a valid zip code... I imagine you have a table that stores a map to validate. Just store that as a dictionary and validate against it client-side, if you end up changing the validation. For me, I would say, "If it ain't broke, don't fix it" in this case.
Related
Say I have an SQL table that has a unique key (or other) constraint on one of the columns.
Then, I have an application (lets go ASP.NET MVC) that allows a user to edit this column.
When the user attempts to save, and the constraint is/will be broken, I need show a user-friendly error message to the user. As such, which of the following is better practice?
Have the application query the database to ensure the constraints are not broken, and then insert/update the row (results in two queries to the db)
OR
Immediately attempt to perform the insert/update, and catch the SqlException should the constraint be broken. I like this, because of only one trip to the db, however how should you then extract which index/constraint was broken and affix the appropriate message? (apart from inspecting Exception.Message?)
1 is the better all-around solution. Db calls are not the only expensive operation, exceptions are costly too. You should really only let your db throw an exception when something goes wrong due to your code, not a user action.
For example, you might later want to install Elmah and get exceptions logged and/or mailed to you. Elmah would log/mail such an exception unless you explicitly told it not to.
Plus like you said, exceptions don't always have the most businessy information to communicate to users (especially SQL exceptions, which are SQL-specific). You have these unique and other constraints for a reason. For those reasons, validate the information before trying to store it.
Take twitter, gmail, etc. When you go to get a username, the application first checks to see if it is taken. These are uniqueness constraints at the application level, which may or may not ultimately be realized as SQL constraints. Since it is your application that faces the user, you should not try to communicate to them by translating from SQL to English.
The problem with option 1 is that it requires the application to have separate distinct knowledge of what the constraints are, which conflicts with a DRY principle, and could lead to problems later when the database constraint gets changed, and the application is not updated - a tight coupling between the data logic, and the application. Also, there is no guarantee of the constraint checks that were performed are still valid at the subsequent pont when you then attempt the update.
This doesn't preclude augmenting your application with some UI layer validation to aid the user before the commit is attempted.
So if that is discarded we are left with option 2, and some translation is necessary somewhere in the application. Where you put that translation and that logic, whether in a Stored Procedurce or a Business Logic layer, is up to you.
I am going to handle a project to a client for a testing phase, the project build with ASP.NET MVC3 .. What I need is to save all exceptions occurs to a persistent location -SQL Database-
and I have some questions..
what field should I save in the database? (Msg, trace, etc)
I have seen people save the inner exception but sometimes my exceptions have null inner exception.
What is the best place to handle the error and save to the DB. (In global.asax OR defining a custom error page in web.config and get the last error with server.getLastError)
what field should I save in the database? (Msg, trace, etc)
Everything you can, if possible:
Type
Message
Stack trace
I have seen people save the inner exception but sometimes my exceptions have null inner exception.
And some will have more than one, nested. You should save everything you can, IMO. How you structure that is up to you, but you could always add the innermost one, then the containining one with a foreign key to the innermost one, etc, working your way outwards.
What is the best place to handle the error and save to the DB.
Can't help you on that part, I'm afraid. You may want to consider doing this asynchronously though, queuing them up. This is particularly relevant when the exception may itself be a database error - if you can build up a queue (with a maximum size, of course) so that once any database problems have been restored, the errors can then be stored, that could be useful. Or potentially dump them to disk first, and periodically upload that log to the database.
As Jon Skeet said, the more fields you save the more data you will have to diagnose the problems as you go. Personally, I'd go for serializing the exception and putting it in the "XML" type column if you have the ability to do so (database space and performance considerations). This would also eliminate the second problems since the inner exception will be serialized also. Though with this approach all your custom exceptions should be able to serialize themselves correctly.
I'd recommend looking at freely available source codes for error loggers such as Elmah to get the basic idea.
I am writing some data access code and I want to check for potentially "invalid" data states in the database. For instance, I am returning a widget out of the database and I only expect one. If I get two, I want to throw an exception. Even though referential integrity should prevent this from occurring, I do not want to depend on the DBAs never changing the schema (to clarify this, if the primary key constraint is removed and I get a dupe, I want to break quickly and clearly).
I would like to use the System.IO.InvalidDataException, except that I am not dealing with a file stream so it would be misleading. I ended up going with a generic applicationexception. Anyone have a better idea?
InvalidDataException seems pretty reasonable to me:
The name fits perfectly
The description fits pretty reasonably when you consider that it's effectively a data "stream" from the database
Nothing in the description mentions files, so I wouldn't be worried about that side of things
You're effectively deserializing data from a store. It happens to be an RDBMS, but that's relatively unimportant. The data is invalid, so InvalidDataException fits well.
To put it another way - if you were loading the data from a file, would you use InvalidDataException? Assuming you would, why should it matter where the data is coming from, in terms of the exception being thrown?
If you need an exception that would exactly describe the situation you're dealing with, why not make your own exception?
Just inherit it from System.Exception.
I might be tempted to use one of the following:
InvalidConstraintException
NotSupportedException
OverflowException
Or, just go ahead and create my own: TooManyRowsException
You could write a custom exception if you do not find any suitable standard-exception ...
But, you say:
Even though referential integrity
should prevent this from occurring, I
do not want to depend on the DBAs
never changing the schema.
When someone changes the DB schema, changes are pretty big that you'll have to make some modifications to your application / data-access code as well ...
I would like to know what are the best practice programming tasks in relation to users submitting data through a web form to a website.
I am particularly interested in any C# or VB.NET commands that should be used through out the process from the moment the user hits the submit button until the data hits the database.
I have been reading about reasons why you may want to take precautions such as SQL injections etc.
Avoiding SQL injections is quite simple - just use parameterized queries, or an ORM such as LINQ to SQL or nHibernate (which all use parameters under the hood). The library takes care of everything for you, and has been thoroughly vetted.
After that, you're safe until it's time to write the data back out to other users. You always want to store the data as close to the original user input as possible. Another way to say this is - don't store a scrubbed version (unless you also store the original alongside it). Scrubbing is a one-way process - it destroys information. It's always easy to scrub again if you need to, but you can't un-scrub something.
However, storing the original format means you do need to make sure you encode the output before you write it to the browser. This prevents users from putting malicious cross-site scripts and other things into your data that might be rendered on other users' pages.
At the highest level, just keep in mind that all the work should be done as late as possible. Be liberal in what you accept (do only what is necessary to protect yourself) and strict in what you send (encode everything, scrub the hell out of it, transform it, etc). You want to have a "pure" copy which is altered to conform to the target output.
If you are serious about it read this book: 19 Deadly Sins of Software Security
Using linq2sql you get protection from SQL injection. Alternatively use .Parameters with parametrized queries.
When you send the data back on the page, you have to prevent the data from running js by encoding it. Use http://msdn.microsoft.com/en-us/library/w3te6wfz.aspx
And overall consider any of use of that data a chance for attack and look for ways to prevent it. For example, using user data as a filename to access/save something can mean access to unintended resources (by adding ..\).
You can't go wrong with the following general rules
Validate everywhere! Where you validate determines the quality of the user experience. The closer to the user, the less safe it is but more responsive. The farther away, the safer but tends to give worse error messages.
Validate at the front-end to give the user a responsive error.
Validate in the middle to give the user nicer error messages.
Validate in the database (constraints and such) to keep your database sane.
Use parameters early, and use them often! Find those square pegs early.
Coerce data into the correct types as quickly as possible. (This is a form of validation.) If something is an int, don't handle it like a string.
Don't throw away errors when checking parameters. If your regex doesn't match, or your try { parse } catch { } gets triggered it's important you know why and don't continue!
Whether you use LINQ or roll-your-own SQL: do not build SQL statements with user-supplied data. EVER. Use parameterized queries and/or stored procedure calls. If you must piece-together SQL as strings, don't do it with user data. Get the "untrustworthy" data stored and manipulate it as needed later, in a separate query.
Encode all data passed to the user. The bad data may not be their fault, don't trash their world.
Assume that anything they pass you is full of JavaScript and HTML. Assume that "binary" data will find its way in. Someone will run your web page on something other than a "browser" eventually. Your "phone number" field will be used to store an .EXE.
Return all data encoded and harmless. Don't assume that "because it's in the database" (or that it's an int, or that it's just a 1 character string) that it's harmless.
Assume that eventually your database will fail you somehow. A developer will drop in "test" data, you'll miss an edge case above, or something may run amok and insert all-purpose crap. This crap has to be passed to the user safely.
Nobody's perfect: especially you. Plan for that.
While pretty much all of the guidelines on the Open Web Application Security Project (OWASP) site are useful, here are their guidelines on data validation.
I work on a database application written in C# with sql server as backend. And for data integrity, I try to enforce as much as possible on database level - relations, check constraints, triggers.
Due to them, if data is not consistent, the save / update / insert can fail, and app throw SqlException.
I do various validations both in UI (to present user with meaningful info if data entered is not valid), also in BL, which reports errors back to UI which presents it to user.
However, there are things that really cannot be checked in the app, and are handled by the db: I mean errors on delete when no cascade delete and user try to delete a entity from a master table, etc.
E.g. Employees table acts as master in lot of relations - manager of employee, manager of department, cashier, team leader, teams members etc, etc. If I add anew employee which is not involved in any relation I can delete it, but of user try to delete one that is master oin such relation, the delete fails (as it should) due to RI rules enforced at DB level, and that's ok.
I write delete code in a try ... catch and handle the exception, telling user he cannot delete that employee. But I want to give user more meaningful info - the reason the record cannot be deleted. Maybe it was just a test employee record, which was also added to a test team. But user forget where added that and if I could tell "Cannot delete employee because it is part of team T1", user will know to go first to Team T1, remove user then try to delete it again. That's a simple example, since as I said an employee can be involved in lot of relations - in my app I have at least 20.
The solution is to display the Message reported by SqlException, but that's not elegant at all. First, that msg is very technical - it talks about FK, PK, Triggers, which are meaningless for users and will scare them. Second my app is uses multi-lang UI and all menus and msgs are shown in user selected language (selected either at login time or in user profile). And the msg from SqlException is english (if I use english version) or worst, less common languages, like german or dutch, if it happens that sql server is in that language.
Is there any common or recommended approach to extract meaningful info from sql exception to be able to present user a meaningful msg (e.g. what relation or child table caused the failure, or what trigger, etc). but something I can test in program in a lang-independent fashion and then format my own error msg in a user-friendly way?
How do you handle this situation?
Thanks for all answers
(PS: Sorry for the long post)
Unfortunately, there isn't an easy answer here.
The amount of work involved will depend on the consistency of your error messages coming from your business layer. You are going to need to do some form of translation from the "technical" error message to your user oriented message.
This should be a matter of making some forms of lookups from your error messages to a resource key, which can be used to pull out your language-specific error message. However, if you need to parse the messages for more information (ie: table names, etc), then it gets a bit trickier. In that case, you'll probably need to have something that maps an error message to a regex/processor of some form as well as a new resource string. Then you could format the user's string with the information you extract from the original error, and present it to the user.
Well, from the database, you'll only ever get these technical messages, e.g. "violation of foreign key relation FK_something_to_another" or such.
Typically, in the SqlException, you also get a SQL error code or something else.
The best approach would probably be to have a separate table in your database which basically maps those technical SQL errors that can happen to more meaningful, user-oriented messages. E.g. if your SQL error says something like "fk violation blablabaal", you could have an entry in your "UserErrorTable" which maps this to a user message saying "could not delete user (this.and.that), most likely because ..... (he's still member of a team)" or whatever.
You could then try to catch those SqlExceptions in your business layer, translate those technical infos into a custom exception for your users, put in a user-friendly message, and stick the technical exception into the .InnerException of your custom exception type:
public class UserFriendlyException : Exception
{
public string UserErrorMessage { get; set; }
public UserFriendlyException(string message, SqlException exc) : base(message, exc)
{
UserErrorMessage = MapTechnicalExecptionToUserMessage(exc);
}
}
Marc
The short answer is "don't." Let the errors bubble out to the global error handling/logging. The validation and business rules should generally preclude database exceptions, so you are better off failing hard and fast and not submitting dirty data. Transactions help too.
Error Messages do not equate Exceptions. An error message is something that the user should find informative and most important actionable. There are some guidelines around error messages in the User Experience Guidelines I recommend you read up. Apple also has good generic guidelines in Writing Good Alert Messages.
You'll immediately notice that most, if not all, of SQL errors are not good end user messages. 'Constraint FKXF#455 violation' - Bad error for end user. 'Filegroup is full' - Bad error for end user. 'Deadlock' - same. What good applications do is they separate the roles of users. Administrators need to see these errors, not end users. So the application always logs the full SQL error with all the details, eventually notifies the administrator, and then display a different error to the user, something like 'A system error occurred, the administrator was notified'.
If the SQL error can is actionable by the end user, then you can display him an error message instruct him what to do to fix the problem (eg. change the invoice date in the input to satisfy a constraint). But even in this case most times you should not display the SQL error straight to the user ( you already seen a very good reason why not: localization). I understand that this creates a much bigger workload for your development team. You must know what errors are user actionable in each case, from the multitude of errors you may catch. That is well known, and that's exactly why good program managers know that about 80% of the code is handling the error cases, and why the application being 'done' usually means is 20% done. This is what differentiates great apps from ordinary ones: how they behave when things go wrong.
My suggestion is to start up with the principle of progressive disclosure. Display an generic error message stating 'the operation has failed'. Offer to display more details if the user presses a 'Show details...' button on the error message dialog, and display the SqlError collection (btw, you should always log and display the entire SqlError collection of SqlException.Errors, not the SqlException). Use the SqlError.Number property to add logic in the catch block that decides if the user can do anything about the error (decide if the error is actionable) and add appropriate information.
Unfortunately there is no pixie dust. You are touching the foot hils of what is probably the most difficult part of your project.
The way to do this is to write a stored procedure, and use TRY and CATCH. Use RAISERROR to raise your own custom messages, and check the error code in the SqlException.
We usually write some sort of translator in our projects.We compare the SQL exception message with some predefine patterns and show the equivalent message to user
About generic technical > userfriendly errors i can only support the answers already giving.
But to your specific example with the employer i must encourage you to not rely only on the SqlException. Before trying to delete the Employee you should make some check to see if he/she is a part of any teams, is manager etc. It will tremendiosuly improve the usability of your application.
Pseudo:
Employee e;
try {
IEnumerable<Team> teams = Team.GetTeamsByEmployee(e);
if (teams.Count() > 0) {
throw new Exception("Employee is a part of team "+ String.Join(",", teams.Select(o => o.Name).ToArray());
}
IEnumerable<Employee> managingEmployees = Employee.GetEmployeesImManaging(e);
if (managingEmployees.Count() > 0) {
throw new Exception("Employee is manager for the following employees "+ String.Join(",", managingEmployees.Select(o => o.Name).ToArray());
}
Employee.Delete(e);
} catch (Exception e) {
// write out e
}
Errors happen. When it doesn't particularly matter what kind or type of error you hit, or how you handle it, slapping your code in a TRY...CATCH... block and write a generic error reporting system is fine. When you want (or are required to) write something better than that, it can take serious effort, as outlined in some prior posts (which I too have upvoted).
I tend to classify errors as anticipatable or unanticipatable. If you can anticipate an error and you want to handle it with a clear an consice message, such as with your "delete employee" situation, you will have to plan and code accordingly. By their definition, you cannot do this for unanticipatable errors--that's usually where TRY/CATCH comes in.
For your situation, one way could be before deleting the row, check via queries against the child tables whether or not the delete could succeed. If it won't, you'll know precisely why (and for all child tables, not just the first one that would block the delete), and can present the user with a clear message. Alas, you must consider whether the data can change between the check and the actual delete -- perhaps not a problem here, but it could be in other situations.
An alternative based on the TRY...CATCH... would be to check the error number within the catch block. If it's a "can't delete due to foreign key", you could then query the children tables and generate the message, and if it was some other unanticipated error, you'd have to fall back on a generic message.
(A caveat: some times when it hits an error, SQL will raise two error messages in a row [is the FK constraint violation one of them?] and in these situations the various ERROR() functions will only return data for the second and invariably less useful message. This is incredibly aggravating, but there's not too much you can do about it.)
In summary I would caution against relying on SQL exceptions and errors. For me, if we rely on such errors we are storing up trouble. Such error messsages are normally not user readable. In addition UI developers may say "oh well, it will be caught by the database guys, I do not need to validate that". Totally incorrect!
Perhaps a better approach would be to ensure that validation prevents these issues in the first place. e.g. If you cannot delete object A because it is referenced by object B, then we should implement some kind of dependency service at a level higher than the database. It depends what kind of application it is.
Another example would be string length validation. Do we really want to rely on the database validation for checking string length of fields entered by the user. Of course, this is an extreme case!
Normally, if the database layer is throwing an exception it can be a sign of a bug elsewhere. With a client/server setup we can assert that validation is the responsibility of both. If it makes it to the database then normally it is too late.
You may want to use RAISEERROR so the calling code can be fed with an exception from a stored procedure. From that you can provide a sensible error message.
There is never a one size fits all approach. My motto here would be prevention is better than cure! Validate earlier rather than later.