I have two classes SqlHelper and DishesTypes there are used in a DAL project
public class SqlHelper
{
public static SqlDataReader ExecuteReader(string procedure,
params SqlParameter[] commandParameters)
{
using (var connection = new SqlConnection(
ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
using (var command = new SqlCommand(procedure, _connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(commandParameters);
return command.ExecuteReader();
}
}
public class DishesTypes
{
public static SqlDataReader DishesTypesSelectAll()
{
return SqlHelper.ExecuteReader("DishesTypesSelectAllRows"); //name of procedure
}
}
And I have class DishedTypes that used in a BLL project like this
public class DishesTypes
{
public int DishTypeId { get; set; }
public string DishType { get; set; }
public static List<DishesTypes> DishesTypesSelectAll()
{
IDataReader dr = DataAccessLayer.DishesTypes.DishesTypesSelectAll();
List<DishesTypes> dishesTypesList = new List<DishesTypes>();
while (dr.Read())
{
DishesTypes myDishesTypes = new DishesTypes
{
DishTypeId = (int)dr["DishTypeId"],
DishType = (string)dr["DishType"]
};
dishesTypesList.Add(myDishesTypes);
}
return dishesTypesList;
}
}
Problems starts here while (dr.Read()),The reason, the connection to this point has already closed and it is necessary to reconnect how best to change the implementation of classes adhering layers DAL and BLL, to work?
If you want to roll your own, something like this is better:
public class DataQuery
{
private readonly string _connectionString;
public DataQuery(string connectionString)
{
_connectionString = connectionString;
}
public IEnumerable<T> GetList<T>(string procedure,
Func<IDataRecord, T> entityCreator,
params SqlParameter[] commandParameters
)
{
var result = new List<T>();
using (var connection = CreateConnection())
using (var command = CreateCommand(procedure, connection, commandParameters))
using (var reader = command.ExecuteReader())
{
result.Add(entityCreator(reader));
}
return result;
}
private SqlConnection CreateConnection()
{
var connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
private static DbCommand CreateCommand(string procedure,
SqlConnection connection, SqlParameter[] commandParameters)
{
var command = new SqlCommand(procedure, connection)
{
CommandType = CommandType.StoredProcedure
};
command.Parameters.AddRange(commandParameters);
return command;
}
}
Which you would call like this:
var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"]
.ConnectionString;
var query = new DataQuery(connectionString);
Func<IDataRecord, DishesTypes> creator = dr =>
new DishesTypes
{
DishTypeId = (int)dr["DishTypeId"],
DishType = (string)dr["DishType"]
};
var results = query.GetList("DishesTypesSelectAllRows", creator);
Otherwise, which I recommend, have a look at Dapper.
Dapper would allow you to simply do:
var results = connection.Query<DishesTypes>("DishesTypesSelectAllRows",
commandType: CommandType.StoredProcedure);
First of all, your using statement is closing your connection, so you cannot expect to return a useable IDataReader. Second, your connection is never opened, so you would not get a result, anyway. Having said that, if your dataset will always be small enough to fit in memory, you could use something like what I have done below. This should have minimal impact on your code.
public class SqlHelper
{
public static IDataReader ExecuteReader(string procedure, params SqlParameter[] commandParameters)
{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (var command = new SqlCommand(procedure, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(commandParameters);
DataTable dt = new DataTable();
using (SqlDataAdapter da = new SqlDataAdapter(command))
da.Fill(dt);
return dt.CreateDataReader();
}
}
}
}
public class DishesTypes
{
public static IDataReader DishesTypesSelectAll()
{
return SqlHelper.ExecuteReader("DishesTypesSelectAllRows");//name of procedure
}
}
Related
I am maintaining a previous developer work.
Here is the db layer class, it has .......
public class Database
{
private string mConnString;
private SqlConnection mConn;
private SqlDataAdapter mAdapter;
private SqlCommand mCmd;
private SqlTransaction mTransaction;
private bool disposed = false;
public Database() : this(Web.GetWebConfigValue("ConnectionString"))
{
}
public Database(string connString)
{
mConnString = connString;
mConn = new SqlConnection(mConnString);
mConn.Open();
mAdapter = new SqlDataAdapter();
mCmd = new SqlCommand();
mCmd.CommandType = CommandType.StoredProcedure;
mCmd.Connection = mConn;
}
public void CloseConnection()
{
mConn.Close();
}
public void BeginTransaction()
{
mTransaction = mConn.BeginTransaction();
mCmd.Transaction = mTransaction;
}
public void CommitTransaction()
{
mTransaction.Commit();
}
public void RollbackTransaction()
{
mTransaction.Rollback();
}
public void AddParam(string name, SqlDbType type, object value)
{
SqlParameter parameter = new SqlParameter('#' + name, type);
parameter.Value = value;
mCmd.Parameters.Add(parameter);
}
public void ChangeParam(string name, object value)
{
mCmd.Parameters['#' + name].Value = value;
}
public void DeleteParam(string name)
{
mCmd.Parameters.RemoveAt('#' + name);
}
public void AddReturnParam()
{
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "return";
parameter.Direction = ParameterDirection.ReturnValue;
mCmd.Parameters.Add(parameter);
}
public void AddOutputParam(string name, SqlDbType type, int size)
{
SqlParameter parameter = new SqlParameter('#' + name, type);
parameter.Direction = ParameterDirection.Output;
parameter.Size = size;
mCmd.Parameters.Add(parameter);
}
public int GetReturnParam()
{
return (int)mCmd.Parameters["return"].Value;
}
public object GetOutputParam(string name)
{
return mCmd.Parameters['#' + name].Value;
}
public void ClearParams()
{
mCmd.Parameters.Clear();
}
public void ExecNonQuery(string cmdText)
{
if(mConn.State==ConnectionState.Closed)
mConn.Open();
mCmd.CommandText = cmdText;
mCmd.ExecuteNonQuery();
}
public DataSet GetDataSet(string cmdText)
{
mCmd.CommandText = cmdText;
mAdapter.SelectCommand = mCmd;
DataSet ds = new DataSet();
mAdapter.Fill(ds);
return ds;
}
public IDataReader GetDataReader(string cmdText)
{
mCmd.CommandText = cmdText;
if(mConn.State==ConnectionState.Closed)
mConn.Open();
return mCmd.ExecuteReader(CommandBehavior.CloseConnection);
}
public DataTable GetDataTable(string cmdText)
{
return GetDataSet(cmdText).Tables[0];
}
public DataTable GetDataTable(string cmdText,string SQL)
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = cmdText;
mAdapter.SelectCommand = cmd;
cmd.Connection = mConn;
DataSet ds = new DataSet();
mAdapter.Fill(ds);
return ds.Tables[0];
}
public DataRow GetDataRow(string cmdText)
{
DataTable dt = GetDataTable(cmdText);
DataRow dr;
if(dt.Rows.Count > 0)
dr = dt.Rows[0];
else
dr = null;
return dr;
}
public object GetScalar(string cmdText)
{
mCmd.CommandText = cmdText;
return mCmd.ExecuteScalar();
}
public void SetCommandType(CommandType type)
{
mCmd.CommandType = type;
}
~Database()
{
this.Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
if(mConn.State == ConnectionState.Open)
mConn.Close();
this.mCmd.Dispose();
this.mAdapter.Dispose();
this.mTransaction.Dispose();
}
}
disposed = true;
}
}
Can you help me in figuring out where connection might not be closed in all the cases where this class is being used.
The connection only ever gets closed when disposing the instance of the DataBase class.
However, while this class is implementing the disposable pattern, it doesn't implement the IDisposable interface - so you can't use it in a using statement.
Moreover, you have to rely on whoever is using this class to dispose it.
If they don't, the connection will not get closed until the Finalizer gets called, and that is completely out of the developer's control. It might even not get called at all - as the garbage collector might not need to clear memory during the runtime of whatever application is using this code.
This is why the proper way of handling connections to the database is as a local variable inside a using statement.
What you want to do is create the connection and open it as late as possible, and dispose it as soon as possible.
A proper method for handling calls to the database looks like this:
int ExecuteNonQuery(string sql)
{
using(var con = new SqlConnection(connectionString))
{
using(var cmd = new SqlCommand(sql, con))
{
con.Open();
return cmd.ExecueNonQuery();
}
}
}
Of course, you would want to add arguments to hold whatever parameters you'll need to pass to the database, and an argument to hold command type, but that should be built on the basis of this structure.
I have a project on GitHub called ADONETHelper (that I've been neglecting for the past year or so due to lack of spare time) that was written in order to reduce the code repetition when using ADO.Net directly.
I've written it a few years back so of course now I have improvements in mind but as I said, I don't have the spare time to work on it - but the general idea is still valid and useful.
Basically, it has a single Execute method that looks like this:
public T Execute<T>(string sql, CommandType commandType, Func<IDbCommand, T> function, params IDbDataParameter[] parameters)
{
using (var con = new TConnection())
{
con.ConnectionString = _ConnectionString;
using (var cmd = new TCommand())
{
cmd.CommandText = sql;
cmd.Connection = con;
cmd.CommandType = commandType;
if (parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
con.Open();
return function(cmd);
}
}
}
Than I've added a few methods that use this method:
public int ExecuteNonQuery(string sql, CommandType commandType, params IDbDataParameter[] parameters)
{
return Execute<int>(sql, commandType, c => c.ExecuteNonQuery(), parameters);
}
public bool ExecuteReader(string sql, CommandType commandType, Func<IDataReader, bool> populate, params IDbDataParameter[] parameters)
{
return Execute<bool>(sql, commandType, c => populate(c.ExecuteReader()), parameters);
}
and so on.
Feel free to borrow ideas from that project - or even use it as is - I have a few applications using this and they run very well for quite some time now.
You aren't implementing the disposable pattern via the IDisposable interface, you just have a Dispose method, in turn you would not be able to call this in a using statement.
public class Database : IDisposable { ... }
This is all a bit suspect: I mean, if you are already using it you aren't using this in a using statement and trying to cache the connection seemingly. I would shy away from this altogether.
Also you have a Destructor, however its usage is wrong 99% of times.
While a persistence layer makes perfectly sense, you have to design it differently. What you do is pack some complexity into methods which still do the same, such as ChangeParam() or GetDataReader().
Normally, you have repositories which have knowledge about the underlying technicalities and remove that kind of complexity, such as GetAllCustomers() (with emphasis on the domain term customer).
When you have say 4 or 5 such repositories, then you start refactoring by abstracting complexity into a parent class. By doing so, you're packing the complexity such as in GetDataReader() and promote it from the repositories into an abstract repository that sits on top of it.
By starting from where you started you'll get just another layer which does not abstract nearly as much and has too much, often unnecessary, functionality.
If it were that easy, the ADO.NET API would already have it removed in the first place.
Another thing you should do is look at this simple but ever-recurring code snippet. It distillates some core concepts about IDisposable and using(){}. You'll always encounter it in correct code:
string sql = "SELECT * FROM t";
using (SqlConnection con = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(sql, con))
{
SqlDataReader reader;
con.Open();
reader = cmd.ExecuteReader();
while (reader.Read())
{
// TODO: consume data
}
reader.Close();
}
This is the stuff I'd expect to see in a persistence layer, and the consume data part is actually the most important, because it is domain-dependent. The rest is just boilerplate code with little interest.
Before you dowonvote this, please have a look first. I know there numerous threads here with this errormessage, but I haven't found one with this particular problem. I've created a custom command class implementing the IDbCommand interface like this:
internal class Prozedur : IDbCommand
{
private static SqlCommand command = new SqlCommand() ;
private SqlConnection connection;
string commandtext;
CommandType commandType;
public Prozedur(string abfrage)
{
commandtext = abfrage;
commandType = CommandType.StoredProcedure;
connection = (SqlConnection)new Verbindung();
SqlCommand command = new SqlCommand(commandtext, connection);
}
public static explicit operator SqlCommand(Prozedur v)
{
return command;
}
...
I'am using the object like this:
internal class Tabelle : DataTable
{
DataTable tabelle = new DataTable();
internal Tabelle(string abfrage)
{
Prozedur p = new Prozedur(abfrage);
SqlDataAdapter adapter = new SqlDataAdapter((SqlCommand)p);
adapter.Fill(tabelle);
}
}
However, on the adapter.Fill(tabelle) i get the Error “Fill: SelectCommand.Connection property has not been initialized.”. But if i look at the object p, the connection is there:
Update
If i change the code like this
internal class Tabelle : DataTable
{
DataTable tabelle = new DataTable();
internal Tabelle(string abfrage)
{
Prozedur p = new Prozedur(abfrage);
SqlCommand c = (SqlCommand)p;
//added this line
c.Connection = p.Connection;
SqlDataAdapter adapter = new SqlDataAdapter((SqlCommand)p);
adapter.Fill(tabelle);
}
}
I get a compiler error for p.Connection; C# Cannot implicitly convert type to. An explicit conversion exists (are you missing a cast?).
The code for the connection is this:
public IDbConnection Connection
{
get
{
return connection;
}
set
{
connection = (SqlConnection)value;
}
}
The problem was in the convert method. When I change the code as below it works. Basically I had to make all variables static and return a new SqlCommandObject in the convert method.
internal class Prozedur : IDbCommand
{
private static SqlCommand command = new SqlCommand();
private static SqlConnection connection;
static string commandtext;
CommandType commandType;
public Prozedur(string abfrage)
{
commandtext = abfrage;
commandType = CommandType.StoredProcedure;
connection = (SqlConnection)new Verbindung();
}
public static explicit operator SqlCommand(Prozedur v)
{
return new SqlCommand() { CommandText = commandtext, Connection = connection, CommandType = command.CommandType };
}
...
And it works like this without static variables:
internal class Prozedur : IDbCommand
{
private SqlCommand command = new SqlCommand();
private SqlConnection connection;
string commandtext;
CommandType commandType;
public Prozedur(string abfrage)
{
commandtext = abfrage;
commandType = CommandType.StoredProcedure;
connection = (SqlConnection)new Verbindung();
}
public static explicit operator SqlCommand(Prozedur v)
{
return new SqlCommand() { CommandText = v.CommandText, Connection = (SqlConnection)v.Connection, CommandType = v.CommandType };
}
...
This is my current pattern
private void ReadData(string connString, string cmdString)
{
using (OracleConnection conn = new OracleConnection(connString))
{
conn.Open();
OracleCommand cmd = new OracleCommand(cmdString, conn);
OracleDataReader reader = cmd.ExecuteReader();
//some long operation using reader
}
}
In the above case, the connection remains open while the long operation is going on. Is there a way I could close the connection but still preserve the reader. Is that going to be advantageous?
If by long operations you mean to say that you need to do extra operations on the database (like update/insert/delete), then you cannot close the connection.
If you want to read the data and do some calculation based on it, then you should modify you pattern to: 1. read all the data, 2. close the connection, 3. do long operation on the data.
using System;
using System.Collections.Generic;
using Oracle.DataAccess.Client;
namespace Utils
{
class Test
{
private class Class
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
private void ReadData(string connString, string cmdString)
{
List<Class> data = new List<Class>();
using (OracleConnection conn = new OracleConnection() { ConnectionString = connString })
using (OracleCommand objCmd = new OracleCommand()
{
Connection = conn,
CommandText = cmdString
})
{
try
{
conn.Open();
}
catch (OracleException)
{
OracleConnection.ClearPool(conn);
conn.Open();
}
using (OracleDataReader dataReader = objCmd.ExecuteReader())
{
while (dataReader.Read())
data.Add(new Class()
{
FirstProperty = dataReader.GetString(0),
SecondProperty = dataReader.GetString(1)
});
}
conn.Close();
}
//some long operation using data
}
}
}
Is the following defensive programming?
What I mean is that if it loses the connection, or some problem occurs during run-time and then the user runsit again will the .NET framework have tidied up any open connections and objects that were created when it first ran?
I've heard mention of a "Singleton pattern" - is this something I should use in the static method CreateConnection?
class Program {
static void Main(string[] args) {
DataTable CasTable = fillSampleDataTable("SELECT top 100 * FROM x");
//do other stuff
}
static SqlConnection CreateConnection() {
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["XXX"].ConnectionString);
return conn;
}
static SqlDataAdapter CreateAdapter(string myCommand) {
SqlDataAdapter myAdapt = new SqlDataAdapter(myCommand, CreateConnection());
return myAdapt;
}
static DataTable fillSampleDataTable(string myCommand) {
using (var adapt = CreateAdapter(myCommand)) {
DataSet mySet = new DataSet();
adapt.Fill(mySet, "SampleData");
return mySet.Tables["SampleData"];
}
}
}
I would recommend you using the ADO.NET connection pool, a.k.a disposing the connections as soon as you have finished using them => wrap all IDisposable resources in using statements:
class Program
{
static void Main(string[] args)
{
DataTable CasTable = fillSampleDataTable("SELECT top 100 * FROM x");
//do other stuff
}
static DataTable fillSampleDataTable(string myCommand)
{
var connectionString = ConfigurationManager.ConnectionStrings["XXX"].ConnectionString;
using (var conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
using (var adapt = new SqlDataAdapter(cmd, conn))
{
conn.Open();
cmd.CommandText = myCommand;
DataSet mySet = new DataSet();
adapt.Fill(mySet, "SampleData");
return mySet.Tables["SampleData"];
}
}
}
But normally DataSets and DataTables are artifacts of the past. Today you are better off using strongly typed models.
So define a model:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
}
and then write a method that will return a list of those models:
class Program
{
static void Main(string[] args)
{
var models = SelectTop100Models("SELECT top 100 * FROM x");
//do other stuff
}
static IEnumerable<MyModel> SelectTop100Models()
{
var connectionString = ConfigurationManager.ConnectionStrings["XXX"].ConnectionString;
using (var conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT top 100 * FROM x";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return new MyModel
{
Id = reader.GetInt32(reader.GetOrdinal("ID")),
Name = reader.GetString(reader.GetOrdinal("Name")),
};
}
}
}
}
}
Alternatively you might consider using an ORM framework such as the ADO.NET Entity Framework as it will simplify you querying the relational database and working directly with your strongly typed models using LINQ queries.
Not a big deal but for neatness sake is there any way to "create and open" a SqlConnection?
I naively wrote this code:
using (var strConnection = new SqlConnection(sourceConnection))
using (var strCommand = new SqlCommand(query, strConnection))
using (var reader = strCommand.ExecuteReader())
{
...
}
Which of course fails on line 3 because the connection isn't open.
Is there a neat way to avoid that nesting that opening the connection introduces?
using (var strConnection = new SqlConnection(sourceConnection))
{
strConnection.Open();
using (var strCommand = new SqlCommand(query, strConnection))
using (var reader = strCommand.ExecuteReader())
{
...
}
}
Good question, my idea is an Extension-Method for SqlConnection.
Check this:
public static class SqlExtensions {
public static SqlConnection OpenAndReturn(this SqlConnection con) {
try {
con.Open();
return con;
} catch {
if(con != null)
con.Dispose();
throw;
}
}
}
Usage:
using(var strConnection = new SqlConnection("CONNECTION").OpenAndReturn())
using(var strCommand = new SqlCommand("QUERY", strConnection))
using(var reader = strCommand.ExecuteReader()) {
//...
}
What about something like that:
class SqlHelper : IDisposable
{
public SqlHelper(string connectionString, string query) { ... }
public SqlConnection Connection { get; set; }
public SqlCommand Command { get; set; }
// SQL querying logic here
public void Execute() { ... }
/** IDisposable implementation **/
}
and in your code
using (SqlHelper sql = new SqlHelper(sourceConnection, query))
{
var reader = sql.Execute();
...
}