Is it bad practice to catch the "happy path" in an exception? - c#

I have an SQL statement that checks to see if a value is in my database or not. I want to respond with the "happy path" if the value is not in the database.
I have found using DbDataReader (.NET) that if a SELECT query can't find the value it throws an exception - so my "happy path" ends up in the exception, not in the main try block.
I can always say "NOT IN" but I don't want to return all of the rows in the database that don't have the value - as this would return many thousands of results where as all I want is just a "no it is not here" type response.
public void wristbandScan(string barcode)
{
string query = "SELECT ticket FROM tickets WHERE
linked_barcode='" + barcode + "'";
ValidTicketEventArgs args = new ValidTicketEventArgs();
try
{
var queryResult = _dbRunner.queryThis(query);
args.Result = false;
args.Message = "WB already linked";
args.Barcode = barcode;
OnValidTicketEvent(args);
}
catch (Exception e)
{
this.updateWristband(barcode);
this.updateValid();
args.Result = true;
args.Message = "WB linked";
args.Barcode = barcode;
OnValidTicketEvent(args);
}
}
It feels wrong to me to catch the happy path in an error statement, but I do not want the lag associated with reading in all the rows with the NOT IN statement.
Is there a better way to do this or is this approach acceptable best practice?

Well, you don't have to fetch all the records to the client; let's extract a method for this. Assuming that you work with MS Sql:
public bool hasScanCode(string barcode) {
if (string.IsNullOrWhiteSpace(barcode))
return false;
//DONE: paramterize queries
string query =
#"SELECT ticket
FROM tickets
WHERE linked_barcode = #prm_BarCode";
using (var conn = new SqlConnection(connection_string_here)) {
conn.Open();
using (var q = new SqlCommand(conn, query)) {
//TODO: q.Parameters.Add is a better choice
q.Parameters.AddWithValue("#prm_BarCode", barcode.Trim());
using (var reader = q.ExecuteReader()) {
// we read (fetch) at most 1 record
// if empty cursor - no record with given barcode
return reader.Read();
}
}
}
}
then we can use it:
public void wristbandScan(string barcode) {
bool result = hasScanCode(barcode);
ValidTicketEventArgs args = new ValidTicketEventArgs() {
Result = result,
Message = result ? "WB linked" : "WB already linked",
Barcode = barcode,
};
OnValidTicketEvent(args);
}
Please, remember - exceptions are for exceptional situations. Exceptions are very slow (stack unrolling wants resources); they are not readable - catch, in fact, works as a notorious goto; they are dangerous - in your current code you catch too many exceptions: e.g. AccessViolationException if it's thrown somewhere within dbRunner.queryThis will be efficiently masked.

Create and call a StoredProcedure which can to handle the empty situation and return no rows instead of an exception.
Then handle the no rows scenario outside the try/catch.

Related

Can't get a value out an ASP.NET hidden field

I am going through a YouTube video, to learn ASP.NET. I've gotten it to work for the most part, with a somewhat major caveat: I can't retrieve a value from a hidden field ID. Because of that, I don't have a value to send to a stored procedure to create or update.
The commented out line is the original statement. When I have that then execute
.ExecuteNonQuery, I get the following error:
Procedure or function 'ResourceCreateOrUpdate' expects parameter '#ResourceID', which was not supplied
When I try to display hfResourceID, I have nothing when trying to pass 0, for a create, or the ResourceID value, i.e. 1. That value however, doesn't get there. I know the stored procedure works because I can execute it in SQL Server Management.
I tried moving hfResourceID to a string, then a integer value, but I seem to be having problems creating the if/else: everything is marked as an error. When I hover over the lines, I get the following message, which pretty much leave me clueless:
"Embedded statement cannot be a declaration or labeled statement".
Would I be able to get any pointers on how to clear up my error, please? Thanks.
2017-10-13 # 10:38: code updated
<asp:HiddenField ID="hfResourceID" runat="server" />
protected void btnSave_Click(object sender, EventArgs e)
{
int intResourceID = 0;
bool boolIDHasValue = true;
try
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
catch (Exception ex)
{
lblErrorMessage.Text = ex.Message;
boolIDHasValue = false;
}
if (boolIDHasValue)
{
if (sqlconnODRConnection.State == System.Data.ConnectionState.Closed)
sqlconnODRConnection.Open();
SqlCommand sqlcmdCreateOrUpdate = new SqlCommand("ResourceCreateOrUpdate", sqlconnODRConnection);
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#ResourceID", intResourceID);
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#Status", txtStatus.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#FirstName", txtFirstName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#MiddleName", txtMiddleName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#LastName", txtLastName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#NickName", txtNickName.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#Gender", txtGender.Text.Trim());
sqlcmdCreateOrUpdate.Parameters.AddWithValue("#USCitizen", txtUSCitizen.Text.Trim());
sqlcmdCreateOrUpdate.ExecuteNonQuery();
sqlconnODRConnection.Close();
string strResourceID = hfResourceID.Value;
Clear();
if (strResourceID == "")
lblSuccessMessage.Text = "Saved Successfully";
else
lblSuccessMessage.Text = "Updated Successfully";
FillGridView();
}
}
There are a few issues with the code you copied from that video. But here a snippet as to how it should be done. I've added 3 ways to convert from the HiddenField value to an actual int. Which one you use can depend on how you want to handle errors, 0 values etc. Not included in the snippet, but I like to check for IsNullOrEmpty while using Trim(), that gets rid of spaces that might make the value non-convertible if (!string.IsNullOrEmpty(hfResourceID.Value.Trim())).
int intResourceID = 0;
//this will try to convert but you won't see exeptions when failed
Int32.TryParse(hfResourceID.Value, out intResourceID);
//checks if there is a value in the hiddenfield, but throws yellow screen if not convertible
if (!string.IsNullOrEmpty(hfResourceID.Value))
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
//catch an error when the value is not convertible, can be wrapped with !string.IsNullOrEmpty(hfResourceID.Value)
try
{
intResourceID = Convert.ToInt32(hfResourceID.Value);
}
catch (Exception ex)
{
//handle the error, can be seen with ex.Message
}
//if the hidden value is still 0 (for whatever reason) you might not want to execute the query
//so the next part will return and stop executing the rest of the code
if (intResourceID == 0)
{
return;
}
//update the database, using 'using' will ensure proper closure of the connection and disposing of any objects
using (SqlConnection connection = new SqlConnection("myConnectionString"))
using (SqlCommand command = new SqlCommand("ResourceCreateOrUpdate", connection))
{
//set the command type and add the parameters
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#ResourceID", SqlDbType.Int).Value = intResourceID;
try
{
//open the database connection and execute the command
connection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
//there was an error opening the database connection or with the command, can be viewed with ex.Message
}
}
Your error regarding the embedded statement is because you a declaring
if (strResourceID == "")
int intResourceID = 0;
else
int intResourceID = (Convert.ToInt32(hfResourceID.Value));
When decalring a variable directly after an If or else then you need your curly brackets. So...
if (strResourceID == "")
{
int intResourceID = 0;
}
else
{
int intResourceID = (Convert.ToInt32(hfResourceID.Value));
}
As for your other issues I would need to see your client side code.
Take a look on ASP code, specifically the way you are setting hidden field value. I guess it is properly marked as runat=server but maybe some problem is going on in your asp.net code, try to debug client code by using: console.log function and see the output in your console browser.

How parse Sql Script with dapper? (SET PARSEONLY ON + Dapper)

I want to check & parse sql script so I searched for that and I found something like this
SET PARSEONLY ON
SELECT * FROM [dbo].[Categories] --Query To Parse
I use dapper, so I write method like this
public bool IsValidSqlScript(string sqlScript)
{
using (SQLConnection)
{
using (SQLTransaction)
{
var status = SQLConnection.Execute("SET PARSEONLY ON " + sqlScript);
// OR
// var status = SQLConnection.Query("SET PARSEONLY ON " + sqlScript);
}
}
return status;
}
How I can get status and if exists any errors get error`s list also ???
SET PARSEONLY ON
SELECT * FR OM [dbo].[Categories] --Query To Parse
>>> false
>>> Msg 102, Level 15, State 1, Line 2 Incorrect syntax near 'FR'.
You're close, but you've got an issue with your status variable. You declare it inside your SQLTransaction using statement and then you try and return it outside of that scope.
You'll want to use a try/catch block to execute your query, that way you'll know when the sqlScript is valid or not. If you enter the catch block, it's invalid, and if you don't then it is valid.
The code should look similar to the following...
public bool IsValidSqlScript(string sqlScript)
{
bool status;
try
{
using (SQLConnection)
{
using (SQLTransaction)
{
SQLConnection.Execute("SET PARSEONLY ON " + sqlScript);
}
}
status = true;
}
catch(Exception e)
{
status = false;
)
return status;
}

How can I pause a SQLCE Query until the Table it is querying is no longer being accessed elsewhere?

I have a method that queries a table for the count of its records. QA has discovered an "edge case" where if a particular operation is canceled in a particular order and speed (as fast as possible), the GUI "forgets" about the rest of the records in that table (the contents of the tables are uploaded to a server; when each one finishes, the corresponding table is deleted).
To be clear, this table that is having records deleted from it and then queried for count ("workTables") is a table of table names, that are deleted after they are processed.
What I have determined (I'm pretty sure) is that this anomaly occurs when a record from the "workTables" table is in the process of being deleted when the workTables table is queried for the count of its records. This causes an exception, which causes the method to return -1, which in our case indicates we should cuase the GUI to not display those records.
Is there a way to check if a table is in the process of having a record deleted from it, and wait until after that operation has completed, before proceeding with the query, so that it won't throw an exception?
For those interested in the specifics, this method is the one that, under those peculiar circumstances, throws an exception:
public int isValidTable(string tableName)
{
int validTable = -1;
string tblQuery = "SELECT COUNT(*) FROM ";
tblQuery += tableName;
openConnectionIfPossibleAndNecessary();
try
{
SqlCeCommand cmd = objCon.CreateCommand();
cmd.CommandText = tblQuery;
object objcnt = cmd.ExecuteScalar();
validTable = Int32.Parse(objcnt.ToString());
}
catch (Exception ex)
{
validTable = -1;
}
return validTable;
}
...and this is the method that deletes a record from the "workTables" table after the corresponding table has had its contents uploaded:
private void DropTablesAndDeleteFromTables(string recordType, string fileName)
{
try
{
WorkFiles wrkFile = new WorkFiles();
int tableOK = 0;
DataSet workfiles;
tableOK = wrkFile.isValidWorkTable(); // -1 == "has no records"
if (tableOK > 0) //Table has at least one record
{
workfiles = wrkFile.getAllRecords();
//Go thru dataset and find filename to clean up after
foreach (DataRow row in workfiles.Tables[0].Rows)
{
. . .
dynSQL = string.Format("DELETE FROM workTables WHERE filetype = '{0}' and Name = '{1}'", tmpType, tmpStr);
dbconn = DBConnection.GetInstance();
dbconn.DBCommand(dynSQL, false);
populateListBoxWithWorkTableData();
return;
} // foreach (DataRow row in workfiles.Tables[0].Rows)
}
}
catch (Exception ex)
{
SSCS.ExceptionHandler(ex, "frmCentral.DropTablesAndDeleteFromTables");
}
}
// method called by DropTablesAndDeleteFromTables() above
public int isValidWorkTable() //reverted to old way to accommodate old version of DBConnection
{
// Pass the buck
return dbconn.isValidTable("workTables");
}
I know this code is very funky and klunky and kludgy; refactoring it to make more sense and be more easily understood is a long and ongoing process.
UPDATE
I'm not able to test this code:
lock (this)
{
// drop the table
}
...yet, because the handheld is no longer allowing me to copy files to it (I get, "Cannot copy [filename.[dll,exe] The device has either stopped responding or has been disconnected" (it is connected, as shown by ActiveStync))
If that doesn't work, I might have to try this:
// global var
bool InDropTablesMethod;
// before querying that database from elsewhere:
while (InDropTablesMethod)
{
Pause(500);
}
UPDATE 2
I've finally been able to test my lock code (copies of binaries were present in memory, not allowing me to overwrite them; the StartUp folder had a *.lnk to the .exe, so every time I started the handheld, it tried to run the buggy versions of the .exe), but it doesn't work - I still get the same conflict/contention.
UPDATE 3
What seems to work, as kludgy as it may be, is:
public class CCRUtils
{
public static bool InDropTablesMethod;
. . .
if (CCRUtils.InDropTablesMethod) return;
CCRUtils.InDropTablesMethod = true;
. . . // do it all; can you believe somebody from El Cerrito has never heard of CCR?
CCRUtils.InDropTableMethod = false;
UPDATE 4
Wrote too soon - the bug is back. I added this MessageBox.Show(), and do indeed see the text "proof of code re-entrancy" at run-time.
while (HHSUtils.InDropTablesMethod)
{
MessageBox.Show("proof of code re-entrancy");
i++;
if (i > 1000000) return;
}
try
{
HHSUtils.InDropTablesMethod = true;
. . .
}
HHSUtils.InDropTablesMethod = false;
...so my guess that code re-entrancy may be a problem is correct...

Update works only for last method call

I have a quite simple UPDATE statement and it works correctly but for only one call. I have few projects (FAIs) in database and I want to change responsible person.
If I want to change two or more responsibles it only changes the responsible person for the last project. I don't have any error messages. All values are correctly send to update query but the query just does not send it to the database.
public void updateFaiUBazu(string orderNumber, FAI fai, int serialNumber)
{
konekcija.Open();
MessageBox.Show(fai.Reviewer);
komanda = new SqlCommand("
update FAI set
AircraftFK = #AircraftFK, GlassFK = #GlassFK, PartNumber = #PartNumber,
SerialNumber = #SerialNumber, ReportNumber = #ReportNumber,
Reviewer = #Reviewer, Comment = #Comment, DateTime = #DateTime,
Iges = #Iges, IgesName = #IgesName, Status = #Status
where
AircraftFK = " + fai.AircraftFK1 +
" and GlassFK = " + fai.GlassFK1 +
" and SerialNumber = " + serialNumber,
konekcija);
try
{
komanda.Parameters.Clear();
komanda.Parameters.AddWithValue("#AircraftFK", fai.AircraftFK1);
komanda.Parameters.AddWithValue("#GlassFK", fai.GlassFK1);
// komanda.Parameters.AddWithValue("#OrderNumberFK", fai[i].OrderNumerFK);
komanda.Parameters.AddWithValue("#PartNumber", fai.PartNumber);
komanda.Parameters.AddWithValue("#SerialNumber", fai.SerialNumber);
komanda.Parameters.AddWithValue("#ReportNumber", fai.ReportNumber);
komanda.Parameters.AddWithValue("#Reviewer", fai.Reviewer);
komanda.Parameters.AddWithValue("#Comment", fai.Comment);
komanda.Parameters.Add("#DateTime", fai.DateAndTime);
if (fai.IgesFile.Length != 0)
{
komanda.Parameters.AddWithValue("#Iges", fai.IgesFile);
}
else
{
komanda.Parameters.Add("#Iges", SqlDbType.VarBinary, -1);
komanda.Parameters["#Iges"].Value = DBNull.Value;
}
if (string.IsNullOrEmpty(fai.IgesName))
{
komanda.Parameters.Add("#IgesName", fai.IgesName).Value = DBNull.Value;
}
else
{
komanda.Parameters.AddWithValue("#IgesName", fai.IgesName);
}
komanda.Parameters.AddWithValue("#Status", "Not Tested");
komanda.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
konekcija.Close();
}
Could that be some limitation with SqlServer update statement?
Follow a couple of steps to get to your problem.
Log yout update queries. JUst after you have created it. This will tell you if your updates are really executed when you are calling them from a loop.
If all your updates are logged, copy them to SSMS query window and execute them one by one in sequence. This would give you corect idea if there is anything wong with way queries are getting formed, or of there is any specific value in one of the parameters which is causing the problem. This will also help you identify if the problem specified by #Steve above exists.

Help with Exception Handling in ASP.NET C# Application

yesterday i posted a question regarding the Exception Handling technique, but i did'nt quite get a precise answer, partly because my question must not have been precise.
So i will ask it more precisely.
There is a method in my BLL for authenticating user. If a user is authenticated it returns me the instance of the User class which i store in the session object for further references.
the method looks something like this...
public static UsersEnt LoadUserInfo(string email)
{
SqlDataReader reader = null;
UsersEnt user = null;
using (ConnectionManager cm = new ConnectionManager())
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#Email", email);
try
{
reader = SQLHelper.ExecuteReader(cm.Connection,
"sp_LoadUserInfo", parameters);
}
catch (SqlException ex)
{
//this gives me a error object
}
if (reader.Read())
user = new UsersDF(reader);
}
return user;
}
now my problem is suppose if the SP does not exist, then it will throw me an error or any other SQLException for that matter. Since this method is being called from my aspx.cs page i want to return some meaning full message as to what could have gone wrong so that the user understands that there was some problem and that he/she should retry logging-in again.
but i can't because the method returns an instance of the User class, so how can i return a message instead ??
i hope i made it clear !
thank you.
There are a lot of approaches you could take here, an easy one would be to return null if you can't find an appropriate user object (because of the exception). then in the calling code you just test for null and if it is null display an error message.
for example
User u = LoadUserInfo(email);
if(u == null)
{
ErrorLabel.Text = "Could not log in.";
ErrorLabel.Visible = true;
//.... or some other notification
}
else
{
//... normal load
}
that would be a basic way to go about it.
There are lot of approaches you have but in your method you declared "SqlDataReader reader = null;" some times you will get error at if condition "if (reader.Read())" because you declared as null.
public static UsersEnt LoadUserInfo(string email)
{
SqlDataReader reader = new SqlDataReader();
UsersEnt user = null;
using (ConnectionManager cm = new ConnectionManager())
{
SqlParameter[] parameters = new SqlParameter[1];
parameters[0] = new SqlParameter("#Email", email);
try
{
reader = SQLHelper.ExecuteReader(cm.Connection,
"sp_LoadUserInfo", parameters);
if (reader.Read())
user = new UsersDF(reader);
}
catch (SqlException ex)
{
user.Exception=ex.Message;
}
}
return user;
}
and then
User us = LoadUserInfo(email);
if(us.Exception != null)
{
ErrorLabel.Text = "Could not log in.";
ErrorLabel.Visible = true;
//.... or some other notification
}
else
{
//... normal load
}
i think it will work.

Categories

Resources