C# Transactionscope multiple connections - c#

I'm trying to execute a query to multiple databases by using a TransactionScope.
Here is my code; I want to execute the query on two different SQL Server databases, but in my code, while the query on the first connection executes without a problem, the second connection.Open() gets stuck.
// connList : connection string List
// query : to execute query
public DataSet SendQueryMultiConnection(List<string> connList, string query)
{
using(TransactionScope scope = new TransactionScope())
{
DataSet retDs = new DataSet();
try
{
foreach(string conn in connList)
{
if (string.IsNullOrEmpty(conn))
throw new Exception("No ConnectionData");
using (SqlConnection connection = new SqlConnection(conn))
{
connection.Open();
using (SqlCommand command = new SqlCommand(query, connection))
{
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable dt = new DataTable();
adapter.Fill(dt);
retDs.Tables.Add(dt);
}
}
}
}
scope.Complete();
}
catch (Exception ex)
{
//...
}
return redDs;
}
}
I can't guess a clue, what is the problem in my code?
Thanks for reading.

Related

DataTable returns null WPF

I want to display data in a datagrid based on value selected in a ComboBox but my datatable function returns null and nothing is displayed in the datagrid.
public DataTable ReadData(User user)
{
using (SqlConnection db = new SqlConnection(AppConnect.Connection))
{
string query = "SELECT moduleCode,moduleName,modCredits,modHrsLeft FROM [Module] WHERE userName=#userName";
try
{
using (SqlCommand command = new SqlCommand(query, db))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
command.Parameters.AddWithValue("#userName", user.UserName);
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
table = new DataTable();
dataAdapter.Fill(table);
}
}
db.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return table;
}
}
This function is in a class library. The function is called in the WPF project, this is the code:
private void BtnDisplay_Click(object sender, RoutedEventArgs e)
{
string userName = CmboxUserN.SelectedItem.ToString();
User user1 = new User
{
UserName = userName
};
DataTable table = data.ReadData(user1);
gridModules.DataContext= table;
}
Try these changes. It seems that the line table = new DataTable(); is never being hit. You don't need to check to make sure the connection is in a closed state because you are using a using statement.
public DataTable ReadData(User user)
{
using (SqlConnection db = new SqlConnection(AppConnect.Connection))
{
string query = "SELECT moduleCode,moduleName,modCredits,modHrsLeft FROM [Module] WHERE userName=#userName";
try
{
using (SqlCommand command = new SqlCommand(query, db))
{
db.Open();
command.Parameters.AddWithValue("#userName", user.UserName);
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
table = new DataTable();
dataAdapter.Fill(table);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return table;
}
}
Also, you don't need to .Close() the connection because the SqlConnection is in a using statement, which means the object will be disposed of when the scope of the using statement is exited.
One other tip: You should change the code to have the try catch around everyting and if there are any errors at all, log the errors to console and return a new DataTable().

Cannot Use DbContext.Query inside a transaction

I am using EF6 to query a backend database. User can customize a temporary table and query the data from the temporary table. I am using
DataTable result = context.Query(queryStatement);
to get the result and it has been working fine.
Now the query is needed among a serious of other sqlcommand and a transaction is needed. So I have
public static DataTable GetData()
{
using (MyDbContext context = new MyDbContext())
using (DbContextTransaction tran = context.Database.BeginTransaction())
{
try
{
int rowAffected = context.Database.ExecuteSqlCommand(
"UPDATE [MyDb].dbo.[TableLocks] SET RefCount = RefCount + 1 WHERE TableName = 'TESTTABLE1'");
if (rowAffected != 1)
throw new Exception("Cannot find 'TestTable1'");
//The following line will raise an exception
DataTable result = context.Query("SELECT TOP 100 * FROM [MyDb].dbo.[TestTable1]");
//This line will work if I change it to
//context.Database.ExecuteSqlCommand("SELECT TOP 100 * FROM [MyDb].dbo.[TestTable1]");
//but I don't know how to get the result out of it.
context.Database.ExecuteSqlCommand(
"UPDATE [MyDb].dbo.[TableLocks] SET RefCount = RefCount - 1 WHERE TableName = 'TestTable1'");
tran.Commit();
return result;
}
catch (Exception ex)
{
tran.Rollback();
throw (ex);
}
}
}
But this throws an exception while executing context.Query
ExecuteReader 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.
And when I read this article: https://learn.microsoft.com/en-us/ef/ef6/saving/transactions
It says:
Entity Framework does not wrap queries in a transaction.
Is it the reason cause this issue?
How can I use context.Query() inside a transaction?
What else I can use?
I tried all other method, none of them work - because the return datatype cannot be predicted before hand.
I just realized that, the Query method is defined in MyDbContext!
public DataTable Query(string sqlQuery)
{
DbProviderFactory dbFactory = DbProviderFactories.GetFactory(Database.Connection);
using (var cmd = dbFactory.CreateCommand())
{
cmd.Connection = Database.Connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = sqlQuery;
using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
{
adapter.SelectCommand = cmd;
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}
May be you are missing this section -
you are free to execute database operations either directly on the
SqlConnection itself, or on the DbContext. All such operations are
executed within one transaction. You take responsibility for
committing or rolling back the transaction and for calling Dispose()
on it, as well as for closing and disposing the database connection
And then this codebase -
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var sqlTxn =
conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
{
try
{
var sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Transaction = sqlTxn;
sqlCommand.CommandText =
#"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
sqlCommand.ExecuteNonQuery();
using (var context =
new BloggingContext(conn, contextOwnsConnection: false))
{
context.Database.UseTransaction(sqlTxn);
var query = context.Posts.Where(p => p.Blog.Rating >= 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
}
sqlTxn.Commit();
}
catch (Exception)
{
sqlTxn.Rollback();
}
}
}
Specially this one -
context.Database.UseTransaction(sqlTxn);
Sorry guys, as mentioned above, I thought the Query method is from EF, but I examined the code and found it is actually coded by another developer, defined in class MyDbContext. Since this class is generated by EF, and I never think somebody have added a method.
It is
public DataTable Query(string sqlQuery)
{
DbProviderFactory dbFactory = DbProviderFactories.GetFactory(Database.Connection);
using (var cmd = dbFactory.CreateCommand())
{
cmd.Connection = Database.Connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = sqlQuery;
//And I added this line, then problem solved.
if (Database.CurrentTransaction != null)
cmd.Transaction = Database.CurrentTransaction.UnderlyingTransaction;
using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
{
adapter.SelectCommand = cmd;
DataTable dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}

SqlHelper DataAdapter

I am using a SqlHelper class which has common methods for CRUD operations.
public static void Fill(DataSet dataSet, String procedureName)
{
SqlConnection oConnection = new SqlConnection(DBInterface.ConnectionString);
SqlCommand oCommand = new SqlCommand(procedureName, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
SqlDataAdapter oAdapter = new SqlDataAdapter();
oAdapter.SelectCommand = oCommand;
oConnection.Open();
using (SqlTransaction oTransaction = oConnection.BeginTransaction())
{
try
{
oAdapter.SelectCommand.Transaction = oTransaction;
oAdapter.Fill(dataSet);
oTransaction.Commit();
}
catch
{
oTransaction.Rollback();
throw;
}
finally
{
if (oConnection.State == ConnectionState.Open)
oConnection.Close();
oConnection.Dispose();
oAdapter.Dispose();
}
}
}
Now in my code, I am calling this method as,
private void BindCustomers()
{
DataSet dsCust = new DataSet();
SqlHelper.Fill(dsCust, "getCustomers");
--then I bind this dataset to datagridview
}
This all works fine. Now I want to update the data in the database. But I am confused how do I call DataAdatpaer.Update(dataset) here to update the changes made in datagridview into database. Is this possible here? Or I need to do it conventionally to find the updated row and call the ExecuteNonQuery function in the SqlHelper? Is there anything which can be done to use dataadapter.update(ds)
Thanks
You don't need to hide data adapter, or if for any reason you did so, you need to expose a method in your class to push updates to server.
Example
Public class SqlHelper
{
string commandText;
string connectionString;
public SqlHelper(string command, string connection)
{
commandText = command;
connectionString = connection;
}
public DataTable Select()
{
var table = new DataTable();
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
adapter.Fill(table)
return table;
}
public void Update(DataTable table)
{
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
{
var builder = new SqlCommandBuilder(adapter);
adapter.Update(table);
}
}
}
By calling this method in your code you can perform all crud operation select, update, delete and insert. you just need to pass connection string, procedure name and parameters list. Using this method you will retrieve data in the DataTable.if anyone want Dataset(Multiple Result) then just need to replace DataSet on the place of DataTable
SqlConnection conn = new SqlConnection("Your ConnectionString");
public DataTable ExecuteDataTable(string ProcedureName, SqlParameter[] _Param)
{
try
{
DataTable dataTable = new DataTable();
SqlCommand cmd = new SqlCommand(ProcedureName, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
if (_Param is not null)
{
for (int i = 0; i < _Param.Length; i++)
{
if (_Param[i].ParameterName is not null)
{
if (_Param[i].Value is not null)
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, _Param[i].Value);
}
else
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, DBNull.Value);
}
}
}
}
conn.Open();
SqlDataAdapter DA = new SqlDataAdapter(cmd);
DA.Fill(dataTable);
conn.Close();
return dataTable;
}
catch (Exception ex)
{
conn.Close();
throw;
}
}

My aim is to import data from SQL and export it to Oracle Database. there might be thousands of records, so I am trying it using bulk import

I have a SP at SQL side returning the required data. I need to export it to Oracle DB. I am using bulkcopy. here is the code.
string connectionString = System.Configuration.ConfigurationManager.AppSettings.Get("ConnectionString");
string ConnectionStringOracle = System.Configuration.ConfigurationManager.AppSettings.Get("ConnectionStringOracle");
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command = new SqlCommand("[SPNAME]", connection))
{
connection.Open();
SqlDataReader rdr = command.ExecuteReader();
using (OracleConnection destinationConnection = new OracleConnection(ConnectionStringOracle))
{
destinationConnection.Open();
try
{
using (Oracle.DataAccess.Client.OracleBulkCopy bulkCopy = new Oracle.DataAccess.Client.OracleBulkCopy(ConnectionStringOracle))
{
bulkCopy.DestinationTableName = "DESTTABLLENAME"; / //bulkCopy.ColumnMappings.Add(1,1);
bulkCopy.WriteToServer(rdr);
bulkCopy.Close();
bulkCopy.Dispose();
destinationConnection.Dispose();
connection.Close();
}
}
catch (OracleException ex)
{
}
}
// ...
}
// ...
}
I am getting the following error :
oracle exception external component has thrown an exception

c sharp return object from function

im facing a problem , when i creat my own class i want to create Mysql connection function and i want to use it inside all my forms i do this
program.cs
using MySql.Data.MySqlClient;
using MySql.Data.Types;
using System.Xml;
using System.IO;
public class testing
{
public string fahadt="Hello Class";
public void conncting()
{
MySqlConnection connection;
string cs = #"server=localhost;userid=root;password=;database=taxi";
connection = new MySqlConnection(cs);
try
{
connection.Open();
}
catch (MySqlException ex)
{
// MessageBox.Show(ex.ToString());
}
}
}
and in my form
private void button7_Click(object sender, EventArgs e)
{
testing fahad = new testing();
try
{
dataGridView1.Show();
fahad.conncting();
// here is error under fahad.conncting.createcommand();
MySqlCommand cmd = fahad.conncting.CreateCommand();
//cmd.CommandText = "SELECT * FROM ocms_visitors WHERE `id`='"+textBox4.Text+"'";
// MySqlDataAdapter adap = new MySqlDataAdapter(cmd);
//textBox4.Text = adap.id;
// DataSet ds = new DataSet();
// adap.Fill(ds);
// dataGridView1.DataSource = ds.Tables[0].DefaultView;
//MessageBox.Show("Yes Mysql Connection is Working Now !");
}
catch (Exception)
{
throw;
}
}
i dont know how i can do it im very new = C#
please help me and also i have another q should i use
using .... in class and form or Enough in class ?
thanks
You need to return MySqlConnection
Modify your function to:
public MySqlConnection conncting()
{
MySqlConnection connection;
string cs = #"server=localhost;userid=root;password=;database=taxi";
connection = new MySqlConnection(cs);
try
{
connection.Open();
return connection;
}
catch (MySqlException ex)
{
return null;
// MessageBox.Show(ex.ToString());
}
}
To answer your initial question, it looks to me like you have already created the connection you were looking for. Next steps for you would be to learn about the SqlCommand Class.
For reference/edification, try this link, and happy coding!
I suspect you want to return a connection from your conncting() method?
public MySqlConnection conncting()
{
string cs = #"server=localhost;userid=root;password=;database=taxi";
MySqlConnection connection = new MySqlConnection(cs);
connection.Open();
return connection;
}
To answer your second question, yes, using using {..} blocks is a good idea for IDisposable instances whenever possible. This includes connections, commands, data adapters, etc. The following might be a reasonable pattern:
using (MySqlConnection conn = fahad.conncting())
using (MySqlCommand cmd = new MySqlCommand("select * from table", conn))
using (MySqlDataAdapter da = new MySqlDataAdapter(cmd))
using (DataTable dt = new DataTable())
{
da.Fill(dt);
// do something with datatable
}

Categories