I am trying to read through a stored SQLiteDataReader object. In theory, it "should" work because the object is stored in a variable before it is referenced (and doesn't hit an error until the reference line is reached), but maybe I have the wrong idea.
I'm trying to keep my application in a neatly layered architecture. So, each database table having its own C# class with its own methods for select, insert, update, and delete; only the data layer knows how to communicate with the database, etc.
I was running into connection issues earlier when I tried to make one static SQLiteConnection object that all the data layer classes could reference (so as to keep it open and minimize overhead, if any). So I'm trying to go with the using block to make sure the connection is properly disposed each time I need to access the database, and hoping that this won't cause performance issues.
So basically, here is the method in my DatabaseConnection class that handles basic query execution:
public SQLiteDataReader ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
}
return rdr;
}
And here is the code that calls that method. I'll use an object/record of the Associate table as an example.
public class Associate
{
public int RowId { get; private set; }
public int Id { get; set; }
public string Name { get; set; }
private string password;
public string Password
{
get
{
return password;
}
set
{
password = Hash(value); // external password hashing method
}
}
public Associate() { } // default constructor with default values
public Associate(int id)
{
this.Id = id;
Select();
}
// select, insert, update, delete methods
private void Select() { ... }
// these are non-queries and return true/false based on success
public bool Insert() { ... }
public bool Update() { ... }
public bool Delete() { ... }
/* Method that causes the error */
public static Associate[] GetAll()
{
DatabaseConnection con = new DatabaseConnection();
SQLiteDataReader rdr = con.ExecuteQuery("SELECT id FROM Associate");
List<Associate> list = new List<Associate>();
if (rdr != null)
{
while (rdr.Read()) /* this line throws the exception */
{
int next = rdr.GetInt32(0);
list.Add(new Associate(next));
}
}
return list.ToArray();
}
}
The idea here is that using the rdr object, I can access the column names directly so that if the database ever changes, I won't have to rewrite a bunch of code to adjust for the column indices (rdr["id"], rdr["name"], etc.)
So what I don't understand is why rdr in the calling method is having "object disposed" issues because it's stored in a variable before I reference it. I know the connection is disposed at the end of the called method, but since the returned result is stored, shouldn't it technically be able to "survive" outside the using block?
It is the connection that got disposed. The data reader can only read data while the connection still exists.
public SQLiteDataReader ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
}
// *** Connection gone at this stage ***
return rdr;
}
Your options are to either return a DataTable, e.g.
public DataTable ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
var dataTable = new DataTable();
dataTable.Load(rdr);
return dataTable;
}
}
otherwise, you could keep the connection alive inside the DatabaseConnection class:
class DatabaseConnection : IDisposable
{
private readonly IDbConnection _conn;
public DatabaseConnection()
{
_conn = new SQLiteConnection(ConnectionString);
}
public void Dispose()
{
_conn.Dispose();
}
public SQLDataReader ExecuteQuery(string sql)
{
...
}
}
// sample usage
using (var conn = new DatabaseConnection())
{
using (var reader = conn.ExecuteQuery("SELECT ...")
{
// do your work in here
}
}
Related
Here i'm using connection state in abstract class
public abstract class Connection
{
private static string _Connection = ConfigurationManager.ConnectionStrings["CTXDB"].ConnectionString;
public static string GetConnection{ get { return _Connection; }}
}
public abstract void ProcessConnection();
This class i implemented in Another class(BusinessConnection.cs)
public class BusinessClass :Connection
{
public override void ProcessConnection()
{
using (var conn = new SqlConnection(Connection.GetConnection))
{
conn.Open();
}
}
Now i called this Class(BusinessConnection.cs) In my controller as
BusinessClass objcon = new BusinessClass();
public IHttpActionResult Index()
objcon.ProcessConnection();
SqlCommand cmd = new SqlCommand("select * from Employee");
//Here how can i inject my Connection in cmd
SqlDataReader rdr = cmd.ExecuteReader();
if (rdr.HasRows == true)
There seems to be a little design flaw. You obviously want to use the connection outside the BusinessClass/Connection classes.
So maybe you can do something like this:
public abstract class Connection
{
public abstract SqlConnection OpenConnection();
// your code
}
and in your BusinessClass:
public override SqlConnection OpenConnection()
{
var conn = new SqlConnection(Connection.GetConnection))
conn.Open();
return conn;
}
And then in your querying code:
using(SqlConnection conn = objcon.OpenConnection())
{
// create SqlCommand and pass conn!
using(SqlCommand cmd = new SqlCommand("select * from Employee", conn))
{
using (SqlDataReader rdr = cmd.ExecuteReader())
{
// and the rest of your rdr reading code
}
}
}
The using statements take care of closing/disposing the respective objects.
I am new to .net. Please help me what the things should I add here? How to add stored procedure? My aim is to keep DataAccess class in one project and use it in other project as .dll. In other project I just want to call dbclass and to add parameter, execute method only. Please help me
My code:
public class DataAccess
{
string commandString;
string commandType;
private static string ConnectionStrings = ConfigurationManager.ConnectionStrings["studentConnectionString"].ToString();
SqlConnection connection = new SqlConnection(ConnectionStrings);
SqlCommand command = new SqlCommand();
DataTable dt = new DataTable();
public String commandStrings
{
get { return commandString; }
set { commandString = value; }
}
public String commandTypes
{
get { return commandType; }
set { commandType = value; }
}
public void addparameters(String name, Object value)
{
command.Parameters.AddWithValue(name, value);
}
public DataTable Exec(String Query)
{
DataTable datatable = new DataTable();
if (connection.State != ConnectionState.Open)
{
connection.Close();
connection.Open();
}
try
{
SqlDataAdapter adapter = new SqlDataAdapter(Query, connection);
adapter.Fill(datatable);
return datatable;
}
catch (Exception e)
{ }
finally
{
connection.Close();
}
return null;
}
public int Execute(String Query)
{
int AffectedRows = 0;
command.CommandText = Query;
command.Connection = connection;
if (connection.State != ConnectionState.Open)
{
connection.Close();
connection.Open();
}
try
{
AffectedRows = command.ExecuteNonQuery();
return AffectedRows;
}
catch (Exception e)
{ }
finally
{
connection.Close();
}
return 0;
}
}
Stored procedures are executed through SqlCommand, there's not much difference to running a insert/update statement.
SqlCommand cmd = new SqlCommand("sp_name", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#SomeParameter", SqlDbType.VarChar).Value = someValue;
con.Open();
cmd.ExecuteNonQuery();
You should consider implementing IDisposable for the DataAccess class to dispose of db resources when they are not needed.
I've tried for a while now and I really dont get it. I recive error "Cannot implicitly convert type 'void' to 'string'"
I have tried multiple variants of string, int, nothing, void, public, static and nope I really dont get it right.
I want to get one value from my db thoug my DAL and BLL, my code looks like this.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Repeater1.DataSource = BLL.getGames();
Repeater1.DataBind();
var culture = CultureInfo.GetCultureInfo("sv-SE");
var dateTimeInfo = DateTimeFormatInfo.GetInstance(culture);
var dateTime = DateTime.Today;
int weekNumber = culture.Calendar.GetWeekOfYear(dateTime, dateTimeInfo.CalendarWeekRule, dateTimeInfo.FirstDayOfWeek);
string mroundY = dateTime.Year.ToString();
string mroundW = weekNumber.ToString();
string mrounddate = mroundY + mroundW;
string mround = BLL.getMroundGames(mrounddate); <-- Here's the error
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
}
}
My BLL looks like this;
public class BLL
{
public static void getMroundGames(string mrounddate)
{
SqlCommand getMroundGames = new SqlCommand("SELECT mround FROM gameTB where mround = #mrounddate");
DAL.ExecuteNonQuery(getMroundGames);
}
}
Also tried this;
public class BLL
{
public static DataTable getMroundGames(string mrounddate)
{
SqlCommand getMroundGames = new SqlCommand("SELECT mround FROM gameTB where mround = #mrounddate");
getMroundGames.Parameters.Add("#mrounddate", SqlDbType.VarChar, 10).Value = mrounddate;
return DAL.GetData(getMroundGames);
}
}
And finaly my DAL looks like this;
public class DAL
{
public static SqlConnection GetConnection()
{
SqlConnection conn = new
SqlConnection(ConfigurationManager.ConnectionStrings["tiptopConnectionString"].ConnectionString);
conn.Open();
return conn;
}
public static DataTable GetData(SqlCommand command)
{
try
{
using (SqlConnection conn = GetConnection())
{
using (DataSet ds = new DataSet())
{
using (SqlDataAdapter da = new SqlDataAdapter())
{
da.SelectCommand = command;
da.SelectCommand.Connection = conn;
da.Fill(ds);
return ds.Tables[0];
}
}
}
}
catch (Exception err)
{
throw new ApplicationException(string.Format("Felmeddelande:{0}", err.Message));
}
}
public static object ExecuteScalar(SqlCommand command)
{
using (SqlConnection conn = GetConnection())
{
command.Connection = conn;
object result = command.ExecuteScalar();
return result;
}
}
public static void ExecuteNonQuery(SqlCommand command)
{
using (SqlConnection conn = GetConnection())
{
command.Connection = conn;
command.ExecuteNonQuery();
}
}
}
Where to begin?
The signature for this is wrong;
public static void getMroundGames(string mrounddate)
You need to change it to something similar to ;
public static string getMroundGames(string mrounddate)
Retrieve the string value from your DAL and return to the consumer accordingly.
var dt = Dal.GetData();
return (string)dt.Rows[0]["field"];
However, in all honesty, I would not be passing a datatable from your DAL to your BLL. I would return the string directly or introduce a DTO and populate this from your DAL through your BLL, back to the consumer.
You need to add a return type of string to getMroundGameas(string mrounddate).
It's not clear what type of object DAL is, but you should also probably use ExecuteReader method, http://msdn.microsoft.com/en-us/library/bb344397.aspx
rather then ExecuteNonQuery. This will return a Reader which can be queried for the value returned by the select statement.
Finally you should return the value.
I am trying to mock database operations. I have problem in mocking SqlParameterCollection. I tried to create virtual method that will return DbParameterCollection but then i am loosing all the functionality that SqlParameterCollection gives like AddWithValue etc. Is there a way i can mock SqlParameterCollection? Is there any other approach to unit test DAL? I am using Moq.
Code goes like this:
in DAL:
protected virtual IDbConnection GetConnection(string connectionString)
{
return new SqlConnection(connectionString);
}
protected virtual IDbCommand GetCommand(IDbConnection cn)
{
return cn.CreateCommand();
}
protected virtual IDbTransaction GetTransaction(IDbConnection cn)
{
return cn.BeginTransaction(IsolationLevel.Serializable);
}
Public Bool InsertInDatabase(DataTable dt)
{
using (IDbConnection cn = GetConnection(cnstr))
{
cn.Open();
using (IDbTransaction tran = GetTransaction(cn))
{
IDbCommand cmd = GetCommand(cn);
cmd.Transaction = tran;
cmd.Connection = cn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_InsertInDatabase";
SqlParameterCollection cmdParams = cmd.Parameters as SqlParameterCollection;
cmdParams.AddWithValue("#param1", dt);
cmd.ExecuteNonQuery();
}
}
}
In Unit test project:
protected override IDbConnection GetConnection(string connectionString)
{
return Mock.Of<IDbConnection>();
}
protected override IDbCommand GetCommand(IDbConnection cn)
{
return Mock.Of<IDbCommand>();
}
protected override IDbTransaction GetTransaction(IDbConnection cn)
{
return Mock.Of<IDbTransaction>();
}
public void TestInsertInDatabase()
{
base.InsertInDatabase(new DataTable());
}
--Solution--
Created an extension method to add parameter with value. Thank you Marc Gravell for pointing me to that direction.
public static IDbDataParameter AddParameterWithValue(this IDbCommand cmd, string paramName, object paramValue)
{
var dbParam = cmd.CreateParameter();
if (dbParam != null)
{
dbParam.ParameterName = paramName;
dbParam.Value = paramValue;
}
return dbParam;
}
Personally, I approach this problem by writing an AddParameterWithValue extension method to DbCommand (or IDbCommand). It has to be on the command so that you have access to CreateParameter, and then call .Parameters.Add.
This allows easy usage against any ADO.NET stack, including abstractions like logging decorators.
#Asdfg HI I have basically mocked the parameter collection as below
string connectionString = "connectionstring";
var sqlConnection = new SqlConnection(connectionString);
var command = sqlConnection.CreateCommand();
//****************Setup Mock************************//
Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(System.Data.SqlClient.SqlClientPermissionAttribute));
var mockDataReader1 = new Mock<IDataReader>();
command.Parameters.Add(new SqlParameter("#po_tint_Result", 1));
//setup read return value
Queue<bool> responseQueue = new Queue<bool>();
responseQueue.Enqueue(true);
responseQueue.Enqueue(false);
mockDataReader1.Setup(a => a.Read()).Returns(() => responseQueue.Dequeue());
var mockDb = new Mock<SqlDatabase>(connectionString);
mockDb.Setup(a => a.GetStoredProcCommand("SPNAME")).Returns(command);
mockDb.Setup(a => a.ExecuteNonQuery(command));
obj1.DbConn = mockDb.Object;
//*************************************************//
Hope this helps
Hi i found the solution.
I had to implement a Moq for the IDataParameterCollection interface and had to send it to the instance of IDbCommand.
With that my IDbCommand.Parameters object became different from null.
public static IDbConnection IDbConnectionMock(int valReturn)
{
var dataParameterCollection = new Mock<IDataParameterCollection>();
var command = new Mock<IDbCommand>();
command.Setup(x => x.Parameters).Returns(dataParameterCollection.Object);
command.Setup(x => x.ExecuteNonQuery()).Returns(valReturn);
var connection = DbConnectionMock_Success(command.Object);
return connection;
}
I have my connection string stored in a myGlobals.cs page as below:
/* Connection String */
public static string conString
{
get { return _conString; }
set { _conString = ConfigurationManager.ConnectionStrings["BaseConnectionString"].ToString(); }
}
I have my code setup in the 4 tier architecture. So I have a Business Object folder, Business Access Layer & Data Access Layer. If I move the connection string into the DAL structure it works fine. Other wise I get this error:
The ConnectionString property has not been initialized
Is the myGlobals.cs class not being included before all this or does it need to be changed around?
Here is my Data Access Layer:
public DataTable Load()
{
SqlConnection conn = new SqlConnection(MyGlobals.conString);
SqlDataAdapter dAd = new SqlDataAdapter("administratorGetAll", conn);
dAd.SelectCommand.CommandType = CommandType.StoredProcedure;
DataSet dSet = new DataSet();
try
{
dAd.Fill(dSet, "AdministratorsTable");
return dSet.Tables["AdministratorsTable"];
}
catch
{
throw;
}
finally
{
dSet.Dispose();
dAd.Dispose();
conn.Close();
conn.Dispose();
}
}
The property 'setter' doesn't get called automatically; its code is executed when you put the property on the left-hand side of an assignment - MyClass.MyProperty = "new value";
You want to be doing this:
public static string conString
{
get { return ConfigurationManager.ConnectionStrings["BaseConnectionString"]; }
}