How to handle SqlDatareader on N-tier application? - c#

I am confuse where can i make my code to n-tier:
While learning n-tier i know now how to insert,delete,update.
But now i am confused how to deal with sqldatareader to bind data on listbox and combo box:
This code works on my presentation layer but dont know how to convert it to layers as DataAccess,BusinessObject,BusinessLogic.
FormLoad
{
getlistview();
cboStatus();
}
#region "fill listview"
public void GetlistView()
{
int i = 0;
SqlConnection sqlcon = new SqlConnection(connStr);
lstBF.Items.Clear();
SqlCommand sqlcom = new SqlCommand("sp_LoadNew", sqlcon);
SqlDataReader dr;
lstBF.Items.Clear();
sqlcon.Open();
dr = sqlcom.ExecuteReader();
while (dr.Read())
{
lstBF.Items.Add(dr["SerialNumber"].ToString());
lstBF.Items[i].SubItems.Add(dr["PartNumber"].ToString());
lstBF.Items[i].SubItems.Add(dr["StatusDescription"].ToString());
lstBF.Items[i].SubItems.Add(dr["CustomerName"].ToString());
lstBF.Items[i].SubItems.Add(dr["DateCreated"].ToString());
lstBF.Items[i].SubItems.Add(dr["CreatedBy"].ToString());
lstBF.Items[i].SubItems.Add(dr["ModifiedBy"].ToString());
i = i + 1;
}
if (sqlcon.State == ConnectionState.Open) sqlcon.Close();
}
#endregion
#region "ListviewChange"
private void lstBF_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstBF.SelectedItems.Count == 1)
{
txtSerialNumber.Text = lstBF.SelectedItems[0].Text;
txtPartNumber.Text = lstBF.SelectedItems[0].SubItems[1].Text;
lblStatus.Text = lstBF.SelectedItems[0].SubItems[2].Text;
lblcustomer.Text = lstBF.SelectedItems[0].SubItems[3].Text;
lblModifiedBy.Text = lstBF.SelectedItems[0].SubItems[6].Text;
}
}
#endregion
#region "FILL combo"
public void cboStatus()
{
try
{
SqlConnection conn = new SqlConnection(connStr);
SqlCommand sqlcom = new SqlCommand("sp_loadStatus",conn);
SqlDataReader dr = null;
conn.Open();
dr = sqlcom.ExecuteReader();
cmbStatus.Items.Clear();
while (dr.Read())
{
cmbStatus.Items.Add((dr["StatusDescription"]));
}
if (conn.State == ConnectionState.Open) conn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error Occurred:" + ex);
}
finally
{
}
}
#endregion

If you want to have a nice, clean separation, here's what you should do:
never ever pass something like a SqlDataReader or any other database-dependant object up from your data layer - encapsulate everything in your data layer, and from there on up, use your own domain model (classes)
the data layer should turn your database requests into objects of your domain model. You can definitely do that by hand - but it's a lot of boring and error prone code to do all the DataReader, read each row, convert to object kind of stuff - here, a tool called an OR mapper (object-relational mapper) can help tremendously, since it does all of this for you - more or less for free. Check out SubSonic, Linq-to-SQL and quite a few more out there.
for things like combobox lookup lists, you would typically design a "view model", e.g. a class for that "view" (or webform, or winform) that will hold the data that this view is supposed to a) show, and b) needs for its job. Typically, such a "view model" is just another class - no magic about it. It will contain one or several of your domain model classes (the actual data you want to show), and one or several lookup lists that contain the possible values for all the dropdowns etc.
With this approach, you should be fine and well on track to a good solid design, and by using an ORM, you can save yourself a ton of boring code and concentrate on the more interesting parts of your app.
Update:
Sample for binding your combo box:
create a class for your lookup values, typically something like:
public class StatusCode
{
public int ID { get; set; }
public string Description { get; set; }
}
have a method in your data layer to retrieve all values from your StatusCode table into a List<StatusCode>
public List<StatusCode> GetAllStatusCodes();
have your combo box in the UI bound to that list:
cbxStatusCode.DataSource = statusCodeList;
cbxStatusCode.DisplayMember = "Description";
cbxStatusCode.ValueMember = "ID";
Note: this is slightly different depending on whether you use Winforms or ASP.NET webforms.
There you have it!

One place you could start is using the Entity Framework or a class generator like Subsonic.
watch this podcast, follow it through and have a look at the code it creates for you:
http://www.techscreencast.com/language/dotnet/subsonic-getting-started-webcast/227

Related

c# methods not running in the sequence I wrote them

I got two standard projects in a solution. The UI and the Logic.
As usual, you need to take the inputs from the UI and do whatever you want with them in the back end part.
So in the UI class, I have this
private void btnAddItems_Click(object sender, RoutedEventArgs e)
{
item_name = lbl_item_name.Text;
item_quantity = lbl_item_quantity.Text;
store_ime = store_Name.Text;
logika.storeInDb(store_ime, item_name, item_quantity);
}
It just stores the input in variables and then sends them to this
public void storeInDb(string store_name, string item_name, string item_quantity)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
dbInsert($"INSERT INTO soping(store_name, item_name, item_quantity, payment_type, date) VALUES('{store_name}', '{item_name}', '{item_quantity}', 'visa', 'danas')");
}
}
And this is the dbInsert method
public void dbInsert(string query)
{
using (MySqlConnection mySqlConn = new MySqlConnection(Logic.connStr))
{
try
{
mySqlConn.Open();
MySqlCommand cmd = new MySqlCommand(query, mySqlConn);
cmd.ExecuteNonQuery();
mySqlConn.Close();
}
catch (MySqlException e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
}
It doesn't store anything. And when I use breakpoints, it seems like the button method runs after storeInDb, even though the variables in the query are perfectly fine. And I can't find anything wrong with the code that would make it behave weird like this.
This code have some issues:
1- You should use parameters instead of direct strings in your sql query;
2- You don't need a connection outside your dbInsert Method
However, this code should work. I guess the problem you are having is located elsewhere, not in the code you posted here. Something simpler, maybe connectionstring problem (saving in other place where you don't expect to) or bad uses of threads...Maybe hitting deadlocks, long processing or something like that (the only way i can think of having button click apparently happenning after the code it calls).

n-tier c# applicaiton with BAL and DAL methods with exact same names ( signatures etc.. )

I'm on a project in which I'm failing to see the point of how a previous developer made decisions.
same exact method names in DAL and BAL
static is EVERYWHERE
what should i do with New methods to follow best practices?
example of existing code:
Calling appliction (could be console app or web app etc.. agnostic )
DataSet DS = CreditMgr.GetCreditRqstInfo(ddlGEO.Text);
BAL
public class CreditMgr
{
public static DataSet GetCreditRqstInfo(String GeoID)
{
try
{
DataSet DS = new DataSet();
DS = CreditIntfDB.GetCreditRqstInfo(GeoID);
return DS;
}
catch (Exception ex)
{
throw ex;
}
}
}
DAL
public class CreditIntfDB
{
public static DataSet GetCreditRqstInfo(String GeoID)
{
try
{
Database DB = new SqlDatabase(Common.ConnectionString);
String SQLCommand = Common.SPGetRqstInfo;
DbCommand DBCommand = DB.GetStoredProcCommand(SQLCommand);
DBCommand.CommandTimeout = Common.CommandTimeOut;
DB.AddInParameter(DBCommand, "#a_geo_id", DbType.String, GeoID);
DataSet DS = new DataSet();
DB.LoadDataSet(DBCommand, DS, new String[] { "CreditRqstInfo" });
return DS;
}
catch (Exception ex)
{
throw ex;
}
}
}
Yes, the whole point is to have layers of separation, but when the same method names are being used , and static, and each are simply doing the same exact thing with passing in string and returning a DataSet has "code smell" to me
Suggestions on better ways?
According to standard Object-Oriented Programming (OOP) design, your BAL classes should represent "things" that have some real world business meaning. Instead of having a CreditMgr that has a static method to get a CreditRqst, create a class CreditRequest that stores its own data (e.g. the DataSet), and preferably wraps it in some business-friendly manner (e.g. List of CreditLine or List of Account).
From there, you can either implement the Get method inside of CreditRequest, or you can turn CreditMgr into a service object (such as "CreditBureau", "Bank", "AccountsDesk", etc.), that has a method that takes a String GeoID and returns a CreditRequest.
In addition, using strings as keys (e.g. in GeoID) is smelly as well. Can you come up with something a little more strongly typed? You could create a class GeoID that enforces requirements (such as maximum length, allowable characters, checksum requirements, etc.)

Adding toolbox items as parameters

Hi I'm writing a program that IO from databses and I have the following function to read from the database and adds the rows to a combobox:
private void loadFromTuzel()
{
string constring = "Server=localhost;Database=ozturk;Uid=____;pwd=_____";
MySqlConnection newCon = new MySqlConnection(constring);
string selectCommand = "SELECT * FROM ozturk.tuzelkisi";
MySqlCommand cmd = new MySqlCommand(selectCommand, newCon);
MySqlDataReader myReader;
newCon.Open();
myReader = cmd.ExecuteReader();
while (myReader.Read())
{
cbselected.Items.Add(myReader["name"].ToString() + " " + myReader["Surname"].ToString());
}
}
as can be seen from the code the program loads the data from database to the combobox...
I need to use this function in a different form but need to load the data to a different combo box and I'm wondering if adding a toolbox item as a parameter to my function is possible so that it would be something like this
private void myfunction(thecombobox parameter comes here)
{
// The execution code and than
// thecomboboxparameter.items.add......
}
so I can use this function over and over at different forms just by adding the parameter value, is something like this possible?
Thanks
Yes, it is possible, but you'd be much better suited by moving that logic outside of your UI code altogether, and just return the items from the database. Don't pass the ComboBox to a parameter, but get the results from that method, and in your UI then tie everything together.
This is the basis of Separation of Concerns. Your ComboBoxes shouldn't care where the data comes from. They only care that they have data to display.
For the simple answer to your question, just adjust your method like this:
public static class Utilities
{
public static void loadFromTuzel(ComboBox cbo)
{
/// All of you other logic
while (myReader.Read())
{
cbo.Items.Add(myReader["name"].ToString() + " " +
myReader["Surname"].ToString());
}
}
}
If you follow SoC, then you'd have something like this:
public class Repository {
public IEnumerable<string> GetNamesFromTuzel()
{
// All of the same logic
while (myReader.Read())
{
yield return myReader["name"].ToString() + " " +
myReader["Surname"].ToString();
}
}
}

Visual C# Dataset to Class

I am a student programer working in Visual Studios C# and I am trying to access information from my dataset and insert the data into a class. This is not, by any means, homework I've just have some personal projects I've been wanting to try. I have tried several approaches that have been mentioned on this site; however, no information is displayed. My code looks similar to this:
class MyClass
{
public string ColumnData1
{
get; set;
}
public int ColumnData2
{
get; set;
}
public string Display()
{
string MyString = ColumnData1 + ColumnData2.ToString();
return MyString;
}
}
I use this to insert data into class:
private void MyForm_Load(object sender, EventArgs e)
{
MyDataSet.MyDataTable MDT = new MyDataSet.MyDataTable();
List<MyClass> MyList = new List<MyClass>();
foreach (DataRow MyDataRow in MDT.Rows)
{
Mylist.Add(new MyClass()
{
ColumnData1 = (string)MyDataRow["Data1"],
ColumnData2 = (int)MyDataRow["Data2"]
{
}
Lastly to display the information:
textBox1.Text = Mylist[0].Display();
}
In the end, however, nothing ends up displaying. This also wasn't the only thing I've tried to display the information.. it's like the information doesn't exist. I don't receive any errors and when I try to add a "Stop Point" at the insertion part of the code it just skips it. I should mention also that I have many text boxes and list boxes that pull data off the database just fine, of course Visual Studios binds those for me. Any help is very much appreciated.
Edit:
Ok, excluding the new data table. If I had an existing dataset how would I would I use it to fill my class.
That's normal because your DataTable is empty (Your create instance)
MyDataSet.MyDataTable MDT = new MyDataSet.MyDataTable();//<------Empty
foreach (DataRow MyDataRow in MDT.Rows)
{
....
}
Fill DataSet :
string queryString =
"SELECT .... FROM YourTable";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "NameOfDataSet");
You must have some data in MDT before you insert it in the MyList, List of MyClass.
Declaration without data:
MyDataSet.MyDataTable MDT = new MyDataSet.MyDataTable();

Storing reader information in C#

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

Categories

Resources