How to make this class generic? (.NET C#) - c#

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

Related

Alternative for wraping ADO.net

I'm using the following class to wrap my CRUD operations:
public class DAL
{
private MySqlConnection conn;
private MySqlCommand cmd = new MySqlCommand();
private MySqlDataAdapter adap = new MySqlDataAdapter();
private MySqlDataReader dr;
public MySqlDataReader Dr
{
get { return dr; }
set { dr = value; }
}
public MySqlDataAdapter Adap
{
get { return adap; }
set { adap = value; }
}
public MySqlCommand Cmd
{
get { return cmd; }
set { cmd = value; }
}
public MySqlConnection Conn
{
get { return conn; }
set { conn = value; }
}
public DAL(IConfiguration configuration)
{
this.conn = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string constr)
{
this.conn = new MySqlConnection(constr);
}
}
Some professionals here say that my DAL Class is terrible because of this
To be honest I didn't understand it very well. But no problem.
You say it's not right to wrap Ado.net. So I don't do it anymore.
There is just one problem. What happens if I have to change my db from MySql to MSSQL?
How can I write code the way I can change my DB with changing just one file and not more.
How should I do that?
You should use interfaces.
By example instead of returning "MySqlDbConnection", you could return a "IDbConnection".
In that case the caller will only know it's a database connection. No matter if it's a MySQL or SQL Server connection.
That way the only file that knows about MySql specifically will be your DAL class.
Those interfaces already exist (IDbConnection, IDbCommand, ...) so don't need to create it yourself.
Edit :
In the following example I modified a few things :
Use interfaces (or abstract classes)
Using naming guidelines + readonly keyword
Use full name for naming things (as suggested by #pinkfloydx33 in a comment) it will help readibility. For example the name of the parameter you used for the connection string was "constr" wich, for me, was meaning "constructor" at first (without reading the following line).
What is not fixed:
The usage of the DbConnection (as you mentionned in your post, you should read again the question you linked because if it's a production code it can/will cause so issues).
Possible improvements:
Using an ORM or a library like Dapper (as suggested by #insane_developer in a comment)
public class DAL
{
private readonly IDbConnection _connection;
private DbCommand _command = new MySqlCommand();
private IDbDataAdapter = new MySqlDataAdapter();
private IDataReader _dataReader;
public IDataReader DataReader
{
get { return _dataReader; }
set { _dataReader = value; }
}
public IDbDataAdapter Adapter
{
get { return _adapter; }
set { _adapter = value; }
}
public DbCommand Cmd
{
get { return _command; }
set { _command = value; }
}
public IDbConnection Connection
{
get { return _connection; }
set { _connection = value; }
}
public DAL(IConfiguration configuration)
{
this._connection = new MySqlConnection(configuration.GetConnectionString("DefaultConnection"));
}
public DAL(string connectionString)
{
this._connection = new MySqlConnection(connectionString);
}
}
That way only this class knows about the DBMS type you're using.

Cannot understand why getting "type used in a using statement must be implicitly convertible to 'System.IDisposable'" error

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.
}
}

How to do a raw sql query with DbContext (one connection per request)?

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

How to test DbProviderFactories using nunit

For the below method I want to pass in a mock of the DbProviderFactories class but can't because it's a static class:
private DbConnection GetConnection()
{
var dbProviderFactory = DbProviderFactories.GetFactory(_name);
try
{
var dbConnection = dbProviderFactory.CreateConnection();
if (dbConnection == null) return null;
dbConnection.ConnectionString = _connectionString;
return dbConnection;
}
catch (Exception)
{
return null;
}
}
How can I test my code / how can I mock DbProviderFactories?
You could create your own non-static wrapper for DbProviderFactory that implements your own interface and calls the static method:
public interface IDbProviderFactories
{
DbProviderFactory GetFactory(string name);
}
public class MyDbProviderFactories : IDbProviderFactories
{
public DbProviderFactory GetFactory(string name)
{
return DbProviderFactories.GetFactory(name);
}
}
If you now inject this into your class that exposes GetConnection() you can mock an implementation of the interface as needed.

How to define base class that handles database connection?

I have a console application with a base class as following:
public abstract class PaymentSystemBase : IPayable
{
private SqlConnection _connection;
protected PaymentSystemBase()
{
CreateDatabaseConnection();
}
protected void CreateDatabaseConnection()
{
if(_connection == null)
{
string connectionString = ConfigurationManager.AppSettings["connString"];
var connection = new SqlConnection(connectionString);
_connection = connection;
connection.Open();
}
}
public SqlConnection Connection
{
get { return _connection; }
}
public abstract void ProcessPayment();
}
And have a few classes that derive from PaymentSystemBase:
public class PS1 : PaymentSystemBase
{
public override void ProcessPayment()
{
// Work with database using Connection from PaymentSystemBase
}
}
public class PS2 : PaymentSystemBase
{
public override void ProcessPayment()
{
// Work with database using Connection from PaymentSystemBase
}
}
In main program:
var lstPayments = new List<IPayable>
{
new PS1(),
new PS2()
};
var processPayments = new ProcessPayments(lstPayments);
processPayments.Process();
Where:
public class ProcessPayments
{
private List<IPayable> _paymentSystems;
public ProcessPayments(List<IPayable> paymentSystem)
{
_paymentSystems = paymentSystem;
}
public void Process()
{
foreach (var paymentSystem in _paymentSystems)
{
paymentSystem.ProcessPayment();
}
}
}
My question is how to use the same connection from PaymentSystemBase class and close it after processing? As I can see the connection was created again every time when PS1 and PS2 were created.
You shouldn't try to share the connection object. The connection objects themselves are actually quite lightweight, being an abstraction built on top of the actual physical connections, that the ADO.NET connection pool takes care of creating.
So you base class should be something like:
public abstract class PaymentSystemBase : IPayable
{
private static string _connectionString =
ConfigurationManager.ConnectionStrings["connString"].ConnectionString
public static string ConnectionString
{
get { return _connection; }
}
public abstract void ProcessPayment();
}
And then your derived classes should be:
public class PS1 : PaymentSystemBase
{
public override void ProcessPayment()
{
using(var conn = new SqlConnection(PaymentSystemBase.ConnectionString))
{
using(var cmd = new SqlCommand("...",conn)
{
//Prepare command
conn.Open();
cmd.ExecuteXXX();
//Process results, etc
}
}
}
}
You'll notice that I've also switched where the connection string is loaded from via the ConfigurationManager class from AppSettings to ConnectionStrings, which is a dedicated part of the configuration system for storing connection strings. This wasn't actually required but it is more conventional.

Categories