Error when doing two calls from DB - c#

I'm getting the error "Cannot acess to disposable object" when I try to use the same connection. So this is my Oracle context:
public class MyOracleContext
{
DbConnection connection;
public MyOracleContext()
{
connection = new OracleConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
}
public TOut ExecuteCommand<TOut>(IDbCommand command, Func<IDataReader, TOut> mapHelper)
{
TOut result = default(TOut);
try
{
using (connection)
{
using (command)
{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
using (IDataReader reader = command.ExecuteReader(CommandBehavior.Default))
{
result = mapHelper(reader);
}
}
}
}
catch (Exception _exp)
{
throw new Exception("Error!" + _exp.Message);
}
return result;
}
public IDbCommand GetCommand()
{
OracleCommand cmd = (OracleCommand)connection.CreateCommand();
cmd.BindByName = true;
return cmd;
}
public IDataParameter GetParameter()
{
return new OracleParameter();
}
public bool ExecuteCommand(IDbCommand command)
{
bool result;
try
{
using (connection)
{
command.Prepare();
using (command)
{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
result = command.ExecuteNonQuery() > 0;
}
}
}
catch (Exception _exp)
{
throw new Exception("Error!" + _exp.Message);
}
return result;
}
public DbParameter GetParameter(string name, object value)
{
OracleParameter para = new OracleParameter(name, value);
para.Size = int.MaxValue;
return para;
}
}
I use the ExecuteCommand to Get results from DB, and the ExecuteCommand to, for eg, Insert. But, when I use the two commands on the same method, it gives me the error "Cannot access to disposable object", when I do Connection.Open, on the method ExecuteCommand . But if i do the inverse order (use first the ExecuteCommand and then use ExecuteCommand), it pass. The problem is that I want to get results from the BD, to compare and then insert. Any idea why? I've been stuck here for hours

You are holding the DbConnection as a property of the class, but then disposing of it after executing a command. If another command is then executed using the same instance, then it's trying to use a disposed connection.
Connections are pooled by .NET, so they're relatively cheap to create. I would just create the command in the ExceuteCommand method:
public TOut ExecuteCommand<TOut>(IDbCommand command, Func<IDataReader, TOut> mapHelper)
{
TOut result = default(TOut);
//try
//{
using (DbConnection connection = new OracleConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
//using (command)
//{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
using (IDataReader reader = command.ExecuteReader(CommandBehavior.Default))
{
result = mapHelper(reader);
}
//}
}
}
//catch (Exception _exp)
//{
// throw new Exception("Error!" + _exp.Message);
//}
return result;
}
There are two other changes I would suggest:
You should not be disposing the command in this method since it didn't create it. Whatever is responsible for creating a disposable item is
responsible for disposing of it.
Do not catch any exception simply to throw another plain vanilla Exception with just the error message extracted. Either just let the exception bubble up or do something meaningful (log it, add some more context, etc) and then either throw a new exception attaching the original exception as the InnerException or rethrow the exception

Change the "return result;" to inside the using(){ }
Like that:
public TOut ExecuteCommand<TOut>(IDbCommand command, Func<IDataReader, TOut> mapHelper)
{
TOut result = default(TOut);
try
{
using (connection)
{
using (command)
{
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
using (IDataReader reader = command.ExecuteReader(CommandBehavior.Default))
{
return result = mapHelper(reader);
}
}
}
}
catch (Exception _exp)
{
throw new Exception("Error!" + _exp.Message);
}
}

Related

Session values in HttpContext.Current.Session in C# set and get

I have static class called MailContainer in a MasterPage.
In MailContainer Class, I have defined properties for get /set like below.
public static class MailContainer
{
public static string TheObjectPropertyEmail
{
get
{
return HttpContext.Current.Session["TheObjectPropertyEmail"].ToString();
}
set
{
HttpContext.Current.Session["TheObjectPropertyEmail"] = value;
}
}
}
When I try to assign an value like below on Default.aspx.cs with MasterPage.
MailContainer.TheObjectPropertyEmail = reader["Email"].ToString();
Its throwing the below exception.
System.NullReferenceException: Object reference not set to an instance
of an object.
on this line :
return HttpContext.Current.Session["TheObjectPropertyEmail"].ToString();
How do i fix this ?
Edit #01
public void Aut()
{
sql = #String.Format(" SELECT * FROM doTable ");
sql += String.Format(" WHERE ");
sql += String.Format(" UPPER(user) IN (?); ");
using (OdbcConnection myConnectionString =
new OdbcConnection(ConfigurationManager.ConnectionStrings["ConnMySQL"].ConnectionString))
{
using (OdbcCommand command =
new OdbcCommand(sql, myConnectionString))
{
try
{
if (username != null)
{
command.Parameters.AddWithValue("param1", username.ToString().ToUpper());
command.Connection.Open();
using (OdbcDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
MailContainer.TheObjectPropertyEmail = reader["Email"].ToString();
}
}
}
}
}
catch (Exception ex)
{
throw new ApplicationException("operation failed!", ex);
}
finally
{
command.Connection.Close();
}
}
}
}
Check HttpContext here. This an HttpContext only available in web related projects like
WebForms for asp .net
WebApi
MVC
Now depending on the lifecycle, the HttpContext is or isn't initiallized. We need to know more about your specific exmample on when you call and set, get the session for us to help you.
Cheers!

Best way to handle exception in ADO.Net repository pattern

i have design small repository pattern for ado.net. now i could not manage to handle exception proper way. i want to push error to calling environment if any occur. if no error occur then result set will be push to calling environment.
i have repository called AdoRepository which extend other repository classes like employee etc. we are calling employee repository function from mvc controller. so i want to push error to mvc controller from employee repository if any occur during data fetch, if no error occur then data will be sent to mvc controller. here is my full code. please have look and share the idea for best design. if possible paste rectified code here.
Base repository
public abstract class AdoRepository<T> where T : class
{
private SqlConnection _connection;
public virtual void Status(bool IsError, string strErrMsg)
{
}
public AdoRepository(string connectionString)
{
_connection = new SqlConnection(connectionString);
}
public virtual T PopulateRecord(SqlDataReader reader)
{
return null;
}
public virtual void GetDataCount(int count)
{
}
protected IEnumerable<T> GetRecords(SqlCommand command)
{
var reader = (SqlDataReader) null;
var list = new List<T>();
try
{
command.Connection = _connection;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
list.Add(PopulateRecord(reader));
}
reader.NextResult();
if (reader.HasRows)
{
while (reader.Read())
{
GetDataCount(Convert.ToInt32(reader["Count"].ToString()));
}
}
Status(false, "");
}
catch (Exception ex)
{
Status(true, ex.Message);
}
finally
{
// Always call Close when done reading.
reader.Close();
_connection.Close();
_connection.Dispose();
}
return list;
}
protected T GetRecord(SqlCommand command)
{
var reader = (SqlDataReader)null;
T record = null;
try
{
command.Connection = _connection;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
record = PopulateRecord(reader);
Status(false, "");
break;
}
}
catch (Exception ex)
{
Status(true, ex.Message);
}
finally
{
reader.Close();
_connection.Close();
_connection.Dispose();
}
return record;
}
protected IEnumerable<T> ExecuteStoredProc(SqlCommand command)
{
var reader = (SqlDataReader)null;
var list = new List<T>();
try
{
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
var record = PopulateRecord(reader);
if (record != null) list.Add(record);
}
}
finally
{
// Always call Close when done reading.
reader.Close();
_connection.Close();
_connection.Dispose();
}
return list;
}
}
StudentRepository which extend base AdoRepository
-----------------------------------------------
public class StudentRepository : AdoRepository<Student>
{
public int DataCounter { get; set; }
public bool hasError { get; set; }
public string ErrorMessage { get; set; }
public StudentRepository(string connectionString)
: base(connectionString)
{
}
public IEnumerable<Student> GetAll()
{
// DBAs across the country are having strokes
// over this next command!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents"))
{
return GetRecords(command);
}
}
public Student GetById(string id)
{
// PARAMETERIZED QUERIES!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents WHERE Id = #id"))
{
command.Parameters.Add(new ObjectParameter("id", id));
return GetRecord(command);
}
}
public IEnumerable<Student> GetStudents(int StartIndex, int EndIndex, string sortCol, string sortOrder)
{
string strSQL = "SELECT * FROM vwListStudents WHERE ID >=" + StartIndex + " AND ID <=" + EndIndex;
strSQL += " ORDER BY " + sortCol + " " + sortOrder;
strSQL += ";SELECT COUNT(*) AS Count FROM vwListStudents";
var command = new SqlCommand(strSQL);
return GetRecords(command);
}
public override Student PopulateRecord(SqlDataReader reader)
{
return new Student
{
ID = Convert.ToInt32(reader["ID"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
IsActive = Convert.ToBoolean(reader["IsActive"]),
StateID = Convert.ToInt32(reader["StateID"].ToString()),
StateName = reader["StateName"].ToString(),
CityID = Convert.ToInt32(reader["CityID"].ToString()),
CityName = reader["CityName"].ToString()
};
}
public override void GetDataCount(int count)
{
DataCounter = count;
}
public override void Status(bool IsError, string strErrMsg)
{
hasError = IsError;
ErrorMessage = strErrMsg;
}
}
calling StudentRepository from mvc controller like below way
public class StudentController : Controller
{
private StudentRepository _data;
public StudentController()
{
_data = new StudentRepository(System.Configuration.ConfigurationManager.ConnectionStrings["StudentDBContext"].ConnectionString);
}
// GET: Stuent
public ActionResult List(StudentListViewModel oSVm)
{
StudentListViewModel SVm = new StudentListViewModel();
SVm.SetUpParams(oSVm);
SVm.Students = _data.GetStudents(SVm.StartIndex, SVm.EndIndex, SVm.sort, oSVm.sortdir).ToList();
SVm.RowCount = _data.DataCounter;
return View("ListStudents",SVm);
}
}
I don't get the point of this:
catch (Exception ex)
{
Status(true, ex.Message);
}
Simply not catch the exception and let it bubble up to the caller who, according to you, will know to handle it. No callbacks necessary.
Storing retrieved data in instance state seems like a bad way to go. Rather, return an object with that data. That results in a more straight forward API and has less mutable state.
finally
{
reader.Close();
_connection.Close();
_connection.Dispose();
}
There is a better way to go about this: Wrap resources in a using statement. In particular, part ways with the superstitious double dispose pattern.
Let the caller deal with the exception making sure that you log a decent error message (showing all relevant fields). The Status class will annoy the hell out of support people as it swallows the stack trace and says nothing about the data which has caused the error. DB exceptions are often caused by malformed data so its important to have this data logged when things go wrong.
As an aside, your PopulateRecord and GetDataCount methods should be abstract as the base versions do nothing. Another dev could easily think they don't need to implement these methods and would be left with a class with useless PopulateRecord and GetDataCount methods.

Eliminate code duplication (Work with DB)

I've coded a dll to execute SQL commands.
And I have some code duplication in my public methods. Is there some way to avoid code duplication?
Method 1:
public Object ExecuteScalar(String command)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand commandScalar = GetSqlCommand(strCommand: command);
object result = new object();
using (commandScalar)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
result = commandScalar.ExecuteScalar();
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", commandScalar.CommandText), exception);
}
finally
{
connection.Close();
}
}
return result;
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
return result;
}
}
Method 2:
public void ExecuteNonQuery(String command, List<SqlParameter> lsParams = null)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand commandNonQuery = GetSqlCommand(strCommand: command, lsParams: lsParams, log: logger);
using(commandNonQuery)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
commandNonQuery.ExecuteNonQuery();
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", commandNonQuery.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
}
}
Method 3:
public List<IDataRecord> ExecuteReader(String strCommand, List<SqlParameter> lsParams = null)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand sqlCommandRead = GetSqlCommand(strCommand: strCommand, lsParams: lsParams, log: logger);
List<IDataRecord> lsDataRecord = new List<IDataRecord>();
using (sqlCommandRead)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
using (SqlDataReader sqlDataReader = sqlCommandRead.ExecuteReader())
{
while (sqlDataReader.Read())
{
lsDataRecord.Add((IDataRecord)sqlDataReader);
}
}
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", sqlCommandRead.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
this.AddDictionary(connection.RetrieveStatistics());
return lsDataRecord;
}
}
Method that generates SQL command to execute:
private static SqlCommand GetSqlCommand(string strCommand, List<SqlParameter> lsParams = null, Logger log = null)
{
lsParams = null ?? new List<SqlParameter>();
log = null ?? LogManager.GetLogger("noname");
if (strCommand == null || strCommand == "")
{
throw new ArgumentException("Передан пустой (или null) текст команды.", "strCommand");
}
int parametersRequired = strCommand.Split('#').Length - 1;
if (parametersRequired != lsParams.Count)
{
String strParameters = null;
foreach (var item in lsParams)
{
strParameters += item.ParameterName + " : " + item.Value + "\n";
}
strParameters = null ?? "No parameters";
throw new ArgumentException(String.Format("При формировании SQL - команды выявлено, что число требуемых параметров: {0} не соответствует числу переданных: {1}\n"
+ "Команда:\n{2}\nПараметры:\n{3}", parametersRequired, lsParams.Count, strCommand, strParameters), "lsParams");
}
SqlCommand sqlCommand = new SqlCommand(strCommand);
foreach (var item in lsParams)
{
sqlCommand.Parameters.Add(item);
}
return sqlCommand;
}
public class SqlHelper
{
private readonly bool statisticsEnabled;
public SqlHelper(bool statisticsEnabled)
{
this.statisticsEnabled = statisticsEnabled;
}
public T ExecuteSqalar<T>(SqlCommand command)
{
return Execute(command, c => (T) c.ExecuteScalar());
}
public void ExecuteNonQuery(SqlCommand command)
{
Execute(command, c => c.ExecuteNonQuery());
}
public List<IDataRecord> ExecuteReader(SqlCommand command)
{
return Execute<List<IDataRecord>>(command, c =>
{
var lsDataRecord = new List<IDataRecord>();
using (SqlDataReader sqlDataReader = command.ExecuteReader())
{
while (sqlDataReader.Read())
{
lsDataRecord.Add(sqlDataReader);
}
}
});
}
public T Execute<T>(SqlCommand command, Func<SqlCommand, T> processFunction)
{
using (var connection = new SqlConnection("CONNECTION_STRING"))
{
object result = new object();
using (command)
{
try
{
connection.StatisticsEnabled = statisticsEnabled;
connection.Open();
result = processFunction(command);
}
catch (Exception exception)
{
throw new Exception(String.Format("Не удалось выполнить команду: {0}", command.CommandText), exception);
}
finally
{
connection.Close();
}
}
if (connection.StatisticsEnabled)
AddDictionary(connection.RetrieveStatistics());
return (T)result;
}
}
private void AddDictionary(IDictionary retrieveStatistics)
{
// TODO:
}
}

Confused on returning values from class methods in C#

I am attempting to build a database constructor (factory?) in C#, however i'm confused as to how to manage errors while having "All paths return a value"
For example, here is a class that I would like to use to return a database connection:
public class DB
{
static SqlConnection Connect()
{
SqlConnection thisConnection;
try {
thisConnection = new SqlConnection(connectionString);
}
catch (SqlException e) { Console.WriteLine(e.Message); }
return thisConnection;
}
}
Obviously this won't work because "not all paths return a value".
How can I manage using try/catch while having a method actually return a value? Obviously i'm not a master Programmer ;) But i'm working on it.
You can return null or re-throw exception from your catch statement. But it is not really a good idea to use a static connection. Rule for database connection should be "Open as late as possible and close as early as possible".
You should enclose your connection in using statement that will ensure its disposal (closing) at the end of using block.
using(SqlConnection connection = new SqlConnection(connectionString))
{
//execute your command etc.
}
an example, always returns something
public bool yesno()
{
bool mybool = false;
try
{
mybool = true;
}
catch
{
mybool = false;
}
return mybool;
}
I would set thisConnection=null and then return it if you are unable to instantiate an object with the given connection string. And then check for null after calling this function like
If (DB.Connect("ConnectionString") != null) {
//Connection successful
}
else {
//Failed
}
public class DB
{
Static SqlConnection Connect(string connectionString)
{
SqlConnection thisConnection = null;
try {
thisConnection = new SqlConnection(connectionString);
}
catch (SqlException e) { Console.WriteLine(e.Message); }
return thisConnection;
}
}

What to put into my Class's Dispose method

I am writing my own data access layer class for the first time so I would like to wrap it inside a (using) parenthesis but that requires the class to implement Idisposable.
The only thing I put in there is
conn.close()
is there something else missing that i should be adding there?
class overview:
public class DAL : IDisposable
{
SqlConnection conn;
SqlCommand cmd;
SqlTransaction transaction;
public int Status = 1;
public DAL()
{
conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString);
conn.Open();
}
public void InsertRequest(UserAndRequestInfo UInfo, UserCrops UCrop)
{
}
public void InsertBGs(BreedingGroups BG)
{
}
public void EndTransaction()
{
if (Status == 1)
transaction.Commit();
if (Status < 0)
transaction.Rollback();
}
public void EndConnection()
{
conn.Close();
}
void Dispose()
{
conn.Close();
}
called from:
using (DAL db = new DAL())
{
foreach (var crop in UInfo.UserCropInfo)
{
db.InsertRequest(UInfo, crop);
foreach (BreedingGroups bg in crop.BGs)
{
db.InsertBGs(bg);
}
db.EndTransaction();
}
db.EndConnection(); <---- //If 'using' is there I'll remove this line
}
Also, in this particular case, wouldn't be just better to call an db.EndConnection() method
Yes: Call Dispose() on any disposable variables in your class.
void Dispose()
{
if(cmd != null)
cmd.Dispose();
if(transaction != null)
transaction.Dispose();
if(conn != null)
conn.Dispose();
}
edit: conn.Dispose() will also call conn.Close() so you shouldn't need to also call close it if you call dispose.
This is just a choich for you, to use the using scope in InsertRequest method instead as the following:
public class DAL
{
public void InsertRequest(UserAndRequestInfo UInfo, UserCrops UCrop)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
conn.Open();
SqlCommand command = conn.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = conn.BeginTransaction("SampleTransaction");
command.Connection = conn;
command.Transaction = transaction;
try
{
command.CommandText = "Insert into ...";
command.ExecuteNonQuery();
command.CommandText = "Insert into ...";
command.ExecuteNonQuery();
// Attempt to commit the transaction.
transaction.Commit();
}
catch (Exception ex)
{
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
public void InsertBGs(BreedingGroups BG)
{
}
}
Note that you don't need to make database connection in Constructure, remove it out.
And use the same concept to implement InsertBGs method.
This is form to call the
DAL db = new DAL();
foreach (var crop in UInfo.UserCropInfo)
{
db.InsertRequest(UInfo, crop);
foreach (BreedingGroups bg in crop.BGs)
{
db.InsertBGs(bg);
}
}

Categories