Right way of using transaction scope with Devart Connectory Mysql - c#

We have been using C# and mysql with .net connector but with individual commit where at times it fail to commit all. So we are now moving to this tool http://www.devart.com/dotconnect/mysql/ which supports distributed transaction. All works fine just that we are not too sure how to put the multiple connection. Method 1 is nesting each of the connection into one another. Method 2 is separately. Where we should be closing the connection or is handled by the transScope.Complete(); and transScope.Dispose();
Method 1.
using (TransactionScope transScope = new TransactionScope())
{
string myConnStringLocal = "User Id=***;Password=****;Host=" + globalSettings.settingLocalIP + ";Database=" + globalSettings.settingLocalDB;
using (MySqlConnection connectionLocal = new MySqlConnection(myConnStringLocal))
{
connectionLocal.open()
string myConnStringCentral = "User Id=***;Password=*****;Host=" + globalSettings.settingCentralIP + ";Database=" + globalSettings.settingCentralDB;
using (MySqlConnection connectionCentral = new MySqlConnection(myConnStringCentral))
{
connectionCentral.Open();
string myConnStringCentralCopy = "User Id=*****;Password=*****;Host=" + globalSettings.settingCentralCopyIP + ";Database=" + globalSettings.settingCentralCopyDB;
using (MySqlConnection connectionCentralCopy = new MySqlConnection(myConnStringCentralCopy))
{
connectionCentralCopy.Open();
}
}
}
if (rollbackBoolean == 0)
transScope.Complete();
else
transScope.Dispose();
}
Method 2
using (TransactionScope transScope = new TransactionScope())
{
string myConnStringLocal = "User Id=***;Password=****;Host=" + globalSettings.settingLocalIP + ";Database=" + globalSettings.settingLocalDB;
using (MySqlConnection connectionLocal = new MySqlConnection(myConnStringLocal))
{
connectionLocal.open()
}
string myConnStringCentral = "User Id=***;Password=*****;Host=" + globalSettings.settingCentralIP + ";Database=" + globalSettings.settingCentralDB;
using (MySqlConnection connectionCentral = new MySqlConnection(myConnStringCentral))
{
connectionCentral.Open();
}
string myConnStringCentralCopy = "User Id=*****;Password=*****;Host=" + globalSettings.settingCentralCopyIP + ";Database=" + globalSettings.settingCentralCopyDB;
using (MySqlConnection connectionCentralCopy = new MySqlConnection(myConnStringCentralCopy))
{
connectionCentralCopy.Open();
}
if (rollbackBoolean == 0)
transScope.Complete();
else
transScope.Dispose();
}

Both methods are correct. TransactionScope will be used together with dotConnect for MySQL in both cases. The following code is not required:
else
transScope.Dispose();
because the Dispose method is called automatically when exiting the using (TransactionScope transScope = new TransactionScope()) block.
... I went through it meaning in my case the transactionScope will close it when I call the complete rite...
No, the transScope.Complete() does not close the connection. If the connection is closed inside the using block for TransactionScope, the connection object will be closed, however the internal connection will stay open for sending changes to the database when calling transScope.Dispose() if it is preceeded by the transScope.Complete() call. transScope.Dispose() closes the internal connection if the Close or Dispose method was called for the connection object. If the connection object was not closed, transScope.Dispose() does nothing to the connection.
... Another thing in each of my connection I keep track of try and catch and if there is any error I flagged the rollbackBoolean to 1 so then it wont complete and whole transaction should be rollbacked is that a correct mechanism? ...
If an error occurred, just don't call Complete. If the Complete method was not called, the transaction will be rolled back when executing the Dispose method.
Here is the example with a try/catch block and rolling back the transaction in case of error:
using (TransactionScope transScope = new TransactionScope())
{
try
{
string myConnStringLocal = "User Id=***;Password=****;Host=" + globalSettings.settingLocalIP + ";Database=" + globalSettings.settingLocalDB;
using (MySqlConnection connectionLocal = new MySqlConnection(myConnStringLocal))
{
connectionLocal.Open();
}
string myConnStringCentral = "User Id=***;Password=*****;Host=" + globalSettings.settingCentralIP + ";Database=" + globalSettings.settingCentralDB;
using (MySqlConnection connectionCentral = new MySqlConnection(myConnStringCentral))
{
connectionCentral.Open();
}
string myConnStringCentralCopy = "User Id=*****;Password=*****;Host=" + globalSettings.settingCentralCopyIP + ";Database=" + globalSettings.settingCentralCopyDB;
using (MySqlConnection connectionCentralCopy = new MySqlConnection(myConnStringCentralCopy))
{
connectionCentralCopy.Open();
}
transScope.Complete();
Console.WriteLine("Transaction is completed");
}
catch (Exception)
{
Console.WriteLine("Transaction is rolled back");
}
}

Related

Running a command although the connection is close adomd

I have read the Mirosoft Document.When we open a connection and then close it, it is possible to use the session.
I have written this block of code to run a command but I get an error message, which says there is no connection. Do you have any Idee how can I close the connection, but use the session to run a cammand:
try
{
using (AdomdConnection adomdConnection = new AdomdConnection("MY Connection String"))
{
adomdConnection.Open();
adomdConnection.Close(false);
while (true)
{
String query = #"EVALUATE { BLANK()}";
AdomdCommand adomdCommand = new AdomdCommand(query);
Console.WriteLine(adomdConnection.SessionID.ToString() + " " + DateTime.Now.ToString());
AdomdDataReader reader = adomdCommand.ExecuteReader();
reader.Close();
System.Threading.Thread.Sleep(30000);
}
}
}
catch(AdomdConnectionException ex)
{
Console.WriteLine(ex.Message.ToString());
}
In the examples shown in the document you list, it has:
/*First, try to connect to the specified data source.
If the connection string is not valid, or if the specified
provider does not support sessions, an exception is thrown. */
objConnection.ConnectionString = connectionString;
objConnection.Open();
// Now that the connection is open, retrieve the new
// active session ID.
strSessionID = objConnection.SessionID;
// Close the connection, but leave the session open.
objConnection.Close(false);
return strSessionID;
And in your code specifically, you have:
adomdConnection.Open();
adomdConnection.Close(false);
while (true)
{
String query = #"EVALUATE { BLANK()}";
AdomdCommand adomdCommand = new AdomdCommand(query);
Console.WriteLine(adomdConnection.SessionID.ToString() + " " +
DateTime.Now.ToString());
AdomdDataReader reader = adomdCommand.ExecuteReader();
reader.Close();
System.Threading.Thread.Sleep(30000);
}
Wouldn't you want to have this instead (based on the example given)?
adomdConnection.Open();
while (true)
{
String query = #"EVALUATE { BLANK()}";
AdomdCommand adomdCommand = new AdomdCommand(query);
Console.WriteLine(adomdConnection.SessionID.ToString() + " " +
DateTime.Now.ToString());
AdomdDataReader reader = adomdCommand.ExecuteReader();
reader.Close();
System.Threading.Thread.Sleep(30000);
}
adomdConnection.Close(false);
It seems as though it's complaining because you're closing the connection before you even use it, according to the order in which your code looks to be operating. Try moving the adomdConnection.Close(false); after your while loop.

How to Authenticate Two Users at One Time using SQL Server and C#

I am writing an unencryption program and the business requirement is that two users must login in order for it to work. How can I write program that in C# that attempts to log both users into SQL? The users are SQL Users and I can't figure out how to authenticate SQL Users in C#.
I imagined a login screen with two user name slots and two password slots and then a shared login button. On click, user authentication happens for each user. If both authentications are successful, the file is decrypted.
Any help, advice or direction would be greatly appreciated.
Something Like this can do the trick:
private async Task TestUserAuthAsync(){
var task1 = Task.Run(AuthenticateUser("username1", "password1"));
var task2 = Task.Run(AuthenticateUser("username2", "password2"));
await Task.WhenAll(task1, task2);
}
You can do more, like return results log...
Something like Parallel.ForEach can also work.
The Authenticate User Method would look something like this - note that the connection strings need to be modified for the specific app and the MessageBox in the Catch statement is probably too much for the average user :)
private bool AuthenticateUser()
{
// rather than picking up variable names, I am picking up the information from the text boxes on the screen.
bool retVal = false;
string cs1 = "Data Source=yourdatasource;Initial Catalog=yourcat;Persist Security Info=True;User ID=" + txtLogin1.Text.Trim() + ";Password=" + txtPW1.Text.Trim() + ";";
string cs2 = "Data Source=yourdatasource;Initial Catalog=yourcat;Persist Security Info=True;User ID=" + txtLogin2.Text.Trim() + ";Password=" + txtPW2.Text.Trim() + ";";
try
{
using (var connection = new SqlConnection(cs1))
{
connection.Open();
retVal = true;
}
}
catch (SqlException e2)
{
MessageBox.Show("User 1 Failed " + e2.ToString());
}
try
{
using (var connection = new SqlConnection(cs2))
{
connection.Open();
retVal = true;
}
}
catch (SqlException ex)
{
MessageBox.Show("User 2 Failed "+ex.ToString());
}
if (retVal)
{ MessageBox.Show("Passed"); }
else
{ MessageBox.Show("Please Try"); }
return retVal;
}
}

Fetch consumer count from RabbitMQ

I am trying to fetch the total number of consumers for a particular queue in RabbitMQ. I wrote my code in C# using RabbitMQ client for .Net.
string user = ConfigurationManager.AppSettings["rmq_user"];
string pword = ConfigurationManager.AppSettings["rmq_pass"];
string port = ConfigurationManager.AppSettings["rmq_port"];
string host = new Uri(ConfigurationManager.AppSettings["rest_api_url"]).Host;
var factory = new ConnectionFactory();
factory.Uri = "amqp://" + user + ":" + pword + "#" + host + ":" + port;
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
var consumerCount = ((RabbitMQ.Client.Impl.ModelBase)(channel)).m_consumers;
}
}
I am getting var consumerCount = ((RabbitMQ.Client.Impl.ModelBase)(channel)).m_consumers; as 0. However I can see the consumers in the RabbitMQ web and I have made sure that the consumers do exist in that queue.
Is this the correct way to get the consumer count or I am doing something wrong? I know this can be done using RabbitMQ admin but I need to do it in C#.
Please let me know if I can give some more details.
It is not working as you have not specified the queue for which you want the consumers.
You can do it as follows:
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
var queueDeclareOK = channel.QueueDeclarePassive(QueueName);
var consumerCount = queueDeclareOK.ConsumerCount;
}
}
See IModal.ConsumerCount() on docs
In golang, there is a method called QueueInspect, it will return consumer count:
queue, err := channel.QueueInspect(queueName)
if err != nil {
return errors.New("fail to inspect queue " + queueName + "err: " + err.Error())
}
fmt.Println("consumer count is ", queue.Consumers)

External table is not in the expected format thrown only in certain situations

I am trying to open an xlsx file and I get the External table is not in the expected format only when I open from a certain area of my app. So it works just fine if I call it from 1 area, but then I do the same thing from a different part of the app, calling the same code block, and it throws that exception. Same code block. Same excel file.
string connectionString = "Provider="+ GetProvider(fileName) +"; data source=" + fileName + "; " + GetExtendedProperties(fileName);
using (var connection = new OleDbConnection(connectionString))
{
connection.Open();
using (DataTable schemaTable = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables,
new object[] { null, null, null, "TABLE" }))
{
if (schemaTable != null && schemaTable.Rows != null)
{
List<string> validTables = schemaTable.Rows.OfType<DataRow>().Select(row => (string)row["TABLE_NAME"]).ToList();
validTables.ForEach(table =>
{
if (table.Contains('$'))
{
results.WorkSheets.Add(table.Trim('\'', '$'));
}
else
{
results.NamedRanges.Add(table);
}
});
}
}
}
My connection string:
Provider=Microsoft.ACE.OLEDB.12.0; data source=C:\moses\BLANKTest\Table.xlsx; Extended Properties="Excel 12.0 Xml";
Also tried the connection string taking out the "Xml" at the end. No luck.
Not sure what is going on here.

import from text file to SQL Server Database, is ADO.NET too slow?

My program is now still running to import data from a log file into a remote SQL Server Database. The log file is about 80MB in size and contains about 470000 lines, with about 25000 lines of data. My program can import only 300 rows/second, which is really bad. :(
public static int ImportData(string strPath)
{
//NameValueCollection collection = ConfigurationManager.AppSettings;
using (TextReader sr = new StreamReader(strPath))
{
sr.ReadLine(); //ignore three first lines of log file
sr.ReadLine();
sr.ReadLine();
string strLine;
var cn = new SqlConnection(ConnectionString);
cn.Open();
while ((strLine = sr.ReadLine()) != null)
{
{
if (strLine.Trim() != "") //if not a blank line, then import into database
{
InsertData(strLine, cn);
_count++;
}
}
}
cn.Close();
sr.Close();
return _count;
}
}
InsertData is just a normal insert method using ADO.NET. It uses a parsing method:
public Data(string strLine)
{
string[] list = strLine.Split(new[] {'\t'});
try
{
Senttime = DateTime.Parse(list[0] + " " + list[1]);
}
catch (Exception)
{
}
Clientip = list[2];
Clienthostname = list[3];
Partnername = list[4];
Serverhostname = list[5];
Serverip = list[6];
Recipientaddress = list[7];
Eventid = Convert.ToInt16(list[8]);
Msgid = list[9];
Priority = Convert.ToInt16(list[10]);
Recipientreportstatus = Convert.ToByte(list[11]);
Totalbytes = Convert.ToInt32(list[12]);
Numberrecipient = Convert.ToInt16(list[13]);
DateTime temp;
if (DateTime.TryParse(list[14], out temp))
{
OriginationTime = temp;
}
else
{
OriginationTime = null;
}
Encryption = list[15];
ServiceVersion = list[16];
LinkedMsgid = list[17];
MessageSubject = list[18];
SenderAddress = list[19];
}
InsertData method:
private static void InsertData(string strLine, SqlConnection cn)
{
var dt = new Data(strLine); //parse the log line into proper fields
const string cnnStr =
"INSERT INTO LOGDATA ([SentTime]," + "[client-ip]," +
"[Client-hostname]," + "[Partner-Name]," + "[Server-hostname]," +
"[server-IP]," + "[Recipient-Address]," + "[Event-ID]," + "[MSGID]," +
"[Priority]," + "[Recipient-Report-Status]," + "[total-bytes]," +
"[Number-Recipients]," + "[Origination-Time]," + "[Encryption]," +
"[service-Version]," + "[Linked-MSGID]," + "[Message-Subject]," +
"[Sender-Address]) " + " VALUES ( " + "#Senttime," + "#Clientip," +
"#Clienthostname," + "#Partnername," + "#Serverhostname," + "#Serverip," +
"#Recipientaddress," + "#Eventid," + "#Msgid," + "#Priority," +
"#Recipientreportstatus," + "#Totalbytes," + "#Numberrecipient," +
"#OriginationTime," + "#Encryption," + "#ServiceVersion," +
"#LinkedMsgid," + "#MessageSubject," + "#SenderAddress)";
var cmd = new SqlCommand(cnnStr, cn) {CommandType = CommandType.Text};
cmd.Parameters.AddWithValue("#Senttime", dt.Senttime);
cmd.Parameters.AddWithValue("#Clientip", dt.Clientip);
cmd.Parameters.AddWithValue("#Clienthostname", dt.Clienthostname);
cmd.Parameters.AddWithValue("#Partnername", dt.Partnername);
cmd.Parameters.AddWithValue("#Serverhostname", dt.Serverhostname);
cmd.Parameters.AddWithValue("#Serverip", dt.Serverip);
cmd.Parameters.AddWithValue("#Recipientaddress", dt.Recipientaddress);
cmd.Parameters.AddWithValue("#Eventid", dt.Eventid);
cmd.Parameters.AddWithValue("#Msgid", dt.Msgid);
cmd.Parameters.AddWithValue("#Priority", dt.Priority);
cmd.Parameters.AddWithValue("#Recipientreportstatus", dt.Recipientreportstatus);
cmd.Parameters.AddWithValue("#Totalbytes", dt.Totalbytes);
cmd.Parameters.AddWithValue("#Numberrecipient", dt.Numberrecipient);
if (dt.OriginationTime != null)
cmd.Parameters.AddWithValue("#OriginationTime", dt.OriginationTime);
else
cmd.Parameters.AddWithValue("#OriginationTime", DBNull.Value);
//if OriginationTime was null, then insert with null value to this column
cmd.Parameters.AddWithValue("#Encryption", dt.Encryption);
cmd.Parameters.AddWithValue("#ServiceVersion", dt.ServiceVersion);
cmd.Parameters.AddWithValue("#LinkedMsgid", dt.LinkedMsgid);
cmd.Parameters.AddWithValue("#MessageSubject", dt.MessageSubject);
cmd.Parameters.AddWithValue("#SenderAddress", dt.SenderAddress);
cmd.ExecuteNonQuery();
}
How can my program run faster?
Thank you so much!
Use SqlBulkCopy.
Edit: I created a minimal implementation of IDataReader and created a Batch type so that I could insert arbitrary in-memory data using SqlBulkCopy. Here is the important bit:
IDataReader dr = batch.GetDataReader();
using (SqlTransaction tx = _connection.BeginTransaction())
{
try
{
using (SqlBulkCopy sqlBulkCopy =
new SqlBulkCopy(_connection, SqlBulkCopyOptions.Default, tx))
{
sqlBulkCopy.DestinationTableName = TableName;
SetColumnMappings(sqlBulkCopy.ColumnMappings);
sqlBulkCopy.WriteToServer(dr);
tx.Commit();
}
}
catch
{
tx.Rollback();
throw;
}
}
The rest of the implementation is left as an exercise for the reader :)
Hint: the only bits of IDataReader you need to implement are Read, GetValue and FieldCount.
Hmmm, let's break this down a little bit.
In pseudocode what you did is the ff:
Open the file
Open a connection
For every line that has data:
Parse the string
Save the data in SQL Server
Close the connection
Close the file
Now the fundamental problems in doing it this way are:
You are keeping a SQL connection open while waiting for your line parsing (pretty susceptible to timeouts and stuff)
You might be saving the data line by line, each in its own transaction. We won't know until you show us what the InsertData method is doing
Consequently you are keeping the file open while waiting for SQL to finish inserting
The optimal way of doing this is to parse the file as a whole, and then insert them in bulk. You can do this with SqlBulkCopy (as suggested by Matt Howells), or with SQL Server Integration Services.
If you want to stick with ADO.NET, you can pool together your INSERT statements and then pass them off into one large SQLCommand, instead of doing it this way e.g., setting up one SQLCommand object per insert statement.
You create the SqlCommand object for every row of data. The simplest improvement would therefore to create a
private static SqlCommand cmdInsert
and declare the parameters with the Parameters.Add() method. Then for each data row, set the parameter values using
cmdInsert.Parameters["#paramXXX"].Value = valueXXX;
A second performance improvement might be to skip creation of Data objects for each row, and assign Parameter values directly from the list[] array.

Categories

Resources