Best practices while update/insert to a database - c#

I'm using the following class to handle the database transactions, but the problem is that when my client come up with a faulty database update i cannot roll back the DB into the old state where everything was fine. So is there any best practice to achieve this.
public class DBConnection
{
static SqlConnection connection;
public void getConnection() {
try {
connection = new SqlConnection("Data Source=PC-DILUKSHAN\\SQLEXPRESS;Initial Catalog=EMS;User ID=user;Password=1234");
connection.Open();
}catch(Exception e){
throw e;
}
}
public DataTable executeSelectQuery(String sql) {
try
{
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.CommandType = CommandType.Text;
// Create a DataAdapter to run the command and fill the DataTable
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
catch (Exception e)
{
throw e;
}
finally {
connection.Close();
}
}
public void executeUpdateQuery(String sql) {
try {
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.ExecuteNonQuery();
}catch(Exception e){
throw e;
}
finally
{
connection.Close();
}
}
}

Use the SqlTransaction object. Here is an example on how to rollback a transaction:
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 =
"Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
command.ExecuteNonQuery();
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
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);
}
}
}
}

Related

How to maintain transaction handling executing two stored procedure from C# code

I one method in my class Which executing two stored procedure and insert data into three tables. How to handle transaction in that case?
For Example:
public class InsertClass
{
public void AddData()
{
try
{
Execute Sp1;
Execute Sp2;
}
catch (Exception ex)
{
throw ex;
}
}
}
Both SP are for insert.
Try this:
SqlConnection con = new SqlConnection(conString);
SqlCommand cmd1,cmd2;
con.Open();
cmd1 = new SqlCommand("sp1",con);
cmd2 = new SqlCommand("sp2",con);
SqlTransaction trans = con.BeginTransaction();
cmd1.Transaction = trans;
cmd2.Transaction = trans;
try
{
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
trans.Commit();
}
catch (Exception)
{
trans.Rollback();
}
Try this code:
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
try
{
Execute Sp1;
Execute Sp2;
}
catch (Exception)
{
transaction.Rollback();
connection.Close();
}
transaction.Commit();

How I can Insert Data in the MySQL Database?

I have a ASP.NET Application and a MySQL Database. I want write a Class to insert,delete and show the Data from the database. I have a Connection to the Database but I can't insert data in the database.
My Class insert method:
public string CreateEntry(string Connectionstring, string mitarbeiter)
{
connection = new MySqlConnection(Connectionstring);
try
{
var command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
connection.Close();
}
}
The Connectionstring is correct. I don't get a error but there is no data in the database.
My tablename: tb_mitarbeiter
columns: ID and Vorname
You should simply execute the command
....
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
command.ExecuteNonQuery();
....
I suppose that mitarbeiter is the real value that should be set in the database.
If this is the case remember to use parameters to insert/update your data
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES (?name)";
command.Parameters.AddWithValue("?name", mitarbeiter);
connection.Open();
command.ExecuteNonQuery();
You forgot to execute the command by calling command.ExecuteNonQuery(). This is how I would typically do it:
public string CreateEntry(string connectionString, string valueToInsert)
{
var stringToReturn = "";
try
{
using(var connection = new MySqlConnection(connectionString))
{
//Open connection
connection.Open();
//Compose query using sql parameters
var sqlCommand = "INSERT INTO table_name (field_name) VALUES (#valueToInsert)";
//Create mysql command and pass sql query
using(var command = new MySqlCommand(sqlCommand, connection))
{
command.Parameters.AddWithValue("#valueToInsert", valueToInsert);
command.ExecuteNonQuery();
}
stringToReturn ="Success Message";
}
}
catch(exception ex)
{
stringToReturn = "Error Message: " + ex.Message;
}
return stringToReturn;
}
There are a few key things to keep in mind:
Wrap disposable objects with a using. In the case of
MySqlConnection, it will properly close and dispose the connection
when its out of scope.
Use SQL parameters when passing values inside
your query. This will avoid SQL injection and its much more easier
to maintain.
Personally, I like to have one exit point in a
function. In this example, the "stringToReturn" variable holds the
value to return once the function is done executing both
successfully or in case of a failure.
To do a Insert / Update / Delete u should add
connection.Open();
command.ExecuteNonQuery();
For select ()to show data from database use:
connection.Open();
command.ExecuteReader();
{
string MyConnection2 = "datasource=localhost;port=3306;username=root;password=1234";
string Query = "insert into DBname.TableName(id,Name,First_Name,Age,Address) values('" +this.IdTextBox.Text+ "','" +this.NameTextBox.Text+ "','" +this.FirstnameTextBox.Text+ "','" +this.AgeTextBox.Text+ "','" +this.AddressTextBox.Text+ "');";
MySqlConnection MyConn2 = new MySqlConnection(MyConnection2);
MySqlCommand MyCommand2 = new MySqlCommand(Query, MyConn2);
MySqlDataReader MyReader2;
MyConn2.Open();
MyReader2 = MyCommand2.ExecuteReader();
MessageBox.Show("Save Data");
while (MyReader2.Read())
{
}
MyConn2.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
You are not executing the command use SqlCommand.ExecuteNonQuery
try
{
MySqlCommand command = connection.CreateCommand();
command.CommandText = "INSERT INTO tb_mitarbeiter (Vorname) VALUES ('tom')";
connection.Open();
command.ExecuteNonQuery();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
connection.Close();
}
You missed to write this:-
....
connection.Open();
command.ExecuteNonQuery();
....
You can also used Sql parameter to prevent Sql Injection
try
{
MySqlCommand command = connection.CreateCommand();
command.CommandText = #"INSERT INTO `tb_mitarbeiter` (`Vorname`) VALUES (#tom)";
command.Parameters.AddWithValue("#tom", tom);
connection.Open();
command.ExecuteNonQuery();
return "Mitarbeiter wurde angelegt";
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
command.Dispose();
command.Close();
connection.Close();
}

ExecuteNonQuery requires the command to have a transaction

I am receiving this error message when i try to execute the following code.
ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction
Can anyone advice where the problem is? I guess the root of the problem is the part where i try to execute a stored procedure.
The stored procedure is creates its own transaction when execute
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand command = conn.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = conn.BeginTransaction("createOrder");
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = conn;
command.Transaction = transaction;
try
{
command.CommandText = "INSERT INTO rand_resupply_order (study_id, centre_id, date_created, created_by) " +
"VALUES (#study_id, #centre_id, #date_created, #created_by) SET #order_id = SCOPE_IDENTITY()";
command.Parameters.Add("#study_id", SqlDbType.Int).Value = study_id;
command.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
command.Parameters.Add("#date_created", SqlDbType.DateTime).Value = DateTime.Now;
command.Parameters.Add("#created_by", SqlDbType.VarChar).Value = username;
SqlParameter order_id = new SqlParameter("#order_id", SqlDbType.Int);
//study_name.Value =
order_id.Direction = ParameterDirection.Output;
command.Parameters.Add(order_id);
command.ExecuteNonQuery();
command.Parameters.Clear();
//loop resupply list
for (int i = 0; i < resupplyList.Count(); i++)
{
try
{
SqlCommand cmd = new SqlCommand("CreateOrder", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#study_id", SqlDbType.Int).Value = study_id;
cmd.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#created_by", SqlDbType.VarChar).Value = username;
cmd.Parameters.Add("#quantity", SqlDbType.VarChar).Value = resupplyList[i].Quantity;
cmd.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#depot_id", SqlDbType.VarChar).Value = depot_id;
cmd.Parameters.Add("#treatment_code", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#order_id", SqlDbType.Int).Value = (int)order_id.Value;
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
catch (Exception ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
finally
{
conn.Close();
conn.Dispose();
}
}
return (int)order_id.Value;
}
catch (Exception ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
finally
{
// Attempt to commit the transaction.
transaction.Commit();
conn.Close();
conn.Dispose();
command.Dispose();
}
when using transaction, you should use it everywhere.
cmd.Transaction = transaction;
using Connection String transaction not popular so far.you can delete every things that related to SqlTransaction and then wrap your code with TransactionScope

create a transaction for MS Access in c#

I need some code in my c# application to implement a transaction using Microsoft Access database
OleDbTransaction class is made just for that. You can create one by calling BeginTransaction on your OleDbConnection object then Commit or Rollback up to your scenario
using (var transaction = cn.BeginTransaction()) {
//Do Stuff here using the connection
transaction.Commit();
}
The transaction will be rolled back at dispose if no commit has been called.
Try to implement the code similar to the one below.
public void InsertPersonAndPhoneNumber(int personId, string name, string phone)
{
string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=./contacts.accdb";
OleDbConnection cn = new OleDbConnection(connectionString);
try
{
cn.Open();
OleDbTransaction trans = cn.BeginTransaction();
try
{
//first transaction ...
string sql1 = "INSERT INTO People (ID, pname) VALUES (#ID, #pname)";
OleDbCommand cmd1 = new OleDbCommand(sql1, cn, trans);
cmd1.Parameters.AddWithValue("#ID", personId);
cmd1.Parameters.AddWithValue("#pname", name);
cmd1.ExecuteNonQuery();
//first transaction ...
string sql2 = "INSERT INTO PhoneNumbers (PID, num) VALUES (#PID, #num)";
OleDbCommand cmd2 = new OleDbCommand(sql2, cn, trans);
cmd2.Parameters.AddWithValue("#PID", personId);
cmd2.Parameters.AddWithValue("#num", phone);
cmd2.ExecuteNonQuery();
trans.Commit();
Console.WriteLine("New Contact Added successfully ...");
}
catch (Exception x)
{
//handle exception ....
trans.Rollback();
Console.WriteLine(x.Message);
}
finally
{
cn.Close();
}
}
catch (Exception x)
{
//handle exception ....
}
}

Commit multiple SqlCommands with SqlTransaction

I am trying to pass a list of SqlCommand into a member function that holds the connection to the database.
public void CommitAsTransaction(List<SqlCommand> commands) {
SqlTransaction transaction = null;
SqlConnection connection = null;
try {
connection = this.CreateSqlConnection();
connection.Open();
transaction = connection.BeginTransaction("TransactionID");
foreach (SqlCommand cmd in commands) {
cmd.Transaction = transaction;
cmd.Connection = connection;
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
catch (Exception ex) {
transaction.Rollback();
}
connection.Close();
}
This is what I currently have. The error occurs because the command seems to be being executed as in place and the transaction.Commit(); is never reached. I have seen many people doing it like this and am not sure what I am doing wrong.
PS: The issue is that the stored procedures that will be getting executed MUST all be run within a single transaction, I do not control these and they're encrypted, the reason they must be run in a transaction is because they create temp records in a table that has a PK requirement.
Can you use a transaction scope instead ?
Something like:
// place this code inside CommitAsTransaction
using (TransactionScope scope = new TransactionScope())
{
Boolean AllOK = true;
SqlConnection connection = this.CreateSQLConnection();
try
{
connection.Open()
}
catch (Exception e)
{
// deal with it how you need to
AllOK = false;
}
if (AllOK)
{
foreach(SQlCommand cmd in Commands)
{
try
{
cmd.Connection = connection;
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
// Deal with it..
AllOK = false;
break;
}
}
if (AllOK)
{
scope.Complete();
try
{
connection.Close();
}
catch (Exception e)
{
// deal with it
}
}
}
}
Thanks so much. I ended up figuring it out on my own based on other peoples combined answers, as a thank you here is the code I used:
public List<Models.eConnectModels.eConnStatus> CommitAsTransaction(List<SqlCommand> commands)
{
SqlTransaction transaction = null;
SqlConnection connection = null;
List<eConnStatus> ErrorList = new List<eConnStatus>();
try
{
connection = this.CreateSqlConnection();
connection.Open();
transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted, "TransactionID");
foreach (SqlCommand cmd in commands)
{
eConnStatus curErr = new eConnStatus();
cmd.Transaction = transaction;
cmd.Connection = connection;
SqlParameter errorString = cmd.Parameters.Add("#oErrString", SqlDbType.VarChar);
errorString.Direction = ParameterDirection.Output;
errorString.Size = 8000;
SqlParameter errorStatus = cmd.Parameters.Add("#O_iErrorState", SqlDbType.Int);
errorStatus.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
curErr.ErrorState = (int)cmd.Parameters["#O_iErrorState"].Value;
curErr.ErrorMessage = (string)cmd.Parameters["#oErrString"].Value;
ErrorList.Add(curErr);
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
connection.Close();
throw ex;
}
connection.Close();
return ErrorList;
}

Categories