Transaction Stored Procedure C# - c#

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.

Related

Modern way connect to SQL Server inside Script Task in SSIS 2012 and later

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.

Running a t-sql stored procedure with EXECUTE AS statement with .NET SqlCommand

I have a .NET web service application that executes parameterized MS SQL Server stored procedures using System.Data.SqlCommand. The application receives a name of the stored procedure and its parameters from user input.
Also the application deployed within Windows AD domain and it is able to obtain the name of a remote user with the help of SSPI authentication.
using (var con = new SqlConnection(connectionString)) {
using (var cmd = new SqlCommand(procedureName, con)) {
cmd.CommandType = CommandType.StoredProcedure;
foreach (var pair in pairs.AllKeys) {
cmd.Parameters.Add(new SqlParameter(pair, pairs[pair]));
}
con.Open();
using (var reader = cmd.ExecuteReader()) {
// processing results
}
}
}
Now I want to execute a stored procedure with an EXECUTE AS statement.
use [db]
go
execute as user = 'domain\user' --execute as RemoteUser
exec [db].[stored_procdure] #param1=value1
Can this be done? How can I add EXECUTE AS to the SqlCommand?
I would not like to resort to sql injection prone code and build the sql request from strings received from user.
Solved it some time ago with a colleague of mine.
To achieve the requested behavior the execute as statement should be run in a separate preceeding SqlCommand while in the same SqlConnection.
After the closing of the reader, still in the same SqlConnection, there's another separate SqlCommand needed - revert - to switch back the context.
I was having a similar issue where I was able to Execute as User but the Revert wasn't working. By following prot's answer I was able to fix it. So for anyone having a similar issue this is how the code looks
SqlCommand cmd = new SqlCommand("EXECUTE AS USER = 'domain\\user';");
OSSDBDataContext dc = new OSSDBDataContext();
cmd.Connection = dc.Connection as SqlConnection;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
//Execute stored procedure code goes here
SqlCommand cmd2 = new SqlCommand("REVERT;");
cmd2.Connection = dc.Connection as SqlConnection;
cmd2.ExecuteNonQuery();

SqlConnection.BeginTransaction(IsolationLevel.ReadUncommitted).Commit();

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.

Need help (The ConnectionString property has not been initialized)

I have a problem. I cannot fix the problem with The ConnectionString property has not been initialized. The problem is in this method:
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand cmd = new SqlCommand())
{
using (TransactionScope ts = new TransactionScope())
{
products.Update(dataSet, "Produktai");
offers.Update(dataSet, "Pasiulimai");
ts.Complete();
}
}
connection.Close();
}
}
catch
{ }
In the class constructor i already have a SqlDataAdapter and SqlCommandBuilder declared. My connection string is in App.config and it looks like this:
<connectionStrings>
<add name="connectionString" connectionString="server=ANDREW-PC\LTSMSQL;database=MarketDB; Integrated Security=true;" providerName="System.Data.SqlClient" />
In my program I already assigned this connection string parameter to string variable. Here is a code sample:
private string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
Any ideas how I can fix this error?
The command isn't assigned to the connection. You need to use the sqlcommand constructor like so: new SQLCommand(connection, "querystring"). I also suggest you use a newer technology such as ORM. I used basic ADO.NET data access before I found Fluent NHibernate, and Fluent is so much easier to use :-)
why are you using System.Transaction.TransactionScope? are you dealing with multiples transaction aware data sources such as sql server and oracle, where you need a transaction manager to coordinate the transaction? if not, then why don't you create a transaction from the connection?
using (var connection = new System.Data.SqlClient.SqlConnection(" "))
{
connection.Open();
var tran = connection.BeginTransaction();
var cmd = new System.Data.SqlClient.SqlCommand();
cmd.Connection = connection;
cmd.Transaction = tran;
//I dont know how the sql command relates to this
products.Update(dataSet, "Produktai");
offers.Update(dataSet, "Pasiulimai");
//commit
tran.Commit();
}
Ok, I just found the problem. For some reasons my database was "read only" for me when I connect with visual studio. I have changed some settings in the database and now it works fine. Thanks for your answers. :)

Using TransactionScope with System.Data.OracleClient - TransactionAbortedException

My system write some data to a SQL Server DB (2008), extracts it later and processes it some more, before writing it to an Oracle (10g) DB.
I've wrapped my SQL Server interactions in a TransactionScope but when I try the same think with my Oracle interactions I get a `TranactionAbortedException - "The transaction has aborted".
Remove the TransactionScope, and everything works OK.
I could always revert back to manually managing my own transactions, but I'm hoping there is a simple solution.
Sample code:
private static void OracleTest()
{
using (TransactionScope ts = new TransactionScope())
{
using (OracleConnection conn = new OracleConnection(connString))
{
try
{
using (OracleCommand cmd = new OracleCommand())
{
cmd.CommandText = "MyPackage.MyFunction";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Connection = conn;
OracleParameter param = cmd.Parameters.Add(new OracleParameter("field1_", "abc123"));
param = cmd.Parameters.Add(new OracleParameter("rs_", OracleType.Cursor));
param.Direction = System.Data.ParameterDirection.Output;
conn.Open();
using (OracleDataReader dr = cmd.ExecuteReader())
{
I haven't found anything to suggest that you can't use TransactionScopes with Oracle, but as you can see from my example, we're falling at the first hurdle (when we open the connection) so it's hard to see where I might be going wrong.
You may want to take a look at this; I haven't had much luck with TransactionScope in Oracle 10g either: http://forums.oracle.com/forums/thread.jspa?messageID=4127323

Categories

Resources