I have a webservice running on IIS, which contains the class to connect to the SQL server. Most of the time, we are able to connect the SQL server using the below code. But some time we could not connect the sql server. We are not getting any error. Here is the source code:
public SqlConnection DbConnectSql()
{
string str = "Server=xxxx\xxx;database=production;Timeout=60000;user id=sa;password=888*;";
_con = new SqlConnection(str);
if (_con.State == ConnectionState.Open)
_con.Close();
_con.Open();
return _con;
}
We are not getting any response during the execution of _con.Open();. We could not understand why we are not getting any response. I have to restart the IIS every 2 days to open the SQL connection. Can anyone let me know the why I have to restart the IIS to work _con.Open(); method?
Here's an interleaving of two calls to DbConnectSql that will a) end up with two threads sharing use of one connection object and b) leaks an open connection object:
//Thread 1
public SqlConnection DbConnectSql()
{
string str = ...;
_con = new SqlConnection(str);
if (_con.State == ConnectionState.Open)
_con.Close();
_con.Open();
//Thread 2
public SqlConnection DbConnectSql()
{
string str = ...;
//-->Look, thread 2 is overwriting _con-->
_con = new SqlConnection(str);
return _con;
}
if (_con.State == ConnectionState.Open)
_con.Close();
_con.Open();
return _con;
}
If we're lucky, then the first caller won't try to use the connection object before the second caller gets around to calling Open on it1. But the connection created by the first caller has been opened and now nobody has a reference to it.
You'll be getting errors because the connection pool (eventually) becomes exhausted and so the Open call will throw an exception. Why you don't see the exception isn't diagnosable from the code shown.
You're probably being lucky to last 2 days at a time, because these connections are eligible for garbage collection and so will eventually be returned to the connection pool.
Far better to just share the connection string around. Construct SqlConnection (and SqlCommand) objects in using statements that keep them nicely locally scoped and ensures that they are cleaned up neatly. You don't need this DbConnectSql function. It's doing more harm than good.
1And there are likely to be other possible errors here, if one caller tries to retrieve a result set whilst the other caller is still retrieving one.
Related
I would like to detect connection state to MySql database. My database is deployed in different server than my app and there is good chances to lose connection to it via network. So I have to take this scenario into consideration.
Here is what I tried so far (a simplified test example):
static string connectionString = "***";
public static MySqlConnection Connection;
static System.Timers.Timer _timer;
static void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
if (Connection.State != System.Data.ConnectionState.Open)
Connection.Open();
// Call method to invoke MySqlCommand.ExecuteNonQuery
mysqlCommand.ExecuteNonQuery();
}
catch (MySqlException ex)
{
Console.WriteLine("SQL EXCEPTION: " + ex);
// Handle all type of database exceptions
switch(ex.Number)
{...}
}
catch (Exception ex)
{
Console.WriteLine("OTHER EXCEPTION: " + ex);
}
}
static void Main(string[] args)
{
Connection = new MySqlConnection(connectionString);
_timer = new System.Timers.Timer(3000);
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true;
Console.ReadKey();
}
If the connection to MySql is lost, I got a general exception:
IOException : Unable to write data to the transport connection: An
established connection was aborted by the software in your host
machine.
I was expecting MySqlException to be fired but that was not the case.
Also, if the connection to MySql is restored, I still get the IOException instead of executing the query. Seems like, MySqlConnection object has not been updated and it doesn't care about new connection state.
What's the best way to handle connection lost exception?
How can I refresh MySqlConnection when connection is restored?
Note: that I can't instantiate a new MySqlConnection object for each new query, because the program I'm trying to change has a Singleton of type MySqlConnection which is initialized only once. I know that's a bad design but I don't want to change this design now. I just want to catch connection lost exception and try to refresh MySqlConnection to continue to work correctly.
If your MySqlConnection instance loses its connection to your MySQL server, you cannot expect that instances's connection to be restored automatically or otherwise.
You need to try to reconnect with a new instance of MySqlConnection. The one that has lost the connection is now in a terminal state and cannot be reused.
To do this, I suppose you could do something like this
...
catch (MySqlException ex)
{
if (/*ex is a connection drop */) {
Connection?.Dispose();
Connection = new MySqlConnection(...);
Connection.ConnectionString = /* your connection string */;
Connection.Open();
}
else {
throw;
}
}
You are correct that your design has a flaw. Whether or not your flaw is fatal is hard to tell without testing.
These Connection instances are not thread safe or in any way reentrant. If you use one in a timer handler or thread, you may only use it in that context. Otherwise, if it's already in use when your timer or thread is invoked, things will get dicey. If you're lucky you'll get an exception. If you're less lucky your MySQL server will receive gibberish from your client and detect it. If you're even less lucky your data will get scrambled up.
ADO.NET and the MySqlConnection object implement connection pooling. This matters because it makes opening connections, using them, and then closing them, a lot cheaper than you might have expected.
Sometimes MySQL drops connections that your programs have held open for long periods of time. This post may help if that is your problem.
How can I change the default Mysql connection timeout when connecting through python?
When you lost your connection by networks problems, the connection object does not change Status property so evaluate it before executting commands doesn't work.
However, the database property (connection.database) goes to empty string so you can evaluate it so can close the connection an restores it:
oConn is an instance of MySQLConnection (it works on odbcconnection)
[VB.NET]
If Not IsNothing(oConn) Then
If (oConn.Database.Equals(String.Empty)) Then oConn.Close()
End If
[C#]
If (Not IsNothing(oConn)){
If (oConn.Database.Equals(String.Empty)) oConn.Close();
}
I am creating a console application and keep the SQL operations in a separate class file. When I execute the application it raises an exception:
ExecuteNonQuery requires an open and available Connection. The connection's current state is closed.
However, in SQL class file's constructor I wrote the code for SqlConnection.Open().
Code for Main application:
using SQL;
class MyClass
{
SQL.executeSQL runSQL=new SQL.executeSQL();
static void Main(string[] args)
{
CheckCounts();
}
public void CheckCounts()
{
string sql="select count(*) from table_name";
runSQL.executeQuery(sql);
}
}
Code for SQL class file:
public class executeSQL
{
SqlConnection con=new SqlConnection(ConfigurationManager.ConnectionString["dbConnection"].ToString());
public executeSQL()
{
if(con.State!=ConnectionState.Open)
{
con.Open();
}
}
public void executeQuery(string sql)
{
SqlCommand cmd=new SqlCommand(sql,con);
cmd.ExecuteNonQuery();
}
}
When I execute the application for the first time for that day it raises the exceptions as follows
ExecuteNonQuery requires an open and available Connection. The connection's current state is closed.
But for next time it runs properly without any exception for the whole day.
Again if I run the application for the next day it raises the same exception and consecutive successful execution.
What should I do to run the application successfully for the first time of the day?
I created a batch file for the application and I scheduled the task using the Task Scheduler with the batch file. If I run the application manually I do not get the error. Using the Task Scheduler, I am getting the error.
This can happen for a few reasons.
Chances are, you're connecting to a remote SQL server.
If this is the case, then there are a variety of reasons why the connection may be closed on you by the time you make your call.
Things like network dropouts etc can cause the connection between your app and SQL to break and require reconnection.
It can also be caused by the agreed timeout between your app and SQL being exceed, however I doubt this is the case given your code sample.
I have personally experienced issues where I have made what appears to be a valid connection to the server, and then on the very next line attempt to make a SQL query, only to receive the same error as yourself.
In those instances, I've added a check before making my SQL query around the SQLConnection.State property, which can return any of the following:
Broken
The connection to the data source is broken. This can occur only after
the connection has been opened. A connection in this state may be
closed and then re-opened. (This value is reserved for future versions
of the product.)
Closed
The connection is closed.
Connecting
The connection object is connecting to the data source.
Executing
The connection object is executing a command. (This value is reserved
for future versions of the product.)
Fetching
The connection object is retrieving data. (This value is reserved for
future versions of the product.)
Open
The connection is open.
If the SqlConnection.State is Open, then I run my SQL query as intended.
If it's Closed, I attempt to re-open it then re-call my SQL query.
If it's Connecting, I pause for a specific amount of time, then retest.
Depending on how you want your app to perform, I would look at either moving the creation of the SQLConnection into the method that makes the SQL call, as follows:
public class executeSQL
{
private void CheckSQLConnection(SqlConnection con)
{
...
}
public void executeQuery(string sql)
{
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["dbConnection"].ToString()))
{
con.Open();
using (var cmd = new SqlCommand(sql, con))
{
CheckSQLConnection();
cmd.ExecuteNonQuery();
}
con.Close();
}
}
}
As in my example, it's also worth implementing using statements around your SQLConnection and SQLCommand objects to ensure they're properly disposed of and don't hang around, potentially holding onto SQL connections you might need.
Hope this helps!
We have a web server which connects to a database using a single connection string, which makes it a strong candidate for being able to use a Connection Pool.
Do we need one SqlConnection object, or many?
i.e. Should we set up one connection in shared memory, and use that each time, or should we create a new one each time we want to use any connection object?
Is it the call to .Open() which assigns it from a pool, or the creating of a new object with the same connection string?
Also, do we need to call .Close() on the connection before it's released back into the pool, or is the variable going out of scope enough?
I read somewhere (I forget where exactly - sorry) that you shouldn't call close on connections in a pool, because it removes them from the pool somehow.
It’s worth bearing in mind that an identical connection string will re-use a connection which has been returned the the pool, but changing it in any way will cause a new connection to be made to the server.
i.e.
Assuming that SQLBox has IP 10.0.0.1
using (var conn = new SqlConnection(#"Server=10.0.0.1;...") {
conn.Open();
.. Do work ..
conn.Close();
}
using (var conn = new SqlConnection(#"Server=SQLBox;…") {
conn.Open(); // This will *NOT* reuse the connection from the pool.
.. Do work ..
conn.Close();
}
using (var conn = new SqlConnection(#"Server=10.0.0.1;...") {
conn.Open(); // This *WILL* reuse the connection from the pool.
.. Do work ..
conn.Close();
}
You should open a separate connection every time you need to access a database and close it once you're done with all the access. Do not keep your connections open for too long. It is not necessary and modern databases are definitely very good at handling multiple connections.
You can leave the management of the connection pool to SQL Server - it will do a good job as long as you'll not try to prevent it.
It's best to use local connection object and not share it across multiple parts of the code. You can use using pattern to make sure your connection will be closed:
using (SqlConnection conn = new SqlConnection("connectionstring"))
{
conn.Open();
// use your connection here
}
I have a C# Console Application that is essentially a long batch process that processes data on a nightly basis across many different databases. What is the proper or preferred way to test basic database connectivity at the beginning of this type of program? I ran into the issue of having an expired database password for one of my connections that was caught by exception handling but I want to test for basic connectivity at the very beginning.
Would a simple SELECT query suffice or is there a more efficient way of doing this for numerous databases?
IMHO the simplest way is trying to connect to database and, if you have a failure, you give up.
As you're running a night batch, it's not important to understand immediately the reason and solve it.
So something like this
using(SqlConnection conn = new SqlConnection(connectionString))
{
try
{
conn.Open();
// Do what you please here
}
catch (Exception ex)
{
// Write error to file
File.Append(...,
DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + " " +
ex.Message);
}
finally
{
conn.Close();
}
}
Next morning you can check file for errors...
'Connection.open`
is the simple way to determine if you can connect to db or not.
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
}
If you get a SqlException with number 18487 or 18488 it indicates the password has been changed.
Connection.changePassword
You don't need to run any query.
If you use SqlConnection passing the connection string, you can just try to Open() the connection and you'll get an exception if you cannot connect
Something like:
try
{
var cnn = new SqlConnection(connectionString);
cnn.Open();
}
catch
{
// connection failed, do something
}
Opening (and then closing) a connection should be sufficient to test the password. however, this does not tell you , if a db-user has permissions to access specific tables.
I get this exception in my error log..
The timeout period elapsed prior to
obtaining a connection from the pool.
Restarting the IIS makes me to login back to my app.. so where do you think the issue is? Is it in IIS property setting?
Code is something like this:
MySqlConnection connection = null;
try
{
connection = MySqlConnectionHandler.OpenConnection();
string strText =string.Empty;
strText = "<<sql statement>>";
using (MySqlCommand cmd = new MySqlCommand(strText, connection))
{
cmd.Parameters.Add(new MySqlParameter("ID", MySqlDbType.Int32));
cmd.Parameters["ID"].Value = 100;
cmd.ExecuteNonQuery();
}
}
finally
{
MySqlConnectionHandler.CloseConnection(connection);
}
My CloseConnection method internally has
if (con != null)
{
con.Close();
con.Dispose();
}
That almost always means you aren't calling Dispose() on your connections, and you have saturated the pool until GC runs - which it probably won't if your app is now dead. Essentially, you have opened connections successively over a period of time (without ever giving them back), and now there are none left. Solution: give them back to the pool when you are done.
Whenever you use something like a connection, use a using statement, or some other mechanism to ensure it is disposed promptly when you are done with it. The same applies to anything IDisposable, but connections are particularly vulnerable to this.
Since you say you do call Dispose()...
I would try two things. Temporarily increase your connection timeout to see if that's the issue:
MySqlCommand.CommandTimeout = 45; //in seconds
This will slow your application so you should only use it for troubleshooting. If it is you need to optimize your SQL query.
Alternately, your connection pool may be exceeded, so you may need to bump that up.