Best practice to retrieve error message from database - c#

We have list of error messages in our database table and we fetch these error messages from table when we face some business validation error.
for e.g. If in c# code we find that the calculated risk % is more then allowed value, when use below code
string sError = GetErrorText(6610); // get error message from application cache
DisplayErrorPopup(sError ); // load a popup to display error to user.
Now we have found that there are scenarios where we have to validate stuff few rules from stored Procedure. for e.g. "No active supervisor for worker."
My question is how should we handle this scenario when validation happens in database?
A. Should we return error text "No active supervisor for worker." as out param of SP and pass it to DisplayErrorPopup
OR
B. Return the error id (which is present in table) and then use GetErrorText(834) and then pass the text to DisplayErrorPopup;
My concerns are
1. Is there any industry standard to best practice to handle error messages and texts.
2. Is there any drawback of returning string / varchar from database when we have option of returning number.
Please guide me on this.

I would suggest throwing (raising) a custom error in SQL (you could select the text out of a table if you like) and letting your application catch that error. This will allow you to let your application decide how to handle different errors based on how critical they are.
using (SqlConnection conn = new SqlConnection(connection))
{
SqlCommand sqlCommand = new SqlCommand(query, conn);
sqlCommand.CommandTimeout = timeout;
sqlCommand.CommandType = CommandType.Text;
conn.Open();
object result = sqlCommand.ExecuteScalar();
return result;
}
Running the above inside of a try catch block will allow you to handle your errors more elegantly inside of your C# app

Both your options are equally correct.You can either return an error text from database, or return some error code and display error message based on that code in your application.
Also, try this atricle from codeproject as it has both industry standard and best practice to handle error messages and texts explained in detail.

Related

Simple SqlCacheDependency

Almost every tutorial I have read seems to incorrectly setup SqlCacheDependency. I believe they normally mix up the outdated polling method with the query notification method.
Here are two of many examples:
Web Caching with SqlCacheDependency Simplified (non-microsoft)
SqlCacheDependency Class (Microsoft)
Based on my testing, if you are using the broker (MSSQL 2015+) you don't need to make any .config changes nor do you need to make any SqlCacheDependencyAdmin calls (Don't need to define tables, etc).
I simplify just do this...
SqlDependency.Start(connString)
...
queryString = "SELECT ...";
cacheName = "SqlCache" + queryString.GetHashCode();
...
using (var connection = new SqlConnection(connString))
{
connection.Open();
var cmd = new SqlCommand(queryString, connection)
{
Notification = null,
NotificationAutoEnlist = true
};
var dependency = new SqlCacheDependency(cmd);
SqlDataReader reader = cmd.ExecuteReader();
try
{
while (reader.Read())
{
// Set the result you want to cache
data = ...
}
}
finally
{
reader.Close();
}
HostingEnvironment.Cache.Insert(cacheName, data, dependency);
}
(The code that checks if the cache is null or not is not included, as that's all just setup. I just want to show the setting of the cache)
This seems to work without the need to define which tables are involved in the query and make complicated triggers on each table. It just works.
More surprising to me is that the rules for making a query have notification :
Creating a Query for Notification (Can't find documentation newer than 2008) don't seem to apply. I purpose to do a TOP in my SQL and it still works.
For a test, I have it run a query 1000 times involving a table named "Settings". Then I update a value in the table and repeat the query.
I watch the Profiler for any queries involving the word "Settings" and I see the query is executed just 1 time (to set the cache) and then the update statement occurs, and then the query is re-executed one more time (the cache was invalidated and the query ran again)
I am worried that in my 2-3 hours of struggling with the proper way to do this I am missing something and it really is this simple?
Can I really just put any query I want and it'll just work? I am looking for any pointers where I am doing something dangerous/non-standard or any small print that I am missing
var dependency = new SqlCacheDependency(cmd);
when you write query like this you autiomatically define table name in it.Your connection already has db name.
It is non explicit way to do same.
Explicit way to catch exception and to know what went wrong is this.
// Declare the SqlCacheDependency instance, SqlDep.
SqlCacheDependency SqlDep = null;
// Check the Cache for the SqlSource key.
// If it isn't there, create it with a dependency
// on a SQL Server table using the SqlCacheDependency class.
if (Cache["SqlSource"] == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Northwind", "Categories");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
try {
SqlCacheDependencyAdmin.EnableNotifications("Northwind");
}
// If the database does not have permissions set for creating tables,
// the UnauthorizedAccessException is thrown. Handle it by redirecting
// to an error page.
catch (UnauthorizedAccessException exPerm) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
try {
SqlCacheDependencyAdmin.EnableTableForNotifications("Northwind", "Categories");
}
// If a SqlException is thrown, redirect to an error page.
catch (SqlException exc) {
Response.Redirect(".\\ErrorPage.htm");
}
}
// If all the other code is successful, add MySource to the Cache
// with a dependency on SqlDep. If the Categories table changes,
// MySource will be removed from the Cache. Then generate a message
// that the data is newly created and added to the cache.
finally {
Cache.Insert("SqlSource", Source1, SqlDep);
CacheMsg.Text = "The data object was created explicitly.";
}
}
else {
CacheMsg.Text = "The data was retrieved from the Cache.";
}
As documented in https://learn.microsoft.com/en-us/dotnet/api/system.web.caching.sqlcachedependency?view=netframework-4.8 "Using a SqlCacheDependency object with SQL Server 2005 query notification does not require any explicit configuration."
So, the CMD has explicit table names in it, and ADO.net is issuing the correct Service Broker configuration commands for you. When the table is updated, SQL Server posts a Service Broker message saying the table has been updated. When ADO.net validates the CMD it checks the explicit tables in the broker for updates.
This is why the SQlCacheDependency associated CMD must use explicit tables.

Sending time to SQL Server Management Studio table but it does not display?

I am currently writing an application which involves a user being able to write the time to a database by clicking a button. The problem is that the data will be send to the database table, but it does not show the time in SQL Server Management Studio.
This is my query:
{
string query = "insert into Sign_In_Out_Table(Sign_In)Values('"+ timetickerlbl.ToString()+ "')";
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.AddWithValue("#SignIn", DateTime.Parse (timetickerlbl.Text));
//cmd.ExecuteNonQuery();
MessageBox.Show("Signed in sucessfully" +timetickerlbl);
con.Close();
}
The datatype in SQL Server is set to datetime.
I'm open for suggestions to find a better way to capture the PC's time and logging it in a database.
Don't wrap the variable in ' when you are setting value with Parameters.Add(), or Parameters.AddWithValue() as they would wrap if needed.
The variable in here would be the value of Sign_In and not the Sign_In itself.
Always use Parameters.Add() instead of Parameters.AddWithValue():
string query = "insert into Sign_In_Out_Table(Sign_In) Values(#value)";
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.Add("#value", SqlDbType.DateTime).Value = DateTime.Parse(timetickerlbl.Text);
Edit (Considering your comment):
If still it does not insert it, of course there is an error in your code, it could be a syntax error, invalid table or column name, connection problem ,... so put your code in a try-catch block (if it isn't already) and see what error you you get, it should give you a hint:
try
{
//the lines of code for insert
}
catch(Exception ex)
{
string msg = ex.Message;
// the above line you put a break point and see if it reaches to the break point, and what the error message is.
}
Your table does not contain your timestamp because you have commented the execution of your query. Presumably you added the comment because this line was throwing an error, remove the comment and share the error with us.
cmd.ExecuteNonQuery();

Ajax webmethod fails to fire a stored procedure on the first attempt only

I have a webmethod that fails to fire a stored procedure during the first attempt only (I guess while establishing a new connection). I get Internal error server as an error Ajax message. However if I hit return from the URL address then the stored procedure is executed and pages work perfectly well, as they should. Then again if I keep the page inactive for a while and then try to establish a new connection the same problem occurs.
I spent the last 2 days trying to identify the problem by checking the parameters, removing most of the codes from the webmethod, and finally I was able to trace the problem to this stored procedure.
CREATE PROCEDURE [dbo].[spUnionServices]
#freetext NVARCHAR(50),
#offset int,
#fetch int
AS SET NOCOUNT ON;
BEGIN
SELECT IDphoto AS IDservice, photos.IDuser, photoCaption AS serviceTitle, photoDate as serviceDate, photoFileName AS servicePhoto, 'Photo' AS service,
photoPublished as servicePublished, inap, hashtags, KEY_TBL.RANK, screenName AS IDname
FROM photos INNER JOIN FREETEXTTABLE(photos, (photoCaption), #freetext) AS KEY_TBL ON photos.IDphoto = KEY_TBL.[KEY]
INNER JOIN editorDetail ON editorDetail.IDuser = photos.IDuser
ORDER BY RANK DESC OFFSET #offset ROWS FETCH NEXT #fetch ROWS ONLY
END
And here is how I connect to the stored procedure from the webmethod
StringBuilder SBsmallSquares = new StringBuilder();
SqlConnection sqlConnection1 = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
using (sqlConnection1)
{
SqlCommand cmd = new SqlCommand();
SqlDataReader ReaderPopup;
cmd.CommandText = "spUnionServices";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConnection1;
cmd.Parameters.AddWithValue("#offset", offset);
cmd.Parameters.AddWithValue("#fetch", fetch);
cmd.Parameters.AddWithValue("#freetext", fts);
sqlConnection1.Open();
ReaderPopup = cmd.ExecuteReader();
if (ReaderPopup.HasRows)
{
while (ReaderPopup.Read())
{
//creating the string to return. Here there is no problem.
}
return SBsmallSquares.ToString();
}
else return string.Empty;
I would appreciate it if someone could find out why I'm having this problem during the first attempt to run the stored procedure. Thanks!
You should retry on this error. During availability events (upgrades for example) a DB can move to a different machine and fdhost needs to pair with the SQL instance again. Usually the pairing process happens on the first free text query or when needed. You may be seeing timeouts in that process for the first time. It should help you on retry. if this is very consistent there could be a bug in the service and let know. I will help you debug this further.
I would put this in a comment but it's easier to write the code here in an answer.
I realize you said you are getting the error in ajax but it is occurring server side and being propagated back to your Ajax call. Because you do not have any exception handling in your code IIS is wrapping the exception for you using a generic exception with minimal detail.
You need to catch the exception server side and examine it, this should give you more detail and hopefully with that insight figuring out why it is occurring and then fixing it should be much easier.
StringBuilder SBsmallSquares = new StringBuilder();
SqlConnection sqlConnection1 = null;
try
{
sqlConnection1 = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand();
SqlDataReader ReaderPopup;
cmd.CommandText = "spUnionServices";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = sqlConnection1;
cmd.Parameters.AddWithValue("#offset", offset);
cmd.Parameters.AddWithValue("#fetch", fetch);
cmd.Parameters.AddWithValue("#freetext", fts);
sqlConnection1.Open();
ReaderPopup = cmd.ExecuteReader();
if (ReaderPopup.HasRows)
{
while (ReaderPopup.Read())
{
//creating the string to return. Here there is no problem.
}
return SBsmallSquares.ToString();
}
else return string.Empty;
}
catch (Exception ex)
{
// write this out to a text file OR (if you can) examine the exception in the debugger
// what you need are
// 0. ex.StackTrace <- the full call stack that generated the exception, now you know what method call caused it
// 1. ex.GetType().FullName <- the full type of the exception
// 2. ex.Message <- the message in the exception
// 3. ex.InnerException <- if this is not null then we need the type and message (1 and 2 above) again, this can recurse as many times as needed (the ex.InnerException can have an ex.InnerException.InnerException and that one can also have an InnerException, and so on)
// 4. some exceptions have additional details in other properties depending on the type, if you see anything usefull get that too
throw; // rethrow the exception because you do not want to ignore it and pretend everything succeeded (it didn't)
}
finally
{
if(sqlConnection1 != null) sqlConnection1.Close();
}
EDIT
It is also possible to update the web site configuration in IIS (and the web.config I believe) to pass through the complete exception and the details. I am not sure if its just sql server that is hosted for you in Azure or also IIS. In the later case I am not familiar with how to configure it to allow this but it might be possible. You can sometimes also specify whether to only allow complete exception/error details when running on local host which is great when you are debugging as you want to see exception information when troubleshooting but you would prefer that the outside world (outside of your company) did not see any sensitive internal details.
You can google this for more information on how to enable it, if you are running IIS in Azure then add a keyword about Azure to the search. Google: iis show exception
My guess is that there is a session authentication scheme that is not being initiated. You should try to perform a get first to allow cookies to be set in the browser. Alternatively have a closer examination of the cookies that are set on the successful attempt and work to align this if the information is available at the point of page generation.

How can I handle oracle exceptions using Enterprise Library in C#?

I'm building an ASP.NET web application using C#, Enterprise Library and Oracle for a database. This is the first time I'm using Oracle as the database and so far my experience hasn't been all that great (I prefer using MS SQL Server). So here's the scenario:
The users have the ability to enter and change the value of a unique field in the database (e.g.- username). If the user enters a value (in this context, a username) which has been already entered as a different record then the requirement is to the inform the user as such. Since the inserting and/or updating is done via stored procedure I decided to handle the exception within the store procedure. I did so by declaring an out parameter called erCode of type NUMBER and added the following the block to the stored procedure after the SQL statement.
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
erCode := SQLCODE
In the event the INSERT/UPDATE operation was successful I return erCode with value 0.
Naturally since there was a return value an exception was never caught in the try-catch block of the code in the data access layer (DAL) of the application. Therefore I returned the value of erCode from DAL to the business logic layer (BLL) and then to the UI layer where I handled it inside the try block by:
if (errorCode = -1)
{
lblMsg.Text = "Username already exists";
}
else
{
lblMsg.Text = "Username changed successfully";
}
I realise this is a horrible way to do this even if the oracle error code is a value apart from "-1", then the Username changed successfully will be shown which would be completely misleading.
I'm not supposed to use the System.Data.OracleClient.OracleException class since its not looked upon favourably when we use that class in adjacent with Enterprise Library at my work place. If we are using Enterprise Library, then we should use that alone for all database related functions.
This bring me to my question; how can I handle such a scenario? Is it possible to do it using Enterprise Library alone?
If you need to take the code as what you are doing and still be on the safe side, then you can do like this:
When no error is encountered, then if I assume that ErrorCode
returned will be a null value, you may do this:
if(erCode==null)
errorCode = 1;
Setting ErrorCode =1 will certainly tell that operation was
success.
Modify your code as:
if (errorCode = -1)
{
lblMsg.Text = "Username already exists";
}
else if(errorCode = 1)
{
lblMsg.Text = "Username changed successfully";
}
else{
lblMsg.Text = "Unknown error while updating!";
}
So if there will be any other error code, relevant message is
shown!
Hope you find it worthy!

Checking whether a database is available?

My problem involves checking if I have a valid database connection before reading from the database. If the database is down I'd like to write to a xml file instead. I have the location of the database (if it's up) at runtime so if the database was working I can create a new sqlConnection to it.
Use a typical try...catch...finally structure, and based on the specific exception type and message, decide whether you want to write to xml or not.
try
{
SqlConnection connection = new SqlConnection(DB("Your DB Name"));
connection.Open();
}
catch (Exception ex)
{
// check the exception message here, if it's telling you that the db is not available. then
//write to xml file.
WriteToXml();
}
finally
{
connection.Close();
}
I would just use something like:
using(SqlConnection conn = new SqlConnection(c)) {
conn.Open();
}
It will throw an exception if invalid. You could write to the xml in the exception.
An easy way would be to execute a simple query and see if an error occurs:
For Oracle:
SELECT * FROM DUAL
For SQL Server
SELECT 1
Basicly just some kind of relatively "free" query that will let you know that the database is up and running and responding to requests and your connection hasn't timed out.
You cannot really tell whether the DB is up and running without actually opening a connecting to it. But still, connection might be dropped while you're working with it, so this should be accounted for.

Categories