Switching to local database when data connection unavaliable - c#

I'm in the process of building a mobile companion app for a database system.
As the mobile app won't always have access to a data connection, I need to save any record changes to the local database on the tablet/phone when the internet is unavailable.
My initial thought was to simply use something like
public bool TestConn()
{
SqlConnection conn = new SqlConnection("some connection string")
bool result;
try
{
conn.Open();
conn.Close();
result = true;
}
catch
{
MessageBox.Show("Data Connection not found");
result = false;
}
return result;
}
then wrapping using an if statement to switch between the 2 databases ie. if true use remote, else use local
Is there a better way to handle this? I considered using a try-catch system where if the exception thrown is that it can't connect then it saves to the local database instead (assuming that can even be done). Is this even practical given the potential error codes that can be thrown?

Related

Need to restart IIS to get the sql connection

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.

C# - How to detect if SQLite DB is locked?

I'm working on a multithreaded C# program that uses SQLite. I'm having a problem that sometimes running SQLiteCommand.ExecuteNonQuery() to update some rows complains "SQLite error (5): database is locked". I'm aware that this happens because the database gets locked while an insert or update is going on, so if another query to modify the DB comes along the second query will have this database is locked error. So I'm trying to implement workarounds to it, but I'm not sure how I should do it.
I was trying to make it so that if the database locked error is thrown then the program waits a bit and tries again until it works. But somehow no exception gets caught and the code just exits the try-catch block even though the database locked message still gets printed in the debug output. I'm not entirely sure whether the SQL query gets rejected or accepted.
I also tried using TransactionScope and I haven't had the database is locked thing since then, but because of the random nature of the problem I can't be 100% sure if TransactionScope actually solves the problem, or if it only does to an extent, or if it doesn't and I was just lucky so far.
SQLiteConnection connection = new SQLiteConnection("Data Source=DB.db;Version=3;");
connection.Open();
SQLiteCommand command = connection.CreateCommand();
command.commandText = inputQuery;
try
{
command.executeNonQuery();
}
catch (SQLiteException sqle)
{
Debug.WriteLine("Database error: " + e.Message);
return false;
}
catch (Exception e)
{
Debug.WriteLine("Database error: " + e.Message);
return false;
}
finally
{
connection.Close();
}
So I'd really like someone to help me find out 1) how to eliminate the database is locked problem or 2) how to detect if the DB is locked error happens. Thanks in advance.
To detect if Sqlite DB is locked I used approach from Determine whether SQLite database is locked
The idea here is to try to acquire lock and release it immediately, if it is not failing then DB is not locked.
The following C# code worked for me:
public bool IsDatabaseLocked(string dbPath)
{
bool locked = true;
SQLiteConnection connection = new SQLiteConnection($"Data Source={dbPath};Version=3;");
connection.Open();
try
{
SQLiteCommand beginCommand = connection.CreateCommand();
beginCommand.CommandText = "BEGIN EXCLUSIVE"; // tries to acquire the lock
// CommandTimeout is set to 0 to get error immediately if DB is locked
// otherwise it will wait for 30 sec by default
beginCommand.CommandTimeout = 0;
beginCommand.ExecuteNonQuery();
SQLiteCommand commitCommand = connection.CreateCommand();
commitCommand.CommandText = "COMMIT"; // releases the lock immediately
commitCommand.ExecuteNonQuery();
locked = false;
}
catch(SQLiteException)
{
// database is locked error
}
finally
{
connection.Close();
}
return locked;
}
Then when you determined if db is locked you can either wait for it to be unlocked:
public async Task WaitForDbToBeUnlocked(string dbPath, CancellationToken token)
{
while (IsDatabaseLocked(dbPath))
{
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
or send cancellation message to other thread (via CancellationTokenSource for example) before running your query.
If you're receiving error code 5 (busy) you can limit this by using an immediate transaction. If you're able to begin an immediate transaction, SQLite guarantees that you won't receive a busy error until you commit.
Also note that SQLite doesn't have row-level locking. The entire database is locked. Using a WAL journal, you can one writer and multiple readers. With other journaling methods, you can have either one writer, or multiple readers, but not both simultaneously.
SQLite Documentation on 'SQLITE_BUSY'

Handle connection to MySql lost

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();
}

ConnectionState Enumeration - Determining Database Status

I am trying to write a background process that checks to see if a database is broken and I am a little bit confused on what exactly would constitute as "broken".
Looking at the official documentation found here on the Microsoft Developers Network for ConnectionState there is a member entitled "broken". At what point would this member result to true, or how exactly would it be used?
This is currently how I am checking if the DB is broken:
public bool DatabaseConnection()
{
bool statusUp = true;
using (var databaseConnection = new SqlConnection(ConfigData.ConnectionStrings.DatabaseConnectionString))
{
try
{
databaseConnection.Open()
}
catch (SqlException ex)
{
const string message = "Could not establish a connection with Database.";
Log.DatabaseStatusDown(message, ex);
statusUp = false;
}
finally { databaseConnection.Close(); }
}
return statusUp
}
I know Using statement leverages the IDisposable class and the connection will be disposed of but I am extra paranoid. Is this efficient? If not, would a more efficient way to determine if my connection is broken would be to do something like this?
public bool DatabaseConnection()
{
using (var databaseConnection = new SqlConnection(ConfigData.ConnectionStrings.DatabaseConnectionString))
{
return databaseConnection.State == ConnectionState.Broken;
}
}
I will be running this process every two minutes, and something tells me the first method I outlined will not be efficient. Would the second method work to determine if my DB is broken? What exactly does Microsoft define as broken for this particular enum?
I wouldn't use ConnectionState.Broken. It is reserved for future use.
The first technique is actually pretty lightweight. All it does is get a connection from the connection pool, which is held locally. Disposing the connection will return the connection to the pool for use by other processes.
I would perhaps consider actually sending a command to the SQL Server, e.g. "SELECT 'ping'" or something lightweight. If you don't get a resultset back it indicates that your SQL Server couldn't service the request for whatever reason.
Not an expert, but I believe broken would indicate that an already connected database connection had an unrecoverable connection issue (Server closed it, etc). It wouldn't be very reliable, and then possibly only detectable after a failed attempt to do something.
It doesn't make sense to check for Broken on a connection you just made. It's only useful for a long-living connection - and it basically tells you to reopen the connection.
It doesn't tell you anything about the state of the database, or the database server. The only thing it tells you is whether the concrete connection is working or not.
If you're always creating new connections, the only thing you care about is whether connection.Open throws an exception or not. And of course, the ExecuteXXX methods etc. - the connection can drop at any point.

Testing Database Connectivity in C# Console Application

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.

Categories

Resources