I have a c# console app client and I am trying to connect to mysql database.
I have a main method called select.
This method return n of rows.
I am looping through results, and for each one I am calling another method call GetProductAttributes and after I get the results I am calling another method called UpdateProductAttributesJsonField
Here is my code :
public void Select(int shopId)
{
using(MySqlCommand cmd = new MySqlCommand("GetProducts", new MySqlConnection(connection.ConnectionString)))
{
cmd.Parameters.Add(new MySqlParameter("#last_modified_date", ""));
cmd.Parameters.Add(new MySqlParameter("#ShopId", shopId));
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection.Open();
MySqlDataReader dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (dataReader.Read())
{
var id = int.Parse(dataReader["Id"].ToString());
var attributes = GetProductAttributes(id);
var json = JsonConvert.SerializeObject(attributes.ToArray());
UpdateProductAttributesJsonField(id, json);
}
//dataReader.Close();
}
}
public Dictionary<string, string> GetProductAttributes(int Id)
{
var result = new Dictionary<string, string>();
using(MySqlCommand cmd = new MySqlCommand("GetProductAttributes", new MySqlConnection(connection.ConnectionString)))
{
cmd.Parameters.Add(new MySqlParameter("#Id", Id));
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection.Open();
//CommandBehavior.CloseConnection
MySqlDataReader dataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (dataReader.Read())
{
result.Add(dataReader["name"].ToString(), dataReader["value"].ToString());
}
//dataReader.Close();
}
return result;
}
public void UpdateProductAttributesJsonField(int productId, string json)
{
using( MySqlCommand cmd = new MySqlCommand("UpdateArticleAttributes", new MySqlConnection(connection.ConnectionString)))
{
cmd.Parameters.Add(new MySqlParameter("#articleId", productId));
cmd.Parameters.Add(new MySqlParameter("#json", json));
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
//cmd.EndExecuteNonQuery(CommandBehavior.CloseConnection); // cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
Here is my initialize method
private void Initialize()
{
server = ConfigurationManager.AppSettings["ShopwareDBServer"];
database = ConfigurationManager.AppSettings["DatabaseName"];
uid = ConfigurationManager.AppSettings["DatabaseUser"];
password = ConfigurationManager.AppSettings["DatabasePassword"];
port = "3306";
string connectionString;
connectionString = "SERVER=" + server + ";" + "Port=" + port + ";" + "DATABASE=" +
database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";" + "Convert Zero Datetime=True";
connection = new MySqlConnection(connectionString);
}
I am getting this error :
{"error connecting: Timeout expired. The timeout period elapsed prior
to obtaining a connection from the pool. This may have occurred
because all pooled connections were in use and max pool size was
reached."}
Since you are using Data Reader in your code, you need to close the connection before opening a new one.
It may seem to be a suggestion rather than answering the question, but let me show you another way to do the same thing.
You want to call 3 procedures then why you are not combining these three procedures and create a new one in the database and call that procedure. I think, this way you need only one connection and your problem will be solved.
By the way, this is not possible to call three procedures on the same connection that uses "Data Reader".
You can check whether the connection state is opened or closed; this can help you decrease your maximum connection pool size.
You can write the code like this:
If(Connection.State == Closed) {
Connection.open
}
This is because you use maximum connection pool size may be in your code you no where write connection.close() so connection size is increase with time so just make sure you close connections in finally block.
First restart MySQL so it will close all the connection and change your code accordingly. And this issue is not due to calling 3 stored procedures in one connection and if you want to do it is done in multiple way.
Either you call one stored procedure and call other procedures from the first one.
You also call stored procedures step by step from c# code as well.
Related
I need to write a huge amount of data to a postgredb. The PG connection_limit is set to 200. And I need to write ~5000 values per second. If I open a new connection for every query, I get too many clients errors. But if I use only one connection, its to slow to insert ~5000 values.
What is the correct method to manage the connection in this case?
At the moment I open and close the connection for every query
public static async Task WriteValueAsync(Item Item)
{
try
{
using (NpgsqlConnection con = new NpgsqlConnection(constring))
{
con.Open();
String sql = "INSERT INTO \"" + Item.ID + "\" (...) VALUES (...)";
NpgsqlCommand cmd = con.CreateCommand();
cmd.CommandText = sql;
await cmd.ExecuteNonQueryAsync();
cmd.Dispose();
con.Close();
}
}
catch (Exception e)
{
Debug.WriteLine("DB WriteValueAsync: " + e.ToString());
}
}
I'm trying to execute an RLOCK() command (record lock) on a FoxPro table via OleDbCommand but I need to know if the lock succeeded. In FoxPro, the RLOCK() returns .T. or .F. to indicate if it succeeded.
How do I get that result via OleDbCommand?
Here is my current code:
using(var conn = new OleDbConnection(...)) //connection string with VFPOLEDB provider
{
conn.Open();
using(var comm = new OleDbCommand())
{
string cText = #"[use table in 0] + chr(13) + "
+ #"[RLOCK(table)]";
comm.Connection = conn;
comm.CommandText = "Execute(" + cText + ")";
var result = comm.ExecuteNonQuery();
Consle.WriteLine(result);
comm.Dispose();
}
conn.Close();
conn.Dispose();
}
Right now, I'm always getting back a 1 (true) even when the Lock should not have taken place due to the fact that the record is already locked by someone else.
Thanks for your help.
Because you are not returning the result of the rlock() (and that you are using ExecuteNonQuery, when you should ask a return value and use ExecuteScalar instead). You would normally get back true with that code if you properly have used ExecuteScalar. In VFP each and every procedure returns .T. if no return value is specified (or call it a function if you will - in VFP procedure and function have no difference except name).
Here is a revised version of your code:
string myCode =
#"use c:\temp\lockTest
locked = rlock()
return m.locked
";
string strCon = #"Provider=VFPOLEDB;Data Source=c:\temp";
using (OleDbConnection con = new OleDbConnection(strCon))
{
OleDbCommand cmd = new OleDbCommand("ExecScript", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#code", myCode);
con.Open();
Console.WriteLine(cmd.ExecuteScalar());
con.Close();
}
While this code works perfectly well, I have no idea what you would do with a useless rlock() other than learning that you can't lock it due to some reason. In real life Execscript has little value.
I have a frustrating issue with a query that typically takes between 1.5-2 minutes to run (due to a lack of ability to modify this database, we cannot improve it more than this time). The query times out, despite the Command Timeout property being set to 0 (this is C# code).
Here is the code that executes the query:
public DataTable GetData()
{
DataTable results = new DataTable();
try
{
using (var sqlConnection = new SqlConnection(ConfigurationManager.AppSettings["SqlConnectionString"].ToString()))
{
String command = _query;
sqlConnection.Open();
var sqlCommand = sqlConnection.CreateCommand();
sqlCommand.CommandText = command;
sqlCommand.CommandType = CommandType.Text;
sqlCommand.CommandTimeout = 0;
SqlDataAdapter daM = new SqlDataAdapter(sqlCommand.CommandText, sqlConnection);
daM.Fill(results);
sqlConnection.Close();
}
}
catch(Exception e)
{
Console.WriteLine("Error " + e.StackTrace);
}
Console.WriteLine("Retrieving results for query " + _query);
Console.WriteLine("Total Results: " + results.Rows.Count);
return results;
}
I'm not sure where to look for the culprit. Setting a more explicit timeout does nothing, and as I said there's no way to further improve the query that we've been able to find. The connection string has the following parameters:
server =
Integrated Security = SSPI
database =
Connection Timeout = 0
Any advice of where I should look next? We are using Microsoft SQL Server.
You have set sqlCommand.CommandTimeout, but later you've created SqlDataAdapter as
SqlDataAdapter daM = new SqlDataAdapter(sqlCommand.CommandText, sqlConnection)
Here adapter implicitly creates and uses new SqlCommand (not the one you've configured) since you've passed there command text, not instance of SqlCommand.
Use another constructor of SqlDataAdapter and create it like
SqlDataAdapter daM = new SqlDataAdapter(sqlCommand)
Set timeout on SqlConnection does not work in your case, you need do it on SqlDataAdapter.
daM.SelectCommand.CommandTimeout = Value;
Google for "How do you change a SqlDataAdapter .CommandTimeout?"
I currently have a little app sends a lot of different MySQL Queries to the server. My idea was to wrap the connection, the query and the read to a function with only the actual query as a parameter.
Here is what I got:
public static MySqlDataReader mySqlRead(string cmdText)
{
string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
MySqlConnection conn = new MySqlConnection(connString);
MySqlCommand command = conn.CreateCommand();
command.CommandText = cmdText;
try
{
conn.Open();
MySqlDataReader reader = command.ExecuteReader();
return reader;
}
catch (MySqlException)
{
throw;
}
}
I connect and send the query here:
private void btnLogin_Click(object sender, EventArgs e)
{
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
MySqlDataReader orRead = ORFunc.mySqlRead("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");
while (orRead.Read())
{
MessageBox.Show(orRead["id"].ToString());
}
}
Works like a charm... BUT, as you can see above, the connection is never closed. When I add the conn.Close() behind the .ExecuteReader() the reader is empty and everything after return is of course useless.
Maybe it's a stupid question but I'm rather new to C# so please be generous, any hint is appreciated.
cheers,
PrimuS
I had a similar problem in JAVA recently, but I think the same will work for you. Essentially, you can create a class that represents a "SqlCall" object (or something). The class would have accessible members including the connection and the results. The ctor for the class would take in your query text.
Then, all you would have to do is create a new instance of that class, run the query in a method in that class (which would set and/or return the results), GET the results, and then when you are done, call close() on your class (which would then have to be coded such that it closes the connection held internally).
Technically, a better way to do this is to EXTEND the connection class itself, but as you are new to C#, I will not go into the details of doing so.
As I was writing the code below, I realized I may have not actually answered your question. But there's no point in backing out now, so here's what I have:
public class SqlCall {
private static connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
private MySqlConnection conn;
private MySqlCommand command;
private MySqlDataReader reader;
public SqlCall(String query) {
conn = new MySqlConnection(connString);
command = conn.CreateCommand();
command.CommandText = query;
}
public MySqlDataReader execute() throws Exception {
conn.Open();
reader = command.ExecuteReader();
return reader;
}
public void close() {
reader.close();
conn.close();
}
}
Your login code would be:
private void btnLogin_Click(object sender, EventArgs e) {
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
SqlCall sqlcall = new SqlCall("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'");
try {
MySqlDataReader orRead = sqlcall.execute();
while (orRead.Read())
{
MessageBox.Show(orRead["id"].ToString());
}
sqlcall.close();
} catch (Exception ex) {
// dostuff
}
}
The point is, unless you copy the data into a new datatable at the very beginning, you'll have to keep the connection open.
On a separate note, YOUR CODE IS PRONE TO SQL INJECTION. Don't know what that is? An example: if I said my username was ';DROP TABLE orUsers;--, then your entire user database would be gone. Look into stored procedures if you want a (very healthy) way around this.
You have difficulties because your idea works against the pattern expected by programs that connects to a database in NET Framework.
Usually, in this pattern you have a method that
INITIALIZE/OPEN/USE/CLOSE/DESTROY
the ADO.NET objects connected to the work required to extract or update data
Also your code has a serious problem called Sql Injection (see this famous explanation) because when you concatenate strings to form your command text you have no defense against a malicious user that try to attack your database
private void btnLogin_Click(object sender, EventArgs e)
{
string username = txtLogin.Text;
string password = ORFunc.GetMD5Hash(txtPassword.Text);
MySqlParameter p1 = new MySqlParameter("#uname", username);
MySqlParameter p2 = new MySqlParameter("#pass", pass);
string cmdText = "SELECT * FROM orUsers WHERE username = #uname AND pass = #pass"
DataTable dt = ORFunc.GetTable(cmdText, p1, p2);
foreach(DataRow r in dt.Rows)
{
Console.WriteLine(r["ID"].ToString());
}
}
public static DataTable GetTable(string cmdText, params MySqlParameter[] prms)
{
string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";";
// This is the INITIALIZE part
using(MySqlConnection conn = new MySqlConnection(connString))
using(MySqlCommand command = new MySqlCommand(cmdText, conn))
{
// OPEN
conn.Open();
DataTable dt = new DataTable();
command.Parameters.AddRange(prms);
// USE
MySqlDataReader reader = command.ExecuteReader();
dt.Load(reader);
return dt;
} // The closing brace of the using statement is the CLOSE/DESTROY part of the pattern
}
Of course this is a generic example and in my real work I don't use very often these generic methods and prefer to write specialized data access code that return the base object needed to the upper layer of code
I am developing a web app and have encountered a problem. I need to insert username and ip address into a SQL database table "log" when someone tries (successfully or unsuccessfully) to login. ID, time and date are inserted automatically...
For some reason I am unable to get it working in my login form. INSERT statement works ok if I initiate it from another form, but I can't get it working together with the SELECT statement I use for checking login credentials.
I have tried different solutions, but none of them inserts data into the table... It does not throw an error, it just doesn't insert a new row into "log" table.
Any help is appreciated. :)
protected void btnLog_Click(object sender, EventArgs e)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConn"].ToString()))
{
string username = null;
string password = null;
string ipAddress = null;
SymCryptography cryptic = new SymCryptography();
SqlCommand cmdSelect = new SqlCommand();
SqlCommand cmdLog = new SqlCommand();
SqlDataReader myReader = null;
cmdSelect.Connection = conn;
cmdLog.Connection = conn;
cmdSelect.CommandText = "SELECT * FROM uporabniki WHERE up_ime=#up_ime AND geslo=#geslo";
cmdSelect.CommandType = CommandType.Text;
cmdLog.CommandText = "INSERT INTO log (up_ime, ip) VALUES (#up_ime, #ip)";
cmdLog.CommandType = CommandType.Text;
cmdSelect.Parameters.Add("#up_ime", SqlDbType.NVarChar, 20).Value = tbUsr.Text;
cmdSelect.Parameters.Add("#geslo", SqlDbType.NVarChar, 20).Value = cryptic.Encrypt(tbPwd.Text);
cmdLog.Parameters.Add("#up_ime", SqlDbType.NVarChar, 20).Value = tbUsr.Text;
cmdLog.Parameters.Add("#ip", SqlDbType.NVarChar, 20).Value = ipAddress;
conn.Open();
try
{
//cmdLog.ExecuteNonQuery(); I tried it here, but it doesn't work
myReader = cmdSelect.ExecuteReader();
if (myReader.Read())
{
username = myReader["up_ime"].ToString();
password = myReader["geslo"].ToString();
Session["rights"] = myReader["pravice"];
Session["login"] = "OK";
pravice = true;
}
myReader.Close();
//cmdLog.ExecuteNonQuery(); I tried it here, but it doesn't work
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
conn.Close();
}
//I tried to open connection again, but stil INSERT does not work
/* using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConn"].ToString()))
{
string ipAddress = null;
SqlCommand cmdLog = new SqlCommand();
cmdLog.Connection = conn;
cmdLog.CommandText = "INSERT INTO log (up_ime, ip) VALUES (#up_ime, #ip)";
cmdLog.CommandType = CommandType.Text;
cmdLog.Parameters.Add("#up_ime", SqlDbType.NVarChar, 20).Value = tbUsr.Text;
cmdLog.Parameters.Add("#ip", SqlDbType.NVarChar, 20).Value = ipAddress;
conn.Open();
try
{
cmdLog.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
conn.Close();
}
if (pravice == true)
{
Response.Redirect("Default.aspx");
}
else
{
Response.Redirect("Login.aspx");
}*/
}
Your not executing your cmdLog.ExecuteNonQuery(); statement.
Also, try opening a query window in your sql database and run the following against it.
INSERT INTO log (up_ime, ip) VALUES (<some time>, <test ip text>)
If the error lies in sql server you should be returned an error message stating if the problem lies withing SQL Server.
Also try changing:
cmdLog.Parameters.Add("#up_ime", SqlDbType.NVarChar, 20).Value = tbUsr.Text;
cmdLog.Parameters.Add("#ip", SqlDbType.NVarChar, 20).Value = ipAddress;
To:
cmdLog.Parameters.Add("#up_ime", tbUsr.Text);
cmdLog.Parameters.Add("#ip", ipAddress);
Check whether your connection string value is ok. Also put a break point on the:
conn.Open();
See whether you get an error?
If not then that means your connectionstring is ok.
Uncomment this line after Try{}
//cmdLog.ExecuteNonQuery(); I tried it here, but it doesn't work
and put a break point on it .. see whether you get an error?
Also from your code I can see that you are inserting a null ipaddress value into the table. See whether the column in your database is allowed to accept nulls??
The using statement will clean up the connection in the garbage collection process which happens at an indeterminable time. This is why 1 of the orders of execution you've tried doesn't work (the connection may not be usable). Suggest you do either:
Both operations within 1 using (connection), make sure you close the connection in a finally() block and re-init your command against the newly opeend connection.
Have 2 different connections conn1 and conn2 and perform the separate operations in each.
EDIT: I have re-read and you say no errors occur? - try checking the result of ExecuteNonQuery (it returns an integer to show the number of rows that have been affected - if this is zero there is a problem with your insert statement).