Transactions in C# - c#

In addition to this question: Preorder tree traversal copy folder
I was wondering if it is possible to create a transaction that contains different calls to the database.
ex:
public bool CopyNode(int nodeId, int parentNode)
{
// Begin transaction.
try
{
Method1(nodeId);
Method2(nodeId, parentNode);
Method3(nodeId);
}
catch (System.Exception ex)
{
//rollback all the methods
}
}
I don't know if this is possible. We are using subsonic to do the database calls.
This is really important, not only for the tree traversal problem but also for some other stuff we do.
The main idea is that we can't let our dabase get corrupted with uncomplete data.

That is possible, you can find a example here
Or perhaps a transaction scope...
http://msdn.microsoft.com/en-us/library/ms172152.aspx

BeginTransaction is called off a ADO.NET collection object.
The Command object needs this transaction (SqlTransaction object) assigned to it.
Commit and Rollback are only called in the outer method.
Check out this code. It works by re-using the SqlConnection and SqlTransaction objects. This is a classic Master>Details type of set up. The master type is ColumnHeaderSet which contains a property of
List<ColumnHeader>
, which is the details (collection).
Hope this helps.
-JM
public static int SaveColumnHeaderSet(ColumnHeaderSet set)
//save a ColumnHeaderSet
{
string sp = ColumnSP.usp_ColumnSet_C.ToString(); //name of sp we're using
SqlCommand cmd = null;
SqlTransaction trans = null;
SqlConnection conn = null;
try
{
conn = SavedRptDAL.GetSavedRptConn(); //get conn for the app's connString
cmd = new SqlCommand(sp, conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
trans = conn.BeginTransaction();
cmd.Transaction = trans; // Includes this cmd as part of the trans
//parameters
cmd.Parameters.AddWithValue("#ColSetName", set.ColSetName);
cmd.Parameters.AddWithValue("#DefaultSet", 0);
cmd.Parameters.AddWithValue("#ID_Author", set.Author.UserID);
cmd.Parameters.AddWithValue("#IsAnonymous", set.IsAnonymous);
cmd.Parameters.AddWithValue("#ClientNum", set.Author.ClientNum);
cmd.Parameters.AddWithValue("#ShareLevel", set.ShareLevel);
cmd.Parameters.AddWithValue("#ID_Type", set.Type);
//add output parameter - to return new record identity
SqlParameter prm = new SqlParameter();
prm.ParameterName = "#ID_ColSet";
prm.SqlDbType = SqlDbType.Int;
prm.Direction = ParameterDirection.Output;
cmd.Parameters.Add(prm);
cmd.ExecuteNonQuery();
int i = Int32.Parse(cmd.Parameters["#ID_ColSet"].Value.ToString());
if (i == 0) throw new Exception("Failed to save ColumnHeaderSet");
set.ColSetID = i; //update the object
//save the ColumnHeaderList (SetDetail)
if (ColumnHeader_Data.SaveColumnHeaderList(set, conn, trans)==false) throw new Exception("Failed to save ColumnHeaderList");
trans.Commit();
// return ID for new ColHdrSet
return i;
}
catch (Exception e){
trans.Rollback();
throw e;
}
finally{
conn.Close();
}
}
public static bool SaveColumnHeaderList(ColumnHeaderSet set, SqlConnection conn, SqlTransaction trans)
//save a (custom)ColHeaderList for a Named ColumnHeaderSet
{
// we're going to accept a SqlTransaction to maintain transactional integrity
string sp = ColumnSP.usp_ColumnList_C.ToString(); //name of sp we're using
SqlCommand cmd = null;
try
{
cmd = new SqlCommand(sp, conn); // re-using the same conection object
cmd.CommandType = CommandType.StoredProcedure;
cmd.Transaction = trans; // includes the cmd in the transaction
//build params collection (input)
cmd.Parameters.Add("#ID_ColSet", SqlDbType.Int);
cmd.Parameters.Add("#ID_ColHeader", SqlDbType.Int);
cmd.Parameters.Add("#Selected", SqlDbType.Bit);
cmd.Parameters.Add("#Position", SqlDbType.Int);
//add output parameter - to return new record identity
//FYI - #return_value = #ID_SavedRpt
SqlParameter prm = new SqlParameter();
prm.ParameterName = "#ID";
prm.SqlDbType = SqlDbType.Int;
prm.Direction = ParameterDirection.Output;
cmd.Parameters.Add(prm);
//Loop
foreach (ColumnHeader item in set.ColHeaderList)
{
//set param values
cmd.Parameters["#ID_ColSet"].Value = set.ColSetID;
cmd.Parameters["#ID_ColHeader"].Value = item.ColHeaderID;
cmd.Parameters["#Selected"].Value = item.Selected;
cmd.Parameters["#Position"].Value = item.Position;
cmd.ExecuteNonQuery();
int i = Int32.Parse(cmd.Parameters["#ID"].Value.ToString());
if (i == 0) throw new Exception("Failed to save ColumnHeaderSet");
}
return true;
}
catch (Exception e)
{
throw e;
}
}

Related

.NET SQL client stored procedure call empty statement

I have a .NET 6 C# app that calls three stored procedures on a SQL Server. Two of these calls work ok, but the third gives a null ref exception at the line:
result = (bool)cmd.ExecuteScalar();
using (var con1 = this.GetSqlConnection())
{
bool result = false;
try
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = OnlinePaymentCheckNurseryChildSProc;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con1;
cmd.Parameters.AddRange(param);
LogSqlParams(cmd.Parameters);
con1.Open();
result = (bool)cmd.ExecuteScalar();
LogInfo(methodName, $"result=:{result}");
LogInfo(methodName, $"Leaving:{methodName}");
return result;
}
catch (Exception ex)
{
LogError(ex, methodName, "Trouble checking nursery child");
return false;
}
}
The stored proc ends with:
IF (#d = #DOB)
BEGIN
SET #Return = 1
END
ELSE
BEGIN
SET #Return = 0
END
RETURN #Return
END
I've called the same stored procedure with the same parameters from SQL Server Management Studio and it works ok.
The key to solving this problem was to use the ExecuteNonQuery method and pass a special param with a "Direction" property of ParameterDirection.ReturnValue.
The working code block can be see below:
using (var con1 = this.GetSqlConnection())
{
try
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = OnlinePaymentCheckNurseryChildSProc;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con1;
cmd.Parameters.AddRange(param);
LogSqlParams(cmd.Parameters);
SqlParameter returnValue = new SqlParameter();
returnValue.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(returnValue);
con1.Open();
await cmd.ExecuteNonQueryAsync();
int result = (int)returnValue.Value;
LogInfo(methodName, $"result=:{result}");
LogInfo(methodName, $"Leaving:{methodName}");
return result == 1 ? true : false;
}
catch (Exception ex)
{
LogError(ex, methodName, "Trouble checking nursery child");
return false;
}
}

Error in ado.net crud operations

I want to update details. I have code in a data access class. But after executing ExecuteScalar(), it goes to the catch block and shows an exception as null.
Program :
public bool UpdateData(Customer objcust) // passing model class object because it contains all customer properties.
{
SqlConnection con = null;
// string result = "";
//int rows = 0;
try
{
string connectionString = #"server=(local)\SQLExpress;database=CustDemo;integrated Security=SSPI;";
con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("UPDATE Customer SET Name = #Name , Address = #Address, Gender =#Gender , City=#City WHERE Customer.CustomerID = #CustomerID",con);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Name", objcust.Name);
cmd.Parameters.AddWithValue("#Gender", objcust.Gender);
cmd.Parameters.AddWithValue("#Address", objcust.Address);
cmd.Parameters.AddWithValue("#City", objcust.City);
con.Open();
cmd.ExecuteScalar();
return true;
}
catch(Exception ex)
{
return false;
}
}
Instead of cmd.ExecuteScalar(); Try to use
cmd.ExecuteNonQuery ();
ExecuteNonQuery is used specifically executing UPDATE, INSERT, or DELETE statements.

Retrieving data from SQL Server with a stored procedure with ASP.net and c#

I have a form and I want to retrieve data from a sql table and show it in the form's fields depending on the ?id I enter in the url, but I always get this error:
Procedure or function 'GetAppForm' expects parameter '#id', which was
not supplied.
Note: GetAppForm is the stored procedure.
Here's my code, please help me:
try
{
if (String.IsNullOrEmpty(Request.QueryString["id"]))
{
sqlConn.Open();
using (SqlCommand cmd = new SqlCommand("GetAppForm", sqlConn))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter id = cmd.Parameters.Add("#id", SqlDbType.Int);
id.Direction = ParameterDirection.Input;
id.Value = Request.QueryString["id"];
SqlDataReader dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (dataReader.Read())
{
OwnerField.Text = dataReader["Owner"].ToString();
OdBookNoField.Text = dataReader["OD"].ToString();
PdLocField.Text = dataReader["pd"].ToString();
StatementNoField.Text = dataReader["Statmnt"].ToString();
ApplicationNoField.Text = dataReader["AppNo"].ToString();
AppDateField.Text = dataReader["AppDate"].ToString();
areaField.Text = dataReader["Area"].ToString();
areaNoField.Text = dataReader["AreaNo"].ToString();
blockNoField.Text = dataReader["BlockNo"].ToString();
streetNoField.Text = dataReader["StreetNo"].ToString();
}
}
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("No Connection!!");
}
finally
{
sqlConn.Close();
}
Change
if (String.IsNullOrEmpty(Request.QueryString["id"]))
to
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
I think you just forgot to negate the String.IsNullOrEmpty condition:
try
{
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
{
Please note, your code is prone to injection.
try
{
if (!String.IsNullOrEmpty(Request.QueryString["id"]))
{
sqlConn.Open();
using (SqlCommand cmd = new SqlCommand("GetAppForm", sqlConn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#id", Request.QueryString["id"]);
SqlDataReader dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
//SqlDataReader dataReader = cmd.ExecuteReader(CommandBehavior.SingleRow);
while (dataReader.Read())
{
OwnerField.Text = dataReader["Owner"].ToString();
OdBookNoField.Text = dataReader["OD"].ToString();
PdLocField.Text = dataReader["pd"].ToString();
StatementNoField.Text = dataReader["Statmnt"].ToString();
ApplicationNoField.Text = dataReader["AppNo"].ToString();
AppDateField.Text = dataReader["AppDate"].ToString();
areaField.Text = dataReader["Area"].ToString();
areaNoField.Text = dataReader["AreaNo"].ToString();
blockNoField.Text = dataReader["BlockNo"].ToString();
streetNoField.Text = dataReader["StreetNo"].ToString();
}
}
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("No Connection!!");
}
finally
{
sqlConn.Close();
}

Unable to execute MySql stored procedure from c#

In MySql:
DELIMITER //
DROP PROCEDURE IF EXISTS `testdb`.`Check_UserId_Sproc` //
CREATE PROCEDURE `testdb`.`Check_UserId_Sproc` (IN User_Id NVARCHAR(100))
BEGIN
select count(*) from demo_user where userid = User_Id;
END //
DELIMITER ;
In C#:
public DataTable ExecuteParameterizedSelectCommand(string CommandName, CommandType cmdType,MySqlParameter[] param)
{
DataTable table = new DataTable();
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
using(MySqlConnection con = new MySqlConnection(CS))
{
using (MySqlCommand cmd = con.CreateCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = CommandName;
cmd.Parameters.AddRange(param);
try
{
if (con.State != ConnectionState.Open)
{
con.Open();
}
using (MySqlDataAdapter da = new MySqlDataAdapter(cmd))
{
da.Fill(table);
}
}
catch
{
throw;
}
return table;
}
}
}
public DataTable checkExistingUserId()
{
MySqlDBHelper oHelper = new MySqlDBHelper();
MySqlParameter[] parameters = new MySqlParameter[]
{
new MySqlParameter("User_Id", 'DemoId')
};
return oHelper.ExecuteParameterizedSelectCommand("Check_UserId_Sproc", CommandType.StoredProcedure, parameters);
}
When I try to execute the checkExistingUserId(), I get following exception:
Incorrect number of arguments for PROCEDURE testdb.Check_UserId_Sproc; expected 1, got 0
May be I am doing a silly mistake but I am not able to figure it out. I am new to mysql and trying to work around it.
When I debug the array contains the parameter as seen in below image, but it is not collected by the SP.
Thanks in advance
In your code:
cmd.CommandType = CommandType.Text;
should be
cmd.CommandType = cmdType;

How to pass DataTable as a parameter to Stored Procedure by not commiting the current session transaction?

My code looks like this:
public void InsertSampleData(DataTable tempTable)
{
session.Transaction.Commit();
var connection = session.GetSessionImplementation().Connection;
using (var sqlConnection = (SqlConnection)connection)
{
using (var cmd = sqlConnection.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.insertData";
cmd.Parameters.Add("#sItems", SqlDbType.Structured);
var sqlParam = cmd.Parameters["#Items"];
sqlParam.Direction = ParameterDirection.Input;
sqlParam.TypeName = "[dbo].[DataItemType]";
sqlParam.Value = tempTable;
cmd.ExecuteNonQuery();
}
}
}
The code works fine but if I do not commit the transaction it throws an exception as shown
System.InvalidOperationException: ExecuteNonQuery requires the command to have a transaction when the connection assigned to the
command is in a pending local transaction. The Transaction property
of the command has not been initialized.
But I don't want the transaction to be commited yet before completing the whole operation. How can I achieve this??
the issue is that the transaction was supposed to use has been committed and already has been disposed, thus either instantiate a new SqlConnection or get a new session and then run the query as part of the new session.
public void InsertSampleData(DataTable tempTable)
{
session.Transaction.Commit();
using (var sqlConnection = new SqlConnection(session.GetSessionImplementation()
.Connection.ConnectionString))
{
using (var cmd = sqlConnection.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.insertData";
cmd.Parameters.Add("#sItems", SqlDbType.Structured);
var sqlParam = cmd.Parameters["#Items"];
sqlParam.Direction = ParameterDirection.Input;
sqlParam.TypeName = "[dbo].[DataItemType]";
sqlParam.Value = tempTable;
cmd.ExecuteNonQuery();
}
}
}
or
public void InsertSampleData(DataTable tempTable)
{
session.Transaction.Commit();
var connection = session.SessionFactory.OpenSession().GetSessionImplementation().Connection;
using (var sqlConnection = (SqlConnection)connection)
{
using (var cmd = sqlConnection.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.insertData";
cmd.Parameters.Add("#sItems", SqlDbType.Structured);
var sqlParam = cmd.Parameters["#Items"];
sqlParam.Direction = ParameterDirection.Input;
sqlParam.TypeName = "[dbo].[DataItemType]";
sqlParam.Value = tempTable;
cmd.ExecuteNonQuery();
}
}
}
BTW, why are using SQLConnection why arent you exexurting the query using the Nhibernate Session, you could do the everything that you are doing right now using the Nhibernate Session.
session.CreateSQLQuery(<your sql>)
.SetParameters() //and various other API to set your parameters
.ExecuteUpdate();

Categories

Resources