I know what I asking might not make a lot of sense for C# experts but I'll explain what I want to do and then you can suggest me how to do it in a better way if you want ok?
I have a C# class called DatabaseManager that deals with different MySQL queries (ado.net NET connector, not linq or any kind of ActiveRecord-ish library).
I am doing something like
categories = db_manager.getCategories();
The list of categories is quite small (10 items) so I'd like to know what's the best way of accessing the retrieved information without a lot of additional code.
Right now I'm using a Struct to store the information but I'm sure there's a better way of doing this.
Here's my code:
public struct Category
{
public string name;
}
internal ArrayList getCategories()
{
ArrayList categories = new ArrayList();
MySqlDataReader reader;
Category category_info;
try
{
conn.Open();
reader = category_query.ExecuteReader();
while (reader.Read())
{
category_info = new Category();
category_info.name = reader["name"].ToString();
categories.Add(category_info);
}
reader.Close();
conn.Close();
}
catch (MySqlException e)
{
Console.WriteLine("ERROR " + e.ToString());
}
return categories;
}
Example:
public IEnumerable<Category> GetCategories()
{
using (var connection = new MySqlConnection("CONNECTION STRING"))
using (var command = new MySqlCommand("SELECT name FROM categories", connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return new Category { name = reader.GetString(0) };
}
}
}
}
Remarks:
Let ADO.NET connection pooling do the right work for you (avoid storing connections in static fields, etc...)
Always make sure to properly dispose unmanaged resources (using "using" in C#)
Always return the lowest interface in the hierarchy from your public methods (in this case IEnumerable<Category>).
Leave the callers handle exceptions and logging. These are crosscutting concerns and should not be mixed with your DB access code.
The first thing I would do is to replace you use of ArrayList with List that will provide compile-time type checkig for your use of the category list (so you will not have to type cast it when using it in your code).
There's nothing wrong with returning them in an like this. However, a few things stand out:
Your catch block logs the error but
then returns either an empty array or
a partially populated array. This
probably isn't a good idea
If an exception is thrown in the try
block you won't close the connection
or dispose of the reader. Consider
the using() statement.
You should use the generic types
(List<>) instead of ArrayList.
From your code I guess you are using .NET 1.1, becuase you are not using the power of generics.
1) Using a struct that only contains a string is an overkill. Just create an arraylist of strings (or with generics a List )
2) When an exception occurs in your try block, you leave your connection and reader open... Use this instead:
try
{
conn.open();
//more code
}
catch (MySqlException e) { // code
}
finally {
conn.close()
if (reader != null)
reader.close();
}
Related
I have created a simplified SQL Data class, and a class method for returning a ready to use resultset:
public SQL_Data(string database) {
string ConnectionString = GetConnectionString(database);
cn = new SqlConnection(ConnectionString);
try {
cn.Open();
} catch (Exception e) {
Log.Write(e);
throw;
}
}
public SqlDataReader DBReader(string query) {
try {
using (SqlCommand cmd = new SqlCommand(query, this.cn)) {
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
} catch {
Log.Write("SQL Error with either Connection String:\n" + cn + " \nor Query:\n" + query);
throw;
}
}
(I catch any errors, log them, and then catch the error higher up the chain. Also, I did not include the ConnectionString() code for brevity. It just returns the requested connection string. That's all.)
This all works just fine, and with a single line of code, I'm ready to .Read() rows.
SqlDataReader rs = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees");
while (rs.Read()) {
// code
}
rs.Close();
I want to expand this and add a .ColumnReader() method that I want to chain to .DBReader() like this:
string empID = new SQL_Data("MyDatabase").DBReader(#"SELECT * FROM Employees).ColumnReader("EmpID");
I attempted this by adding a .ColumnReader() method, but it ends up being a method of SQL_Data() class directly, not a member or extension of .DBReader(). I also tried adding the .ColumnReader() inside the .DBReader() (like a "closure"), but that didn't work either.
Can this be done?
This ended up working for me:
public static class SQLExtentions {
public static dynamic ColumnReader(this SqlDataReader rs, string colName) {
return rs[colName];
}
}
I will have to expand on it a bit to add some error checking, and perhaps return more than just the dynamic value - like return an object with the value and it's SQL data type. But Paul and Bagus' comments got me on the right track.
I've came across a code snippet like the following:
using (IDbCommand selectCommand = this.createCommand(selectSQL))
using (IDataReader theData = selectCommand.ExecuteReader())
{
while (theData.Read())
{
Phone aPhone = new Phone(...some code here...);
thePhones.Add(aPhone);
}
}
Now I am trying to learn the using statement in this context in by interpreting above code into old try/finally statement. From my understanding, the using statement will interpret the code after the brackets as try. In this case, try should enclose all the stuff after (IDbCommand selectCommand = this.createCommand(selectSQL)). However in this code, there is another using statement immediately comes after the first using.
Is it correct to interpret them as a nested try/finally statement in this context?
Using statements will automatically call Dispose() for any IDisposable object within it declaration. It's important to note that this is not a replacement for any try/catch/finally statements that your own code may need to do. The using statement ensures that Dispose() will be called if any code within it's block throws or not. This is extremely important any time I/O objects are used such as network calls, stream reading, database calls, etc. This will make sure you don't have memory leaks or locked files.
using (IDbCommand selectCommand = this.createCommand(selectSQL))
{
//an exception or not will call Dispose() on selectCommand
using (IDataReader theData = selectCommand.ExecuteReader())
{
//an exception or not will call Dispose() on theData
while (theData.Read())
{
Phone aPhone = new Phone(...some code here...);
thePhones.Add(aPhone);
}
}
}
The "equivalent" to running this code without using statements would be:
var selectCommand = this.createCommand(selectSQL);
try
{
var theData = selectCommand.ExecuteReader();
try
{
while (theData.Read())
{
Phone aPhone = new Phone(...some code here...);
thePhones.Add(aPhone);
}
}
finally
{
if (theData != null)
{
theData.Dispose();
}
}
}
finally
{
if (selectCommand != null)
{
selectCommand.Dispose();
}
}
However, the above code should not be used since the using statement is refined, optimized, guaranteed, etc, but the gist of it is above.
using (IDbCommand selectCommand = this.createCommand(selectSQL))
using (IDataReader theData = selectCommand.ExecuteReader())
{
}
At compile time will be converted to
using (IDbCommand selectCommand = this.createCommand(selectSQL))
{
using (IDataReader theData = selectCommand.ExecuteReader())
{
}
}
Just that. And it not related to try catch in anything
Yes, your code is interpreted as
using (IDbCommand selectCommand = this.createCommand(selectSQL))
{
using (IDataReader theData = selectCommand.ExecuteReader())
{
while (theData.Read())
{
Phone aPhone = new Phone(...some code here...);
thePhones.Add(aPhone);
}
}
}
which could be thought of as "nested try/finally blocks"
Using multiple using statements is just like with multiple if statements, you could do
if(foo)
if(bar)
{
DoStuff()
}
Which is the same as
if(foo)
{
if(bar)
{
DoStuff()
}
}
I'm trying to return a string though a Getter, the string is obtained from a DataRead object which loops through the mysql query. The problem is that, upon load, the string does not get loaded onto the main form's label, it returns an empty string and if I assign a string to the variable upon declaration, it returns that to my main form. Here's the code:
string text;
public string Text { get { return text; } }
public void DBConn()
{
MySqlConnection conn = new MySqlConnection(connStr);
DataSet ds = new DataSet();
MySqlDataReader reader = null;
try
{
// connection to DB
reader = cmd.ExecuteReader();
if (reader != null && reader.HasRows)
{
while (reader.Read())
{
text = reader["string1"].ToString() + " " + reader["string2"].ToString() + " " + reader["string3"].ToString();
}
}
}
// try, catch. conn.close()
}
The reader is assigning the values onto text just fine, but outside of the while, the value is not assigned to the globally declared variable, it seems to get destroyed as soon as it leaves the loop. Can anyone please help?
Thank you.
Thank you to those who helped me find the solution to the problem described. As some of you kindly explained (and I was lacking the knowledge to understand at first until a lot of research was done), the issue turned out to be a problem with my Main form being in a different instance from the first class that calls upon the class that has the Getter in it. I know now I should have mentioned all of these details in the original post, I apologize to those of you who tried to understand from a small piece of code. After a lot of research on Instances, the solution I came to was to first, create a class that holds static Getters and Setters public static string Text { set; get; } (this was how I wanted it for easy access from other classes) this helps in making them accessible from all instances, it might not be the best solution especially on OOP - During my research I read somewhere that using Setters and Getters is a waste of time and not the best practice for programmers, that it is best to encapsulate but because I am still very fresh I will look up how to do that later.
Thank you guys again.
So i have this project that needs to print data from the database.
I do this with a simple foreach loop:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
The problem is that it will loop anywhere from 2 times to 6 times meaning that it prints everything at least twice.
So at the moment my database contains Animal_Id's id:1 and id:2.
Now i get in my listbox (1, 2, 1, 2) or more depending on the amount of loops. I have no idea why this is happening and how to fix this. So all help would be highly appreciated
ps: if more code or information is needed please let me know.
pss: this is for a windows mobile 6.5 device with .net 3.5 build in Visual studio 2008. also im useing sqlite (not the newest version)
EDIT: After some testing it looks like my other 2 foreach loops in this project have the same problem.
Edit: So with al you guy's help i was able to fix it.
public void GetData()
{
try
{
SQLiteConnection Connection = new SQLiteConnection(#"Data Source = \Program Files\Mobile\Resources\Database\PPP_DB");
Connection.Open();
SQLiteCommand Command = new SQLiteCommand(Query, Connection);
Command.ExecuteNonQuery();
Data_Adapter = new SQLiteDataAdapter("SELECT * FROM Animal_Info", Connection);
Data_Set.Reset();
Data_Adapter.Fill(Data_Set);
Data_Table = Data_Set.Tables[0];
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
foreach (DataRow row in Data_Table.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
Connection.Close();
Program.AnimalInfo.Refresh();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
So it seems that it messed up with the DataTable somewhere, and that made it loop multiple times. ps: i tried to set all items with stuff like Databound, but didn't really work out for me, thats the reason im still doing it this way.
Add the following two lines at the top of your function.
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
_dataTable.Clear();
This will ensure that you do not double up any row data.
Assuming that there is no unique constraint on "Animal_ID" field in the database (you din't answer to my comment), check for items duplication:
if (!Program.AnimalInfo.Info_ID_ListBox.Items.Contains(row["Animal_ID"].ToString())
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
Well first, LoadDatabase() should return a DataTable (or DataSet), this make sthe LoadDatabase() function useful for other data requests, Also you should never need to use a 'for loop' to add items to a ListBox. you can bind the ListBox directly to the source.... something like this should do it
listBox1.DataSource = _dataTable;
listBox1.ValueMember = "Animal_ID";
listBox1.DisplayMember = "Animal_ID";
There are some examples using SqlDataReader and SqlDataAdapter here http://gsidev.somee.com/#2&2AD97ECBE2AE41D08191F6E4C773D8A9&cs
Unique contraints are not needed here. But the listbox.Items collection might have have to be cleared.
Possibly you are (or the system, depends on where the code is, for example in a paint event handler) calling the 'LoadDatabase()' function twice!
Just place a break point in the _dataAdapter.fill() and press F11 to let the app run over this and then right click the _dataTable to inspect it's contents.
Although you can explictely set the datasource of the ListBox, that is not needed and may lead to other side effects you are currently not aware of. It is OK to start with simple code before letting the system do unknwon stuff in the background.
Try that:
public void LoadDatabase()
{
_connection.Open();
_dataAdapter.Fill(_dataTable);
Program.AnimalInfo.Info_ID_ListBox.Items.Clear();
try
{
foreach (DataRow row in _dataTable.Rows)
{
Program.AnimalInfo.Info_ID_ListBox.Items.Add(row["Animal_ID"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to LoadDatabase()" + ex.Message);
}
_connection.Close();
}
I have this code that queries a database. I want to put the actual database code into a separate class so I can reuse it in other places. This will leave just the actual read of the PassResult value so I can make a Unit Test of the code without having the SQL code running. I am having trouble finding references on how to make this kind of code Unit Testable. Could someone help out?
using System;
using System.Data;
using System.Data.SqlClient;
namespace CS_UI_Final_Inspection
{
public class CalibrationTestCheck
{
// declare the variables
private bool _calibrationTestPass = false;
private string _connectionString = string.Empty;
public bool CheckCalibrationTestResults(string serialNumber, IDeviceInfo deviceInfo, string mapID)
{
// get database location
DhrLocationPull dhrLocation = new DhrLocationPull();
_connectionString = dhrLocation.PullDhrLocation();
// build the query
SqlConnection calibrationCheckConnection = new SqlConnection(_connectionString);
SqlCommand calibrationCheckCommand = new SqlCommand("[MfgFloor].[GetLatestTestResultsForDeviceByTestType]",
calibrationCheckConnection);
// build the stored proc
calibrationCheckCommand.CommandType = CommandType.StoredProcedure;
calibrationCheckCommand.Parameters.Add(new SqlParameter("#SerialNumber", serialNumber));
calibrationCheckCommand.Parameters.Add(new SqlParameter("#DeviceTypeID", mapID));
calibrationCheckCommand.Parameters.Add(new SqlParameter("#TestDataMapTypeID", "C"));
calibrationCheckCommand.Connection.Open();
SqlDataReader calibrationCheckReader = calibrationCheckCommand.ExecuteReader();
// is there data?
if (calibrationCheckReader.HasRows)
{
// read the data
calibrationCheckReader.Read();
try
{
_calibrationTestPass = (bool) calibrationCheckReader["PassResult"];
}
catch (InvalidOperationException)
{
// means last element was not filled in
}
finally
{
// close refs
calibrationCheckReader.Close();
calibrationCheckCommand.Connection.Close();
calibrationCheckConnection.Close();
calibrationCheckReader.Dispose();
calibrationCheckCommand.Dispose();
calibrationCheckConnection.Dispose();
}
}
return _calibrationTestPass;
}
}
}
create an interface and implement it.
move all references to be tested to use the interface (exposing any methods/properties required through the interface)
have the constructor or method being tested take the interface as a parameter.
Roy Oscherov is a good resource on this. Roy Oscherov wrote a great book called "The art of unit testing". Roy's website can be found here: http://osherove.com/