Folks I ran into a bit of code and I'm a tad confused about whats going on.
I'm working to refactor the code to instead process a handful of SqlCommands rather than the single SqlCommand that it currently works with. It's my hope all SqlCommands can be processed under ONE transaction.
Each SqlCommand is a Stored Procedure, so in effect my transaction would call one (or many) Stored Procedures - first off, is that even possible?
Regardless, here's the code block:
public virtual void Execute()
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
SqlCommand storedProcedure = new SqlCommand(BlahBah, conn);
storedProcedure.CommandType = CommandType.StoredProcedure;
conn.Open();
**conn.BeginTransaction(IsolationLevel.ReadUncommitted).Commit();**
storedProcedure.ExecuteNonQuery();
conn.Close();
}
}
In particular the highlighted statement that sets a Transaction on the Connection object, appended with a ".Commit()".
The actual source code has no ROLLBACK, nor anywhere is there a COMMIT. Am I essentially seeing some sort of AutoCommit? Does it even make sense to have a TRANSACTION here if for example the DB doesn't require TRANSACTIONal processing?
Perhaps more important to my refactoring efforts, would something like this make sense? That's to ask, if I processed 10 Stored Procedures and the last one threw an error, would there be an auto ROLLBACK on all 10?
Here's where I want to land:
public virtual void ExecuteTest()
{
using (SqlConnection conn = new SqlConnection(ApplicationConfig.DbConnectInfo.ConnectionString))
{
var errorText = string.Empty;
conn.Open();
conn.BeginTransaction(IsolationLevel.ReadUncommitted).Commit();
foreach (var storedProcedure in StoredProcedures)
{
storedProcedure.Connection = conn;
storedProcedure.ExecuteNonQuery();
}
conn.Close();
}
}
EDIT: this MS link suggests the approach will work:
SqlConnection.BeginTransaction Method (IsolationLevel)
Thank you for your interest.
Related
I have a database from which I want to read a particular value.
When I am reading ID is always showing up as 0.
public void ClustersStatus(Clusters cl)
{
using (SqlConnection con = new SqlConnection("Data Source=DESKTOP-JMGPJ6J;Initial Catalog=Infinity;Integrated Security=True"))
{
using (SqlCommand cmd = new SqlCommand("Select Approved from Master_Cluster where ClusterID = #id", con))
{
con.Open();
cmd.Parameters.AddWithValue("#id", cl.ClusterID);
cmd.Connection = con;
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
rdr.Read();
if (Convert.ToInt32(rdr["Approved"]) == 1)
{
}
else
{
}
}
}
// return cl;
}
}
It is really hard to answer your question without even more information about your specific problem, but I think the real issue in your
rdr.Read();
line. SqlDataReader.Read method advances your data reader to the next record which means that you are reading your rdr["Approved"] column of your next line with your
if (Convert.ToInt32(rdr["Approved"]) == 1)
code which seems weird and I think this is not what you want. You might wanna consider to delete it.
Also I have a few suggestions;
Do not put your connection string directly in your code. These settings can be change over time to time. Put it some configuration file or read it from some settings file.
Open your connection object just before you use it. Not a big deal, but it is a good practice.
Do not use AddWithValue method. It may generate unexpected results sometimes. Use Add method instead (preferably Add(String, SqlDbType, Int32) overload)
Supposed your query offers you what you want, to be able to use the data read with SqlDataReader, you need to use the IDataRecord interface. You cannot use directly the rdr variable.
For ease of use, the I wrote the reading engine into a function. I am writing here the idea for you to adapt it to your needs:
private static int ReadSingleRow(IDataRecord record)
{
//below is an example of processing using `record`. You adapt it to your needs
int.TryParse(record[0].ToString(), out int temp);
MessageBox.Show (temp.ToString());
//...do the processing you need
return temp;
}
After you wrote the function, the main becomes:
if (rdr.HasRows)
{
while (rdr.Read());
int x=ReadSingleRow((IDataRecord) rdr);
//or use, somehow, the above function
// other processing
}
When you search the internet or SO how to connect to SQL database inside Script Task in SSIS you will find .NET v1.1ish code like this:
ConnectionManager cm;
System.Data.SqlClient.SqlConnection sqlConn;
System.Data.SqlClient.SqlCommand sqlComm;
cm = Dts.Connections["ADO.NET.SqlDB"];
sqlConn = (System.Data.SqlClient.SqlConnection)cm.AcquireConnection(Dts.Transaction);
sqlComm = new System.Data.SqlClient.SqlCommand("your SQL Command", sqlConn);
sqlComm.ExecuteNonQuery();
cm.ReleaseConnection(sqlConn);
I am looking for updated code that makes good use of later introduced .NET features.
For a start, how about the code below. Is this the current recommended way to connect to SQL Server inside Script Task in SSIS 2012 and later or do I miss something here?
ConnectionManager cm = Dts.Connections["ADO.NET.SqlDB"];
using (var sqlConn = (SqlConnection)cm.AcquireConnection(Dts.Transaction))
{
if (sqlConn.State != ConnectionState.Open)
sqlConn.Open();
using (var sqlComm = new SqlCommand(
String.Format("UPDATE myTab SET Processed = 4 where ID = '{0}'",
idNumber), sqlConn))
{
return sqlComm.ExecuteNonQuery();
}
}
Is the ReleaseConnection() still needed?
Is sqlConn.Open() really needed in an SSIS context?
One year later, and hopefully a little wiser, I settled with code like this:
ConnectionManager cm = Dts.Connections["ADO.NET.SqlServerDB"];
var sqlConn = (SqlConnection)cm.AcquireConnection(Dts.Transaction);
using (var sqlCmd = new SqlCommand(
"INSERT INTO apou_moodle_import(id_num, username, email) VALUES(#IdNum, #Username, #Email)",
sqlConn))
{
sqlCmd.CommandType = CommandType.Text;
sqlCmd.Parameters.AddWithValue("#IdNum", newUser.id_num);
sqlCmd.Parameters.AddWithValue("#Username", newUser.username);
sqlCmd.Parameters.AddWithValue("#Email", newUser.email);
int rowsAffected = sqlCmd.ExecuteNonQuery();
}
cm.ReleaseConnection(sqlConn);
So, I keep using ConnectionManager.ReleaseConnection, however, SqlConnection.Open & Close are not needed in an SSIS context. Plus, use Parameters to play safe.
Well, using structure allows you to automate disposing variables and handle it better. However, sqlConn is not a simple class, it is a ConnectionManager instance. When you start using it, you call AcquireConnection, when end - call ReleaseConnection. The ReleaseConnection may perform some housekeeping specific to this Data Source. Depending on ConnectionManager implementation, it may check at disposal whether ReleaseConnection was called or not and call it.
To my understanding, your code with using should be Ok in most cases. Problems may arise when you repeatedly open connections and do not release it - you might run of connection pool etc. I would wrap internals into try - finally block to ensure ReleaseConnection execution.
SqlCommand cmd = new SqlCommand();
SqlConnection con = new SqlConnection();
string checkRadioButton()
{
string rbdText;
if(RadioButton1.Checked)
{
rbdText = RadioButton1.Text;
}
else
{
rbdText = RadioButton2.Text;
}
return rbdText;
}
protected void Button1_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand(" insert into Registration values(#Name, #Gender, #MobileNumber, #EmailID, #UserID, #Password, #Address, #Country)", con);
cmd.Parameters.AddWithValue("#Name", TextBox1.Text);
cmd.Parameters.AddWithValue("#Gender", checkRadioButton());
cmd.Parameters.AddWithValue("#MobileNumber", TextBox2.Text);
cmd.Parameters.AddWithValue("#EmailID", TextBox3.Text);
cmd.Parameters.AddWithValue("#UserID", TextBox5.Text);
cmd.Parameters.AddWithValue("#Password", TextBox6.Text);
cmd.Parameters.AddWithValue("#Address", TextBox8.Text);
cmd.Parameters.AddWithValue("#Country", DropDownList1.SelectedItem.Value);
cmd.ExecuteNonQuery();
con.Close();
Response.Redirect("Homepage2.aspx");
}
This is my aspx.cs file for registration page. There is no compilation error, but after the Button1_Click event executed, the registration data is not saved into the database.
You would need to add a connection string into the SQLConnection constructor. The connection string itself is usual to keep in the web.config. So the code could be similar to:
var connectionString = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Do your insert here;
}
As others have mentioned, the answer to your question is that you are missing a connection string as a parameter for the instantiation of your SqlConnection object:
SqlConnection con = new SqlConnection("connection string goes here");
However, there are a few other things I would like to recommend you change in your code.
Properly releasing of unmanaged resources
Any class that implements the IDisposable interface needs to be disposed of properly. What that means is calling the Dispose() method, or wrapping the instantiation of the object in a using block (I would highly recommend this route if possible as it is much simpler).
So, for example, SqlConnection implements IDisposable, so I would change this:
SqlConnection con = new SqlConnection();
to this:
using (SqlConnection con = new SqlConnection())
{
// ...
}
You would need to make these changes for SqlCommand as well.
Use of try...catch blocks for code that could throw exceptions
Any code that could throw an exception should be wrapped in a try...catch block. What this does is prevent your application from crashing when an exception is thrown. Exceptions can be thrown in places you would not expect that have nothing to do with your code.
Take SqlConnection for example. If your network connection suddenly stops working and your code calls SqlConnection.Open(), an exception would be thrown and your application would crash. Wrapping this line in a try...catch block would prevent the app from crashing and allow you to handle the exception "gracefully" (by logging the error and continuing running the application if possible).
using (var connection = new SqlConnection("Server=SQLServerName;Integrated Security=True;"))
{
try
{
connection.Open()
}
catch (Exception ex)
{
// Do something with the exception
}
}
Moving hardcoded SQL statements to Stored Procedures/Functions
If you put your SQL statements directly in your source code (commonly called "hardcoding"), you then have to re-compile and re-deploy your entire application if that SQL statement has to change in the future.
Instead, you can extract SQL statements into Stored Procedures or Functions and call those from your code. That way, when the SQL statement needs to change, you don't need to re-compile and re-deploy your application; simply update the stored procedure/function.
There are a few more parts of your code that could be refactored to be simpler, but this post is already far larger than I initially intended, so I will stop here.
I have an application that fires a mysql command (query) "show databases", the query works and returns properly but I can't close my connections. The user I used had 24 connections allowed at the same time so the problem popped up further down my program but reducing the allowed connections to 2 shows me that I can't even close the first query (which isn't in a loop). The code is the following:
protected override Dictionary<string, Jerow_class_generator.Database> loadDatabases()
{
MySqlConnection sqlCon = new MySqlConnection(this.ConnectionString);
sqlCon.Open();
MySqlCommand sqlCom = new MySqlCommand();
sqlCom.Connection = sqlCon;
sqlCom.CommandType = CommandType.Text;
sqlCom.CommandText = "show databases;";
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader();
Dictionary<string, Jerow_class_generator.Database> databases = new Dictionary<string, Jerow_class_generator.Database>();
string[] systemDatabases = new string[] { "information_schema", "mysql" };
while (sqlDR.Read())
{
string dbName = sqlDR.GetString(0);
if (!systemDatabases.Contains(dbName))
{
databases.Add(sqlDR.GetString(0), new MySQL.Database(dbName, this));
}
}
sqlCom.Dispose();
sqlDR.Close();
sqlCon.Close();
sqlCon.Dispose();
return databases;
}
P.S. The 'New MySQL.Database(dbName, this));' is my owm made class which only stores the DB structure, could be considered irrelevant.
The exact error I get is 'max_user_connections'. on the connection.open line of the next time a query needs to be fired.
Rather than keeping track of all the Open/Close/Dispose calls all over the place, I'd recommend just replacing all of those with using statements. This will make sure the expected scope of each object is clear and that it will be destroyed/disposed upon exiting that scope.
Close() nor using will help alone with your problem because ADO.NET is using its own connection pooling and connections are by default not closed until program is closed. There are few options to solve this, but consider performance implications and is this really desired behavior for your application.
Add ";Pooling=False" to your connection string.
SqlConnection.ClearPool Method
SqlConnection.ClearAllPools Method
For more information read: SQL Server Connection Pooling (ADO.NET)
Along with the using suggestions above, when creating your sqlDR variable you should use the CloseConnection command behavior to close the actual connection if that is your intended action. As noted in the documentation here.
When the command is executed, the associated Connection object is closed when the associated DataReader object is closed.
So your code to instantiate your reader would look like this:
//to instantiate your variable
MySqlDataReader sqlDR;
sqlDR = sqlCom.ExecuteReader(CommandBehavior.CloseConnection);
//closing your datareader reference here will close the connection as well
sqlDR.Close();
If you wrap all your code in a using block using the above method, you don't need any of those Close() or Dispose() methods other than the sqlDR.Close();
when use "using" key word what happen is.when the garbage collector activate it first dispose objects which was declred in using statement.
I recommend using connection pooling in combination with the MySqlHelper class, passing the connection string as the first argument. That allows MySQL to open the connection if necessary, or keep it open according to the pooling cfg, without you having to know about it.
I changed my code to use 1 connection and keep it open and when testing I came across an error that a datareader should be closed. Now since all my queries didn't close the dataReader object (I used dataTable.Load(cmd.ExecuteReader()).) I think the problem might be there.
Keeping 1 open connection worked perfectly so I don't know what caused the not closing problem. I gues it was the dataReader not closing by itself.
Close() will definitely help you close your.
using (MySqlConnection conn = GetConnection())
{
conn.Open();
using (MySqlCommand cmd = conn.CreateCommand())
{
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "UserDetail";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
list.Add(new Album()
{
Id = Convert.ToInt32(reader["UId"]),
Name = reader["FirstName"].ToString(),
ArtistName = reader["LastName"].ToString()
});
}
}
}
}
In the above code, you can see one if condition before opening the connection it will help you to reuse your already open connections check below code.
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
I am having a slight issue. Please guide me.
I am coding in C#(Console App). I have called 2 different stored procedure in my code. Basically both these stored procedures access the same table.
First SP has a select query and an update query.
Second SP has a single update query.
Now I want to call these SP in a transaction mode(Either all succeeds or is second SP fails rollback first SP). I have used "TransactionScope" within my C# code but is doesnt seem to work fine. ie when I stop the Console App sometimes I see that the first SP is executed and the second one fails.
Can anybody suggest me on this.
Regards,
Justin Samuel.
If you are using TransactionScope, it should work fine, but the scope must surround the connection(s):
using(TransactionScope tran = new TransactionScope()) {
using(SqlConnection conn = new SqlConnection(cs)) {
// either multiple commands on one connection
using(SqlCommand cmd = conn.CreateCommand()) {
// etc
}
using(SqlCommand cmd = conn.CreateCommand()) {
// etc
}
}
using(SqlConnection conn = new SqlConnection(cs)) {
// or a separate connection
using(SqlCommand cmd = conn.CreateCommand()) {
// etc
}
}
tran.Complete();
}
There is an edge case where a TransactionScope can fail causing the later command to run without a transaction.
Alternatively, for a single connection use SqlTransaction, but remember to associate the transaction (from the connection) to each command.