asp.net/sql server try catch what is going on? - asp.net

I identified a bug in my code and I'm baffled as to how it could have occurred in the first place.
My question is, I found a skip in the database ID fields (it is an incremented identity field), indicating some records weren't inserted - which means my SQL sprocs (probably) threw an error. Thinking backwards from there, that means that my business object should have caught that error and thrown it, exiting immediately and returning back to the page's codebehind (instead it continued right on to the second stored procedure). I don't understand why or how this is possible. What's going on in respect to the code execution skipping my try catches?
Page code behind:
protected void submitbutton_click(object sender, EventArgs e){
try{
mybusinessobject.savetodatabase()
} catch( Exception ex) {
Response.Redirect("Error.aspx");
}
}
business object code:
public static void savetodatabase(){
int ID1=-1;
int ID2=-1;
//store the billing contact
SqlCommand cmd1 = new SqlCommand("SaveInfo1", con);
cmd1.CommandType = CommandType.StoredProcedure;
//...
cmd1.Parameters.Add("#Ret", SqlDbType.Int);
cmd1.Parameters["#Ret"].Direction = ParameterDirection.ReturnValue;
try
{
con.Open();
cmd1 .ExecuteNonQuery();
ID1 = Convert.ToInt32(cmd1.Parameters["#Ret"].Value);
}
catch (Exception ex) { throw ex; }
finally { con.Close(); }
if (ID1 > 0)
{
SqlCommand cmd = new SqlCommand("SaveInfo2", con);
cmd.CommandType = CommandType.StoredProcedure;
//...
try
{
con.Open();
cmd.ExecuteNonQuery();
ID2= Convert.ToInt32(cmd.Parameters["#Ret"].Value);
}
catch (Exception ex) { throw ex; }
finally { con.Close(); }
}
}
SQL Code:
PROCEDURE [dbo].[SaveInfo1]
(
-- ... parameters ...
)
AS
INSERT INTO Table1 ( ... ) Values ( ... )
RETURN SCOPE_IDENTITY
PROCEDURE [dbo].[SaveInfo2]
(
-- ... parameters ...
)
AS
DECLARE #SpecialID INT
INSERT INTO Table2 ( ... ) Values ( ... )
SET #SpecialID = SCOPE_IDENTITY()
INSERT INTO Table3 ( [ID], ... ) Values ( #SpecialID, ... )
RETURN SCOPE_IDENTITY()

Your exception handling is horrible. Never do this:
catch (Exception ex) { throw ex; }
All that accomplishes is to screw up the stack trace in the exception. It makes it look like the exception originated at the point of the throw.
Never do this:
try{
mybusinessobject.savetodatabase()
} catch( Exception ex) {
Response.Redirect("Error.aspx");
}
You don't know what exception happened. You have no idea whether or not it's safe to redirect, and on top of it all, you lose all information about what the exception was!
You should also get into the habit of implementing using blocks:
public static void savetodatabase()
{
using (SqlConnection con = new SqlConnection("Connectionstring"))
{
int ID1;
//store the billing contact
using (SqlCommand cmd1 = new SqlCommand("SaveInfo1", con))
{
cmd1.CommandType = CommandType.StoredProcedure;
//...
cmd1.Parameters.Add("#Ret", SqlDbType.Int);
cmd1.Parameters["#Ret"].Direction = ParameterDirection.ReturnValue;
con.Open();
cmd1.ExecuteNonQuery();
ID1 = Convert.ToInt32(cmd1.Parameters["#Ret"].Value);
}
if (ID1 <= 0)
{
return;
}
int ID2 = -1;
using (SqlCommand cmd = new SqlCommand("SaveInfo2", con))
{
cmd.CommandType = CommandType.StoredProcedure;
//...
con.Open();
cmd.ExecuteNonQuery();
ID2 = Convert.ToInt32(cmd.Parameters["#Ret"].Value);
}
}
}
A using block will ensure that the resource will have its Dispose method called, whether or not an exception is thrown.

Isn't the more likely scenario that someone just deleted some records from the table?

If records are deleted, their unique identifiers will not be recycled, even when new records are later inserted. You can use RESEED in SQL to reset the identity seed to 0 if you desire, but I suggest against that unless you wipe the table. Otherwise you could end up with primary key violations.
Also, make sure your column's identity seed is set to increment 1 at a time.

Your code doesn't matter, just go to Web.config and play with appropriate node:
<customErrors mode="On|Off" />
P.S.
Use the using clause to auto-close a connection, instead of manual in the finally clause

you can test the catch. just change the procedure:
PROCEDURE [dbo].[SaveInfo1]
(
-- ... parameters ...
)
AS
INSERT INTO Table1 ( ... ) Values ( ..., some_out_of_range_value_here, ....)
RETURN SCOPE_IDENTITY()
to have some hard coded out of range value (so the insert fails), and then run your application...

Related

How to get last incremented id in SQL with single query

My requirement I inserted successfully I want to bind last increment id to the root folder file name.id was automatic incremented in SQL. I want to bind last incremented id on that bold part place.
This is my code please help me to solve this problem:
string insert = "insert into Articles values('" + html+ "','" + text + "')";
try
{
con.Open();
SqlCommand cmd = new SqlCommand(insert, con);
int i = cmd.ExecuteNonQuery();
if (i > 0)
{
using (StreamWriter file = new StreamWriter(System.Web.Hosting.HostingEnvironment.MapPath(#"~\Articles\**ID**.html"), true))
{
file.WriteLine(value.editor); // Write the file.
}
return msg;
}
else
{
return msg1;
}
}
catch (Exception ex)
{
}
finally
{
con.Close();
}
Please note that your code is a security risk as it's vulnerable to sql injection attacks as Sean Lange rightfully wrote in the comments.
Also, the empty catch is a problem as he pointed out. Do yourself a favor and never ever use empty catch blocks.
To get the last generated identity value in the current session you should use Sql Server's SCOPE_IDENTITY() function.
Note that if you have an instead of insert trigger on the table SCOPE_IDENTITY() will not give you the correct value.
Your code should look something like this:
string insert = "insert into Articles values(#html, #text); select scope_identity()";
using (var con = new SqlConnection("<YOUR CONNECTION STRING HERE>"))
{
using (var cmd = new SqlCommand(insert, con))
{
cmd.Parameters.Add("#html", SqlDbType.NVarChar).Value = html;
cmd.Parameters.Add("#text", SqlDbType.NVarChar).Value = text;
try
{
con.Open();
var databaseId = cmd.ExecuteScalar();
if (databaseId is int)
{
using (StreamWriter file = new StreamWriter(System.Web.Hosting.HostingEnvironment.MapPath(string.Format(#"~\Articles\{0}.html", databaseId)), true))
{
file.WriteLine(value.editor); // Write the file.
}
return msg;
}
else
{
return msg1;
}
}
catch (Exception ex)
{
// Write to log, show an error message to the user
}
}
}

For Tuncating table what statement is used?

I have created one sp that tuncate table.Table name is dynamic.Here I used dynamic sql.Sp is working fine.I want to execute that sp from C#.net(from cs file).
I know executenonquery returns no of row affected.executenonquery is used for insert,update and delete command.Exectesclare is used for select which has only one cell.EceuteReader is used for selecting multiple record.What shall i use that tell my tuncate table clause executed properly or not?
You can use ExecuteNonQuery to truncate the table.
try
{
string connectionString = ConfigurationManager.ConnectionStrings["MyConnection"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string commandText = "TRUNCATE TABLE myTable";
using (SqlCommand command = new SqlCommand(commandText, connection))
{
command.ExecuteNonQuery();
lblStatus.Text = "Table Deleted Successfully.";
}
}
}
catch(Exception ex)
{
lblStatus.Text = "Table can not be deleted, Error " + ex.Message;
}
I suppose the best way is to use ExecuteNonQuery.
1. It returs the number of rows affected by the statement.
2. And if something doesn't work properly, you will get a nice SqlException which won't go by unnoticed.
try
{
procedure.ExecuteNonQuery();
nextStep.ExecuteWhatever();
}
catch(SqlException e)
{
Console.WriteLine("Oh noes!");
}

update multiple table using one query in SQL SERVER

I am working on asp.net(c#) project with SQL SERVER 2008. I want to update three tables using one query. Please suggest me how to do that. thnaks
You can't. Update statement works for a single table. You have to write three different queries for three tables.
You can use transaction to make sure that your update statements are atomic.
BEGIN TRANSACTION
UPDATE Table1
Set Field1 = '1';
Where Field = 'value';
UPDATE Table2
Set Field1= '2'
Where Field = 'value';
UPDATE Table3
Set Field1= '3'
Where Field = 'value';
COMMIT
For C# you can use SqlTransaction. An example from the same link (bit modified)
private static void ExecuteSqlTransaction(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = connection.BeginTransaction("SampleTransaction");
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText =
"UPDATE Table1 Set Field1 = '1' Where Field = 'value';";
command.ExecuteNonQuery();
command.CommandText =
"UPDATE Table2 Set Field1= '2' Where Field = 'value'";
command.ExecuteNonQuery();
command.CommandText =
"UPDATE Table3 Set Field1= '3' Where Field = 'value'";
command.ExecuteNonQuery();
// Attempt to commit the transaction.
transaction.Commit();
Console.WriteLine("Both records are written to database.");
}
catch (Exception ex)
{
Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
Write three queries and put them all into a single transaction, either in a stored procedure or using TransactionScope from your c# code.
using System.Transactions;
using( var ts = new TransactionScope() ){
// execute your queries
ts.Complete();
}
Full example here: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx
One of the way will be create a procedure and inside that procedure update all the table. You can also use transaction if you want to insert all the three or non of them. Here is an example
You can also maintain transaction from C#. here is an example
Transaction Stored Procedure C#
Example
using (var connection = new SqlConnection("your connectionstring"))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
using (var command1 = new SqlCommand("SP1Name", connection, transaction))
{
command1.ExecuteNonQuery();
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}

Database Queries don't run but neither are the SQL Exceptions Thrown

I am running C# .NET code where I need to test certain values that have been entered in a web page form and then passed to the server. The server then uses those values
in a query string to return a value. I encase these query instructions within a try-catch block in order to trace any Sql exceptions.
The problem is this:
Once the application has started, the connection string is set, and the query is run, I don't get a stack trace from the catch block's SQL Exception but instead
I just get a blank/empty value within the method that ran the query. The method will return a Boolean variable to indicate if there was any value read from the query and if so it returns true however it always returns false which should not happen because I have checked the query string that it builds by pasting it into MS SQL 2008's Query Console and running it. The results from running the pasted SQL instruction test does produce non-null data from the query. Thank much for your help.
I'm running VS2003 with IIS 6.0 and using MS SQL 2008 Enterprise Studio
Here is the code segment for the web.config and C# code. Thanks much for your help.:
<system.web>
<!-- DYNAMIC DEBUG COMPILATION
Set compilation debug="true" to enable ASPX debugging. Otherwise, setting this value to
false will improve runtime performance of this application.
Set compilation debug="true" to insert debugging symbols (.pdb information)
into the compiled page. Because this creates a larger file that executes
more slowly, you should set this value to true only when debugging and to
false at all other times. For more information, refer to the documentation about
debugging ASP.NET files.
-->
<compilation defaultLanguage="c#" debug="true" />
====================
//This method takes one string and returns either country name or Canadian state as a string, according to query.
private string candStateCountryCheck(string strQuery)
{
string strStateCountry = "";
SqlConnection con = null;
SqlCommand cmd = null;
SqlDataReader sdr = null;
try
{
string strConnection = ConfigurationSettings.AppSettings["OLEDBConnectionString"];
con = new SqlConnection(strConnection);
con.Open();
cmd = new SqlCommand(strQuery, con);
sdr = cmd.ExecuteReader();
if(sdr.Read())
{
strStateCountry = sdr.GetString(0);
}
}
catch (Exception exc)
{
ErrorLabel.Text = "ERROR:" + exc.Message;
}
finally
{
if (sdr != null)
sdr.Close();
if (cmd != null)
cmd.Dispose();
if (con != null)
con.Close();
}
return strStateCountry;
}
This should work, but I think your should use ExecuteScaler for single result queries. I also encourage you to use parameterized queries:
while (sdr.Read())
{
strStateCountry = sdr.GetString(0);
}
ExucuteScaler example:
try
{
string strConnection = ConfigurationSettings.AppSettings["OLEDBConnectionString"];
using(SqlConnection con = new SqlConnection(strConnection))
{
con.Open();
using(SqlCommand cmd = new SqlCommand(strQuery, con))
{
strStateCountry = (String)cmd.ExecuteScalar();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
I'll try to avoid anything here that won't work with VS2003, but it's gonna be hard... VS2003 is getting pretty old now, and you know that the express edition of VS2010 is available for free, right?
Also, this is a re-write of your code to show a better example of how it should look, if it were using a fictional database. You did not share any of your database structure, any example data, or what a query might look like, so that's the best we can do for now:
private string candStateCountryCheck(string toTest)
{
string sql = "SELECT countryStateName FROM SomeTable WHERE ItemKey LIKE #Query + '%';";
try
{
string strConnection = ConfigurationSettings.AppSettings["OLEDBConnectionString"];
con = new SqlConnection(strConnection);
cmd = new SqlCommand(sql, con);
cmd.Parameters.Add("#Query", SqlDbType.VarChar, 50).Value = toTest;
con.Open();
return cmd.ExecuteScalar().ToString();
}
catch (Exception exc)
{
ErrorLabel.Text = "ERROR:" + exc.Message;
}
finally
{
cmd.Dispose();
con.Dispose();
}
}
You NEVER want to write methods that expect fully-formed sql code as arguments.

Data not being written to Database ASP.Net

I am building a Forum in ASP.Net but have a small problem.
I have 1 user who creates a topic and can write a post to it fine, but if another user logs in, it won't insert the post into the database. It's returning that it does but nothing inserts. The original user can login and still post but no-one else can.
this is my code in behind
protected void addPostBtn_Click(object sender, EventArgs e)
{
//Define ADO.NET objects.
string insertSQL;
string topic = Request.QueryString["topicid"].ToString();
insertSQL = "INSERT INTO Posts (TopicID, PostBody, PUserID)"
+ "VALUES (#Topic, #NewPostText, #PUserID)";
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(insertSQL, con);
// Try to open the database and execute the update
int added = 0;
try
{
cmd.Parameters.AddWithValue("#Topic", topic);
cmd.Parameters.AddWithValue("#NewPostText", newPostText.InnerText);
cmd.Parameters.AddWithValue("#PUserID", Session["User_ID"]);
con.Open();
added = cmd.ExecuteNonQuery();
lblResults.Text = "Your post has been added";
}
catch (Exception err)
{
lblResults.Text = "Error inserting record. " + err.Message;
}
finally
{
con.Close();
}
if (added > 0)
{
this.BindRepeater();
}
}
I don't get any errors at all. It says it submitted fine, but it's not in the database unless the original poster does it.
EDIT:
Just realized it's to do with my view. This is my current view that it's reading from
SELECT dbo.Posts.PostBody, dbo.Posts.PostDate, dbo.Posts.PostID, dbo.[User].username, dbo.Topic.TopicID
FROM dbo.Topic RIGHT OUTER JOIN
dbo.Posts ON dbo.Topic.TopicID = dbo.Posts.TopicID LEFT OUTER JOIN
dbo.[User] ON dbo.Topic.TUserID = dbo.[User].UserID AND dbo.Posts.PUserID = dbo.[User].UserID
But it's returning NULL now for the other users names
The view needs to be as follows
SELECT dbo.Posts.PostBody, dbo.Posts.PostDate, dbo.Posts.PostID, dbo.[User].username, dbo.Topic.TopicID
FROM dbo.Topic LEFT OUTER JOIN
dbo.Posts ON dbo.Topic.TopicID = dbo.Posts.TopicID LEFT OUTER JOIN
dbo.[User] ON dbo.Posts.PUserID = dbo.[User].UserID

Resources