I tried to close a connection in the destructor of my class, to be sure that if I forget to close it - it close automatically, and it fires an exception.
I searched a little and I founded here that It can't be done.
Now I tried to close it twice - and it works!!!
But I'm wondering if it's a good solution.
what do you think?
here is the code
public class MyCommand : IDisposable
{
public readonly DbCommand command;
public MyCommand(string ConnectionString, DbProviderFactory factory)
{
var tempConnexion = factory.CreateConnection();
tempConnexion.ConnectionString = ConnectionString;
tempConnexion.Open();
var t = tempConnexion.BeginTransaction(IsolationLevel.ReadCommitted);
command = tempConnexion.CreateCommand();
command.Connection = tempConnexion;
command.Transaction = t;
}
public MyCommand(string ConnectionString, DbProviderFactory factory, string requete)
: this(ConnectionString, factory)
{
command.CommandText = requete;
}
public MyCommand(string ConnectionString, string provider)
: this(ConnectionString, DbProviderFactories.GetFactory(provider)) { }
public MyCommand(string ConnectionString, string provider, string requete)
: this(ConnectionString, DbProviderFactories.GetFactory(provider), requete) { }
public static implicit operator DbCommand(myCommand c)
{
return c.command;
}
public void Dispose()
{
try
{
var t = command.Transaction;
if (t != null)
{
t.Commit();
t.Dispose();
}
}
catch { }
try
{
if (command.Connection != null)
command.Connection.Dispose();
command.Dispose();
}
catch { }
}
~MyCommand()
{
if (command != null && command.Connection != null && command.Connection.State == ConnectionState.Open)
for (int i = 0; i < 2; i++)//twice to get the handle - it's working!
Dispose();
}
}
The connection is closed by the Dispose method not by the Destructor.
Also see the MSDN caution
Caution
Do not call Close or Dispose on a Connection, a DataReader, or any
other managed object in the Finalize method of your class. In a
finalizer, you should only release unmanaged resources that your class
owns directly. If your class does not own any unmanaged resources, do
not include a Finalize method in your class definition.
A much better and recommended way to deal with connection is to use USING statement which is equivalent to saying like
try
{
// your code
}
finally
{
myobject.Dispose();
}
Related
I am using Visual Studio 2010, .NET Framework 4.
I am getting type used in a using statement must be implicitly convertible to 'System.IDisposable' error although I am implementing IDisposable so I am not sure where I am making the mistake. Please check and let me know.
I have a class named MyDatabaseContext which contains DbConnection; it implements IDisposable :
public abstract class MyDatabaseContext : IDisposable
{
private string _dataProvider;
private string _connectionString;
private DbConnection _dbConnection;
public MyDatabaseContext(string dataProvider, string connectionString)
{
_dataProvider = dataProvider;
_connectionString = connectionString;
}
public void OpenConnection()
{
DbProviderFactory dbProviderFactory = DbProviderFactories.GetFactory(_dataProvider);
_dbConnection = dbProviderFactory.CreateConnection();
_dbConnection.ConnectionString = _connectionString;
_dbConnection.Open();
}
public void Dispose()
{
if (_dbConnection != null)
{
if (_dbConnection.State != ConnectionState.Closed)
{
_dbConnection.Close();
}
_dbConnection.Dispose();
_dbConnection = null;
}
}
}
Now I have a class EmployeeDatabaseContext which inherit from MyDatabaseContext. In it I have a method test() where I am calling the parent class's OpenConnection() method inside using block.
public class EmployeeDatabaseContext : MyDatabaseContext
{
public EmployeeDatabaseContext(string dataProvider, string connectionString)
: base(dataProvider, connectionString)
{
}
public void test()
{
using (OpenConnection())
{
}
}
}
Problem is I am getting error when I build. Error is in EmployeeDatabaseContext class in test() method on the using block. The error is
'void': type used in a using statement must be implicitly convertible
to 'System.IDisposable'
But the parent class MyDatabaseContext is implementing IDisposable so I dont know why I am getting this error.
Thanks
OpenConnection() returns void. To use it in a using statement, it needs to return an object that implements IDisposable. It doesn't matter what the class owing the method implements. The using statement doesn't go off looking for something nearby to dispose. It disposes exactly what you give it, and you're not giving it anything.
So:
public MyDatabaseContext(string dataProvider, string connectionString)
{
_dataProvider = dataProvider;
_connectionString = connectionString;
OpenConnection();
}
But let OpenConnection() not be public, as you don't want interlopers calling it redundantly:
protected void OpenConnection()
{
// ...
}
You're correctly calling the base constructor in EmployeeDatabaseContext's constructor, so that's fine.
And here's how you use it in a using statement:
public void test()
{
using (var ctxt = new EmployeeDatabaseContext(someProviderString, someConnString))
{
// Do stuff with ctxt
// ctxt.Dispose() will be called when control exits this block.
}
}
I am currently following this instructions on how to do a query with raw sql on ef.
https://learn.microsoft.com/en-us/ef/ef6/querying/raw-sql
Is it possible to do a raw sql read query(ies) on a database context in such a way so we are sure, that we use only one database connection during request processing.
Similar to this, but with DBContext instead od IDBConnection.
public class SqlConnectionFactory : ISqlConnectionFactory, IDisposable
{
private readonly string _connectionString;
private IDbConnection _connection;
public SqlConnectionFactory(string connectionString)
{
this._connectionString = connectionString;
}
public IDbConnection GetOpenConnection()
{
if (this._connection == null || this._connection.State != ConnectionState.Open)
{
this._connection = new SqlConnection(_connectionString);
this._connection.Open();
}
return this._connection;
}
public void Dispose()
{
if (this._connection != null && this._connection.State == ConnectionState.Open)
{
this._connection.Dispose();
}
}
}
I have a unitofWork class and a repository that uses it
something along the lines of this:
unit of work
private SqlConnection _conn;
private TransactionScope _ts;
public UnitOfWork(SqlConnection conn, TransactionScope ts)
{
_ts = ts;
_conn = conn;
_conn.EnlistTransaction(Transaction.Current);
}
public SqlConnection Connection
{
get { return _conn; }
}
public void Save()
{
_ts.Complete();
}
#region IDisposable
public void Dispose()
{
if (_conn != null)
{
_conn.Dispose();
_conn = null;
}
if (_ts != null)
{
_ts.Dispose();
_conn = null;
}
}
repository
SqlConnection _conn;
public Repository(IUnitOfWork uow)
{
_conn = uow.Connection;
}
// some methods
public void Dispose()
{
if (_conn != null)
{
_conn.Dispose();
_conn = null;
}
}
I am using it like this
using (var uow = new UnitOfWork())
{
using (var repo = new Repository(uow))
{
if (repo.UpdateStuff())
{
uow.Save();
}
}
}
the dispose method is first called in the repo, which disposes and nulls the connection. but then when it we get to the uow dispose, the connection is no longer null and is disposed 'again'
I am sure I am being dull, but can anyone explain please
thanks
The right part of the statement
_conn = uow.Connection;
actually returns a copy of the object reference, not the reference itself.
So what you're setting to null in the Repository Dispose method is the local reference to the connection, which is different from the connection reference in the UnitOfWork instance.
If you really want to keep this behavior ( having the UnitOfWork Connection property set to null by the Repository Code), you must set the uow.Connection to null, not the local reference _conn.
Firstly, am new to C# programming.
I have created a dedicated class to get the connection string from the app.config of a Web Services application in Visual Studio 2010 as per the code below.
On building the code I get the following error via the catch block:
"The name 'connection' does not exist in the current context".
Obviously connection is going out of scope.
How do I avoid this error?
Is the Dispose method being used correctly here?
public class FCSConnection : IDisposable
{
public string GetDefaultConnectionString()
{
string DefaultConnectionString = null;
try
{
DefaultConnectionString = ConfigurationManager.AppSettings["ConnectionString"];
SqlConnection connection = new SqlConnection(DefaultConnectionString);
connection.Open();
return DefaultConnectionString;
}
catch (Exception)
{
if (DefaultConnectionString != null)
{
connection.Dispose();
}
}
return DefaultConnectionString;
}
public void Dispose()
{
throw new NotImplementedException();
}
}
The exact compiler message refers to your catch statement:
connection.Dispose();
Here, connection is an unknown name, because it's declared inside the try block.
As for your entire code, I think it's also wrong. If you want your FCSConnection class to encapsulate the SQL connection, you should declare connection as a private member and then dispose it in your Dispose() method.
public class FCSConnection : IDisposable
{
private SqlConnection connection = null;
public string GetDefaultConnectionString()
{
string defaultConnectionString = null;
try
{
defaultConnectionString = ConfigurationManager.AppSettings["ConnectionString"];
connection = new SqlConnection(defaultConnectionString);
connection.Open(); // are you sure want to keep the connection being opened??
}
catch
{
Dispose();
}
return defaultConnectionString;
}
public void Dispose()
{
if (connection != null)
{
connection.Dispose();
connection = null; // to avoid repeat dispose
}
}
}
My class has the following core:
class SmartDbConnection
{
private readonly IDbConnection Connection;
public SmartDbConnection(string ConnectionString)
{
if(ConnectionString.Contains("MultipleActiveResultSets=true"))
{
Connection = new SqlConnection(ConnectionString);
}
}
}
I don't want it to have "SqlConnection" hardcoded. So I thought in making it a Generic class (accepting IDbConnection classes). But I don't know how to do it. Anyone can help?
First - I've added IDisposable to this, as I believe it is important.
Second, note that providers are an alternative here:
class SmartDbConnection
{
private DbConnection Connection;
public SmartDbConnection(string provider, string connectionString)
{
Connection = DbProviderFactories.GetFactory(provider)
.CreateConnection();
Connection.ConnectionString = connectionString;
}
public void Dispose() {
if (Connection != null)
{
Connection.Dispose();
Connection = null;
}
}
}
If you must go generic, how about:
class SmartDbConnection<T> : IDisposable where T : class,
IDbConnection, new()
{
private T Connection;
public SmartDbConnection(string connectionString)
{
T t = new T();
t.ConnectionString = connectionString;
// etc
}
public void Dispose() {
if (Connection != null)
{
Connection.Dispose();
Connection = null;
}
}
}
Why don't you accept IDbConnection instead of connectionstring to your ctor?
Maybe...
class SmartDbConnection<T> where T : IDbConnection, new()
{
private readonly IDbConnection Connection;
public SmartDbConnection(string connectionString)
{
if (connectionString.Contains("MultipleActiveResultSets=true"))
{
Connection = new T();
Connection.ConnectionString = connectionString;
}
}
}
EDIT: But what kaanbardak suggests can be even better...
If you don't want to specify SqlConnection there, where would you specify it - and how would you know to use it only if the connection string contains "MultipleActiveResultSets=true"?
I suspect at some level you want a connection factory - either a Func<string, IDbConnection> you can pass in or set somewhere, or possibly just a class:
public static class ConnectionFactory
{
public static IDbConnection CreateConnection(string connectionString)
{
// Hard-code stuff here
}
}
Of course, they're just two sides of the same coin - ConnectionFactory is just a static implementation of the Func<string, IDbConnection>.
class SmartDbConnection<T> where T: IDbConnection , new()
{
private readonly T Connection;
public SmartDbConnection(string ConnectionString)
{
if (ConnectionString.Contains("MultipleActiveResultSets=true"))
{
Connection = new T();
Connection.ConnectionString = ConnectionString;
}
}
}