Using the TransactionScope object to set up an implicit transaction that doesn't need to be passed across function calls is great! However, if a connection is opened whilst another is already open, the transaction coordinator silently escalates the transaction to be distributed (needing MSDTC service to be running and taking up much more resources and time).
So, this is fine:
using (var ts = new TransactionScope())
{
using (var c = DatabaseManager.GetOpenConnection())
{
// Do Work
}
using (var c = DatabaseManager.GetOpenConnection())
{
// Do more work in same transaction using different connection
}
ts.Complete();
}
But this escalates the transaction:
using (var ts = new TransactionScope())
{
using (var c = DatabaseManager.GetOpenConnection())
{
// Do Work
using (var nestedConnection = DatabaseManager.GetOpenConnection())
{
// Do more work in same transaction using different nested connection - escalated transaction to distributed
}
}
ts.Complete();
}
Is there a recommended practise to avoid escalating transactions in this way, whilst still using nested connections?
The best I can come up with at the moment is having a ThreadStatic connection and reusing that if Transaction.Current is set, like so:
public static class DatabaseManager
{
private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true";
[ThreadStatic]
private static SqlConnection _transactionConnection;
[ThreadStatic] private static int _connectionNesting;
private static SqlConnection GetTransactionConnection()
{
if (_transactionConnection == null)
{
Transaction.Current.TransactionCompleted += ((s, e) =>
{
_connectionNesting = 0;
if (_transactionConnection != null)
{
_transactionConnection.Dispose();
_transactionConnection = null;
}
});
_transactionConnection = new SqlConnection(_connectionString);
_transactionConnection.Disposed += ((s, e) =>
{
if (Transaction.Current != null)
{
_connectionNesting--;
if (_connectionNesting > 0)
{
// Since connection is nested and same as parent, need to keep it open as parent is not expecting it to be closed!
_transactionConnection.ConnectionString = _connectionString;
_transactionConnection.Open();
}
else
{
// Can forget transaction connection and spin up a new one next time one's asked for inside this transaction
_transactionConnection = null;
}
}
});
}
return _transactionConnection;
}
public static SqlConnection GetOpenConnection()
{
SqlConnection connection;
if (Transaction.Current != null)
{
connection = GetTransactionConnection();
_connectionNesting++;
}
else
{
connection = new SqlConnection(_connectionString);
}
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
return connection;
}
}
Edit: So, if the answer is to reuse the same connection when it's nested inside a transactionscope, as the code above does, I wonder about the implications of disposing of this connection mid-transaction.
So far as I can see (using Reflector to examine code), the connection's settings (connection string etc.) are reset and the connection is closed. So (in theory), re-setting the connection string and opening the connection on subsequent calls should "reuse" the connection and prevent escalation (and my initial testing agrees with this).
It does seem a little hacky though... and I'm sure there must be a best-practise somewhere that states that one should not continue to use an object after it's been disposed of!
However, since I cannot subclass the sealed SqlConnection, and want to maintain my transaction-agnostic connection-pool-friendly methods, I struggle (but would be delighted) to see a better way.
Also, realised that I could force non-nested connections by throwing exception if application code attempts to open nested connection (which in most cases is unnecessary, in our codebase)
public static class DatabaseManager
{
private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true; enlist=true;Application Name='jimmy'";
[ThreadStatic]
private static bool _transactionHooked;
[ThreadStatic]
private static bool _openConnection;
public static SqlConnection GetOpenConnection()
{
var connection = new SqlConnection(_connectionString);
if (Transaction.Current != null)
{
if (_openConnection)
{
throw new ApplicationException("Nested connections in transaction not allowed");
}
_openConnection = true;
connection.Disposed += ((s, e) => _openConnection = false);
if (!_transactionHooked)
{
Transaction.Current.TransactionCompleted += ((s, e) =>
{
_openConnection = false;
_transactionHooked = false;
});
_transactionHooked = true;
}
}
connection.Open();
return connection;
}
}
Would still value a less hacky solution :)
One of the primary reasons for transaction escalation is when you have multiple (different) connections involved in a transaction. This almost always escalates to a distributed transaction. And it is indeed a pain.
This is why we make sure that all our transactions use a single connection object. There are several ways to do this. For the most part, we use the thread static object to store a connection object, and our classes that do the database persistance work, use the thread static connection object (which is shared of course). This prevents multiple connections objects from being used and has eliminated transaction escalation. You could also achieve this by simply passing a connection object from method to method, but this isn't as clean, IMO.
Related
I have a c# windows form application that connects to databases dynamically where each user may connect to different databases.
The current implementation is as follows:
Connection Repository that contains a dynamically populated list of connections (per user).
When a user initiates a request that requires a database connection the respective connection is looked up from the connection repository ,opened , and then used in the user request .
Code Sample from the connection repository
public class RepoItem
{
public string databasename;
public SqlConnection sqlcnn;
}
public class ConnectionRepository
{
private List<RepoItem> connectionrepositroylist;
public SqlConnection getConnection(String dbname)
{
SqlConnection cnn = (from n in connectionrepositroylist
where n.databasename == dbname
select n.sqlcnn).Single;
cnn.Open();
return cnn;
}
}
sorry for any code errors i just improvised a small version of the implementation for demonstration purpose.
I'am not closing connections after a command execution because it may be used by another command simultaneously.
The questions are:
Should i be worried about closing the connections ?
Does connection close automatically if it is idle for a specific period ?
I have a method in mind to implement a timer in the created Connection Repository and check for idle connections through the Executing ConnectionState Enumeration and close them manually.
Any suggestions are welcome .
When i want a specific connection i call the getConnection function in the ConnectionRepository class and pass the database name as a parameter.
PS: I didn't post the complete implemented code because it is quite big and includes the preferences that affect the populating of the connection list.
I would suggest not to return the SQLConnection to the calling method at all.
Instead, create a method that will accept an Action<SqlConnection>, create the connection inside a using block, and execute the action inside that block
This way you know that the connection will always be correctly closed and disposed, while giving the using code the freedom to do whatever it needs:
public class RepoItem
{
public string databasename;
public SqlConnection sqlcnn;
}
public class DatabaseConnector
{
private List<RepoItem> connectionrepositroylist;
private SqlConnection GetConnection(String dbname)
{
return (from n in connectionrepositroylist
where n.databasename == dbname
select n.sqlcnn).SingleOrDefault();
}
public void Execute(String dbname, Action<SqlConnection> action)
{
using (var cnn = GetConnection(dbname))
{
if (cnn != null) // in case dbname is not in the list...
{
cnn.Open();
action(cnn);
}
}
}
}
Then, to execute an sql statement you can do something like this:
public void ExecuteReaderExample(string dbName, string sql)
{
Execute("dbName",
connection =>
{
using (var cmd = new SqlCommand(sql, connection))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
// do stuff with data form the database
}
}
}
});
}
Of course, you can also wrap the SqlCommand in a method like this.
I've been working with this approach for quite some time now, and as far as I can tell it's working well. In fact, It's working so well I've published a project on git hub based on this approach.
It saves you a lot of the plumbing when dealing with ado.net, by wrapping the connection, command, reader and adapter much the same way.
Feel free to download it and adapt to your needs.
P.S.
To answer your questions directly:
Should i be worried about closing the connections ?
Yes, you should.
Does connection close automatically if it is idle for a specific period ?
No, it doesn't.
However, implementing a method like I suggested will handle closing and disposing the connection object for you, so you don't need to worry about it.
Update
As Yahfoufi wrote in his comment, this design has a flaw, since multiple commands are using the same instance of SqlConnection, you are risking closing the connection while other commands are running.
However, fixing this design flaw is very easy - instead of holding SqlConnection in RepoItem you can simply hold the connection string:
public class RepoItem
{
public string DatabaseName {get; set;}
public string ConnectionString {get; set;}
}
Then you change the GetConnection method like this:
private SqlConnection GetConnection(String dbname)
{
return new SqlConnection(from n in connectionrepositroylist
where n.databasename == dbname
select n.sqlcnn).SingleOrDefault());
}
Now each Execute method is working on it's own individual instance of SqlConnection so you don't need to worry about closing in the middle of some other command executing.
However, While we are on the subject of refactoring, I would suggest removing the RepoItem class all together and instead of using a List<RepoItem> to hold the connection strings simply use a Dictionary<string, string>, where the database name is the key and the connection string is the value. This way you can only have one connection string per database name, and your GetConnection method is simplified to this:
private Dictionary<string, string> connectionrepositroylist;
private string GetConnectionString(String dbname)
{
return connectionrepositroylist.ContainsKey(dbname) ? connectionrepositroylist[dbname] : "";
}
So, the complete DatabaseConnector class will look like this:
public class DatabaseConnector
{
private Dictionary<string, string> connectionrepositroylist;
private string GetConnectionString(String dbname)
{
return connectionrepositroylist.ContainsKey(dbname) ? connectionrepositroylist[dbname] : "";
}
public void Execute(String dbname, Action<SqlConnection> action)
{
var connectionString = GetConnectionString(dbname);
if(!string.IsNullOrEmpty(connectionString))
{
using (var cnn = new SqlConnection(connectionString))
{
cnn.Open();
action(cnn);
}
}
}
// Of course, You will need a way to populate your dictionary -
// I suggest having a couple of methods like this to add, update and remove items.
public bool AddOrUpdateDataBaseName(string dbname, string connectionString)
{
if(connectionrepositroylist.ContainsKey(dbname))
{
connectionrepositroylist[dbname] = connectionString;
}
else
{
connectionrepositroylist.Add(dbname, connectionString);
}
}
}
The good news is that ADO.Net manages your connection pools dynamically, so there's minimal overhead in you dynamically opening and closing connections in code. There's a good document here if you want to look through the detail.
To answer the specific questions you've raised:
Should i be worried about closing the connections ?
Yes, but not for the reasons you may think. Microsoft encourage you to close your connections, so as to return them to the pool for (re)use elsewhere in your code. Closing the connection doesn't actually close it - it merely returns the underlying connection to the pool. Failure to close your connections properly can lead to delays in them being returned to the pool, thus adversely affecting your applications performance as more connections need to be added to the pool to cope with demand.
Does connection close automatically if it is idle for a specific
period ?
A connection is only returned to the pool when it's Dispose or Finalise methods get called. If you create a connection and drop it into a static container then it will not be returned to the pool at all. As such, your ConnectionRepository may actually be harming performance.
I have a method in mind to implement a timer in the created Connection
Repository and check for idle connections
This is unnecessary - close your connections to allow them to return to the pool. This way they will be available for other threads to use
Personally, I'd suggest that you modify your RepoItem class to store connection strings, rather than connection objects, and let ADO.Net's pooling do all the heavy lifting.
public static class ConnectionRepository
{
private static readonly Dictionary<string, string> Connections = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
public static bool Contains(string key)
{
return Connections.ContainsKey(key);
}
public static void Add(string key, string connectionString)
{
Connections.Add(key, connectionString);
}
public static SqlConnection Get(string key)
{
var con = new SqlConnection(Connections[key]);
con.Open();
return con;
}
}
With this in place, you can query the database as follows:
public static void foo()
{
using (var con = ConnectionRepository.Get("MyConnection"))
using (var cmd = new SqlCommand("SELECT * FROM MyTable", con))
{
var dr = cmd.ExecuteReader();
//...
}
}
Once the query has executed and the connection is no longer required, the using() block calls its Dispose() method and releases the underlying connection back to the pool for re-use.
As #tinudu says, the SqlConnection class reuses existing connections automatically - you don't have to implement that yourself. See SQL Server Connection Pooling.
If you create the SqlConnection object in a using statement, C# will close the connection automatically as required.
Wrapping the whole thing (create connection, open, run query, close connection) in a function is the best idea. You can put the function in a repository base-class, so it is available to all your repositories.
You would need several functions for the different types of SQL query (select, update, stored proc) but you only need to write one of each - they will get reused.
if you worried about so many conditions let say parallel execution as
well so consider reserving connections for app and close all at once
when app is closing.
public class RepoItem
{
public string databasename;
public SqlConnection sqlcnn;
}
public class ConnectionRepository
{
private List<RepoItem> connectionrepositroylist;
public SqlConnection getConnection(String dbname)
{
SqlConnection cnn = (from n in connectionrepositroylist
where n.databasename == dbname
select n.sqlcnn).Single;
if (cnn!= null && cnn.State == cnn.Closed) // Impelement other checks as well
{
cnn.Open();
}
return cnn;
}
}
Implement CloseConnections and call while application closing i.e
Application.ApplicationExit event
public void CloseConnections()
{
foreach (var connection in connectionrepositroylist)
{
try
{
if (connection.State == System.Data.ConnectionState.Open) // check other conditions
{
connection.Close();
}
}
catch (Exception)
{
//logging or special handling
}
}
}
Points to be note
If some query is still executing and user tries to shut down or close the
app can consider following implementations
Wont allow the application shutdown . Callback delegate will help in this
case to ensure that query is returned.
Force stop and close the connection
It is a better Practice to close the sqlconnection manually, Since it can release the connection which can be used for other processes. Also note that you should open the connection as much as late you can and close it early as possible.
Which is the proper way of using BeginTransaction() with IDbConnection in Dapper ?
I have created a method in which i have to use BeginTransaction(). Here is the code.
using (IDbConnection cn = DBConnection)
{
var oTransaction = cn.BeginTransaction();
try
{
// SAVE BASIC CONSULT DETAIL
var oPara = new DynamicParameters();
oPara.Add("#PatientID", iPatientID, dbType: DbType.Int32);
..........blah......blah............
}
catch (Exception ex)
{
oTransaction.Rollback();
return new SaveResponse { Success = false, ResponseString = ex.Message };
}
}
When i executed above method - i got an exception -
Invalid operation. The connection is closed.
This is because you can't begin a transaction before the connection is opened. So when i add this line: cn.Open();, the error gets resolved. But i have read somewhere that manually opening the connection is bad practice!! Dapper opens a connection only when it needs to.
In Entity framework you can handle a transaction using a TransactionScope.
So my question is what is a good practice to handle transaction without adding the line cn.Open()... in Dapper ? I guess there should be some proper way for this.
Manually opening a connection is not "bad practice"; dapper works with open or closed connections as a convenience, nothing more. A common gotcha is people having connections that are left open, unused, for too long without ever releasing them to the pool - however, this isn't a problem in most cases, and you can certainly do:
using(var cn = CreateConnection()) {
cn.Open();
using(var tran = cn.BeginTransaction()) {
try {
// multiple operations involving cn and tran here
tran.Commit();
} catch {
tran.Rollback();
throw;
}
}
}
Note that dapper has an optional parameter to pass in the transaction, for example:
cn.Execute(sql, args, transaction: tran);
I am actually tempted to make extension methods on IDbTransaction that work similarly, since a transaction always exposes .Connection; this would allow:
tran.Execute(sql, args);
But this does not exist today.
TransactionScope is another option, but has different semantics: this could involve the LTM or DTC, depending on ... well, luck, mainly. It is also tempting to create a wrapper around IDbTransaction that doesn't need the try/catch - more like how TransactionScope works; something like (this also does not exist):
using(var cn = CreateConnection())
using(var tran = cn.SimpleTransaction())
{
tran.Execute(...);
tran.Execute(...);
tran.Complete();
}
You should not call
cn.Close();
because the using block will try to close too.
For the transaction part, yes you can use TransactionScope as well, since it is not an Entity Framework related technique.
Have a look at this SO answer: https://stackoverflow.com/a/6874617/566608
It explain how to enlist your connection in the transaction scope.
The important aspect is: connection are automatically enlisted in the transaction IIF you open the connection inside the scope.
Take a look at Tim Schreiber solution which is simple yet powerful and implemented using repository pattern and has Dapper Transactions in mind.
The Commit() in the code below shows it.
public class UnitOfWork : IUnitOfWork
{
private IDbConnection _connection;
private IDbTransaction _transaction;
private IBreedRepository _breedRepository;
private ICatRepository _catRepository;
private bool _disposed;
public UnitOfWork(string connectionString)
{
_connection = new SqlConnection(connectionString);
_connection.Open();
_transaction = _connection.BeginTransaction();
}
public IBreedRepository BreedRepository
{
get { return _breedRepository ?? (_breedRepository = new BreedRepository(_transaction)); }
}
public ICatRepository CatRepository
{
get { return _catRepository ?? (_catRepository = new CatRepository(_transaction)); }
}
public void Commit()
{
try
{
_transaction.Commit();
}
catch
{
_transaction.Rollback();
throw;
}
finally
{
_transaction.Dispose();
_transaction = _connection.BeginTransaction();
resetRepositories();
}
}
private void resetRepositories()
{
_breedRepository = null;
_catRepository = null;
}
public void Dispose()
{
dispose(true);
GC.SuppressFinalize(this);
}
private void dispose(bool disposing)
{
if (!_disposed)
{
if(disposing)
{
if (_transaction != null)
{
_transaction.Dispose();
_transaction = null;
}
if(_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
_disposed = true;
}
}
~UnitOfWork()
{
dispose(false);
}
}
There are two intended ways to use transactions with Dapper.
Pass your IDbTranasction to your normal Dapper call.
Before:
var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"});
After:
var affectedRows = connection.Execute(sql, new {CustomerName = "Mark"}, transaction=tx);
Use the new .Execute extension method that Dapper adds to IDbTransaction itself:
tx.Execute(sql, new {CustomerName = "Mark"});
Note: the variable tx comes from IDbTransaction tx = connection.BeginTransaction();
This is how you're supposed to use transactions with Dapper; neither of them are TranasctionScope.
Bonus Reading
https://stackoverflow.com/a/67474832/12597
Here is a sample of the class I currently use for database interaction:
using System;
using System.Data;
using System.Collections.Generic;
// Libraries
using log4net;
using log4net.Config;
using MySql.Data.MySqlClient;
namespace AIC
{
class DB
{
private static readonly ILog _logger = LogManager.GetLogger(typeof(DB));
private MySqlConnection _connection;
private MySqlCommand _cmd;
private string _server;
private string _database;
private string _username;
private string _password;
//Constructor
public DB(string server, string database, string username, string password)
{
log4net.Config.XmlConfigurator.Configure();
_server = server;
_database = database;
_username = username;
_password = password;
_connection = new MySqlConnection(string.Format("SERVER={0};DATABASE={1};UID={2};PASSWORD={3};charset=utf8;", _server, _database, _username, _password));
}
public bool TestConnection()
{
try
{
_connection.Open();
_connection.Close();
_logger.Info("Connection test, passed...");
return true;
}
catch (MySqlException ex)
{
_logger.Error(ex.ToString());
return false;
}
}
//open connection to database
private bool Open()
{
try
{
if (_connection.State != ConnectionState.Open)
_connection.Open();
_logger.Info("Starting connection to database...");
return true;
}
catch (MySqlException ex)
{
_logger.Error(ex.ToString());
return false;
}
}
//Close connection
private bool Close()
{
try
{
if (_connection.State != ConnectionState.Closed)
_connection.Close();
_logger.Info("Closing connection to database...");
return true;
}
catch (MySqlException ex)
{
_logger.Error(ex.ToString());
return false;
}
}
// Some basic functions
public bool UserExist(string user)
{
string query = "SELECT user_id FROM users WHERE username=#name LIMIT 1";
if (this.Open())
{
try
{
// Assign the connection
_cmd = new MySqlCommand(query, _connection);
// Prepare to receive params
_cmd.Prepare();
// Fill up the params
_cmd.Parameters.AddWithValue("#name", user);
// returned count bool
bool result = Convert.ToInt32(_cmd.ExecuteScalar()) > 0;
// Close connection
this.Close();
return result;
}
catch (MySqlException ex)
{
_logger.Error(ex.ToString());
this.Close();
return false;
}
}
else
{
_logger.Error("You must be connected to the database before performing this action");
return false;
}
}
public bool AddUser(string user)
{
// .... add user to database
}
public bool DelUser(string user)
{
// .... del user from database
}
public int CountUsers()
{
// .... count total users from database
}
}
}
Currently, I don't have any management for opening and closing the connections so it will always check wether the database is connected or not, perform the action and close it as shown in the UserExist function.
Considering this, it came to my attention that I might be closing my own connections in the middle or their transactions since I am using this in 2 different threads.
My doubt here is wether this simple class could lock my application for any reason making it unresponsive or cause me any troubles in the long run?
What should I consider, improve, etc.?
Would appreciate code samples.
Each thread should have its own connection instance, in your case probably an instance of Db.
But the problem would be solved (a lot) better by not storing a connection in your Db objects at all. The best pattern is to only use connections as local variables in a using() {} statement.
Currently, your class should implement IDisposable (just for the case where your try/catch logic fails).
Waiting for exceptions to be thrown and then handle them is not a good way for design a multithreading class. a good design will be with using lock statement. when using a lock you are providing a critical regions so only one thread is allowed to access to the resources at time. once one thread finish its usage the other can proceed and so on.
For example:
so it will always check wether the database is connected or not, perform the action and close
what will happen if two threads try to enter to the same method concurrently? one thread checking if the connection is not set to continue and it finds that the connection is not set so it proceed. but at the middle of its process and before it connects, the Thread Context Switching switches to the other thread and pause the first one, the second thread in turn ask if the connection was set and it will find that it is not, so it connect and proceed. Now the thread context switching switches to the first thread to continue its execution. and the problems begins...
But the scenario is different when using 'lock'; One and only one thread will allowed to access to the method region that marked with the lock. So one thread enter the lock region and establish the connection. at that time the other thread try to access to the method but the first one is still there so the second will be waiting until the first one finishes its work and then it will proceed.
You don't have to lock them, but yes: you must ensure 2 threads aren't using the same connection at the same time.
Synchronisation (locks, etc) is one way to do that; isolation is another (better, IMO) way. If two threads never have the same connection then all is good. For this reason, a static connection is never a good idea.
To test if i can connect to my database, I execute the following code :
using (SqlConnection connection = new SqlConnection(myConnectionString))
{
try
{
connection.Open();
canConnect = true;
}
catch (SqlException) { }
}
This works except it throws an exception if the connection failed. Is there any other way to test a Sql connection that doesn't throw an exception ?
Edit :
To add precision, i'm asking if there is a simple method that does that without having to open the connection and catch exceptions that can occur
When attempting to open a connection, there is no way to avoid the exception if the connection can't be opened. It can be hidden away in a function somewhere, but you're going to get the exception, no matter what.
It was designed like this because generally you expect to be able to connect to the database. A failed connection is the exception.
That being said, you can test the current connection state at any time by checking the State property.
write an extension like so:
public static class Extension{
public static bool CanOpen(this SqlConnection connection){
try{
if(connection == null){ return false; }
connection.Open();
var canOpen = connection.State == ConnectionState.Open;
connection.close();
return canOpen;
}
catch{
return false;
}
}
Then you can consume it like:
using(var connection = new SqlConnection(myConnectionString)){
if(connection.CanOpen()){
// NOTE: The connection is not open at this point...
// You can either open it here or not close it in the extension method...
// I prefer opening the connection explicitly here...
}
}
HTH.
If it throws an an exception and you handle it in your catch block you already know the connection failed. I think you answered your own question.
I think the real answer here is ping.
string data = "ismyserverpingable";
byte[] buffer = Encoding.ASCII.GetBytes (data);
int timeout = 120;
PingReply reply = pingSender.Send ("google.com", timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
}
Unless you are explicitly checking to see if a sql connection is possible 9/10 you should know if something is a sql server. This would save you that nasty memory usage of an exception which is what i am betting you are really after.
You can not avoid exception coming while connecting database but have some function which handle this very well. I am using this function which return true if connection exist.
public static bool IsSQLConnectionAvailable()
{
SqlConnection _objConn = new SqlConnection();
try
{
_objConn.ConnectionString = ConfigurationManager.ConnectionStrings["DefaultSQLConnectionString"].ConnectionString;
_objConn.Open();
}
catch
{
return false;
}
finally
{
if (_objConn.State == ConnectionState.Open)
_objConn.Close();
}
return true;
}
You could always use the ConnectionStringBuilder class and check for the existence of each piece that is required by a connection string before attempting to open it.
If the connection string is correct, but the database server you're connecting to is down, you're still going to get an excepton. Kind of pointless to check the quality of the string if the endpoint you're connecting to can potentially be offline.
I'd like to share the whole solution I've implemented to avoid checking the connection at every call. If the connection string is wrong an exception stops the execution, otherwise the attempt to open the connectionstring is done just once for each connectionstring.
Since the connection is often multithreaded I've added a syncobj used by the lock which checks
#if DEBUG
private static object syncobj = new object();
private static ConcurrentDictionary<string, bool> CheckedConnection = new ConcurrentDictionary<string, bool>();
private static void CheckCanOpenConnection(SqlConnection connection)
{
lock (syncobj)
{
try
{
CheckedConnection.TryGetValue(connection.ConnectionString, out bool found);
if (found)
{
return;
}
else
{
connection.Open();
var canOpen = connection.State == ConnectionState.Open;
connection.Close();
CheckedConnection.TryAdd(connection.ConnectionString, true);
return;
}
}
catch
{
throw new ApplicationException("Unable to connect to: " + connection.ConnectionString);
}
}
}
#endif
Here is the call to from the method which instantiate the connection
private SqlConnection CreateConnection()
{
if (_connection == null)
{
_connection = new SqlConnection(this.ConnectionString);
#if DEBUG
CheckCanOpenConnection(_connection);
#endif
}
return _connection;
}
I have a web application that issues requests to 3 databases in the DAL. I'm writing some integration tests to make sure that the overall functionality round trip actually does what i expect it to do. This is completely separate from my unit tests, just fyi.
The way I was intending to write these tests were something to the effect of this
[Test]
public void WorkflowExampleTest()
{
(using var transaction = new TransactionScope())
{
Presenter.ProcessWorkflow();
}
}
The Presenter in this case has already been set up. The problem comes into play inside the ProcessWorkflow method because it calls various Repositories which in turn access different databases, and my sql server box does not have MSDTC enabled, so I get an error whenever I try to either create a new sql connection, or try to change a cached connection's database to target a different one.
For brevity the Presenter resembles something like:
public void ProcessWorkflow()
{
LogRepository.LogSomethingInLogDatabase();
var l_results = ProcessRepository.DoSomeWorkOnProcessDatabase();
ResultsRepository.IssueResultstoResultsDatabase(l_results);
}
I've attempted numerous things to solve this problem.
Caching one active connection at all times and changing the target database
Caching one active connection for each target database (this was kind of useless because pooling should do this for me, but I wanted to see if I got different results)
Adding additional TransactionScopes inside each repository so that they have their own transactions using the TransactionScopeOption "RequiresNew"
My 3rd attempt on the list looks something like this:
public void LogSomethingInLogDatabase()
{
using (var transaction =
new TransactionScope(TransactionScopeOption.RequiresNew))
{
//do some database work
transaction.Complete();
}
}
And actually the 3rd thing I tried actually got the unit tests to work, but all the transactions that completed actually HIT my database! So that was an utter failure, since the entire point is to NOT effect my database.
My question therefore is, what other options are out there to accomplish what I'm trying to do given the constraints I've laid out?
EDIT:
This is what "//do some database work" would look like
using (var l_context = new DataContext(TargetDatabaseEnum.SomeDatabase))
{
//use a SqlCommand here
//use a SqlDataAdapter inside the SqlCommand
//etc.
}
and the DataContext itself looks something like this
public class DataContext : IDisposable
{
static int References { get; set; }
static SqlConnection Connection { get; set; }
TargetDatabaseEnum OriginalDatabase { get; set; }
public DataContext(TargetDatabaseEnum database)
{
if (Connection == null)
Connection = new SqlConnection();
if (Connection.Database != DatabaseInfo.GetDatabaseName(database))
{
OriginalDatabase =
DatabaseInfo.GetDatabaseEnum(Connection.Database);
Connection.ChangeDatabase(
DatabaseInfo.GetDatabaseName(database));
}
if (Connection.State == ConnectionState.Closed)
{
Connection.Open() //<- ERROR HAPPENS HERE
}
ConnectionReferences++;
}
public void Dispose()
{
if (Connection.State == ConnectionState.Open)
{
Connection.ChangeDatabase(
DatabaseInfo.GetDatabaseName(OriginalDatabase));
}
if (Connection != null && --ConnectionReferences <= 0)
{
if (Connection.State == ConnectionState.Open)
Connection.Close();
Connection.Dispose();
}
}
}
Set Enlist=false on connection string to avoid auto enlistment on transaction.
Manually enlist connection as participants in transaction scope. (http://msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx)
Ok, I found a way around this issue. The only reason I'm doing it this way is because I couldn't find ANY other way to fix this problem, and because it's in my integration tests, so I'm not concerned about this having adverse effects in production code.
I had to add a property to my DataContext to act as a flag to keep track of whether or not to dispose of the connection object when my DataContext is being disposed. This way, the connection is kept alive throughout the entire transaction scope, and therefore no longer bothers DTC
Here's sample of my new Dispose:
internal static bool SupressConnectionDispose { get; set; }
public void Dispose()
{
if (Connection.State == ConnectionState.Open)
{
Connection.ChangeDatabase(
DatabaseInfo.GetDatabaseName(OriginalDatabase));
}
if (Connection != null
&& --ConnectionReferences <= 0
&& !SuppressConnectionDispose)
{
if (Connection.State == ConnectionState.Open)
Connection.Close();
Connection.Dispose();
}
}
this allows my integration tests to take the form of:
[Test]
public void WorkflowExampleTest()
{
(using var transaction = new TransactionScope())
{
DataContext.SuppressConnectionDispose = true;
Presenter.ProcessWorkflow();
}
}
I would not recommend utilizing this in production code, but for integration tests I think it is appropriate. Also keep in mind this only works for connections where the server is always the same, as well as the user.
I hope this helps anyone else who runs into the same problem I had.
If you don't want to use MSDTC you can use SQL transactions directly.
See SqlConnection.BeginTransaction().